Title: ActiveRecord::Base.find_or_create and find_or_initialize
Date: April 11, 2007
Timestamp: 1176287040
Author: sjs
Tags: activerecord, coding, rails, ruby
----

<p>I’ve extended ActiveRecord with <tt>find_or_create(params)</tt> and <tt>find_or_initialize(params)</tt>. Those are actually just wrappers around <tt>find_or_do(action, params)</tt> which does the heavy lifting.</p>


	<p>They work exactly as you’d expect them to work with possibly one gotcha. If you pass in an <tt>id</tt> attribute then it will just find that record directly. If it fails it will try and find the record using the other params as it would have done normally.</p>


	<p>Enough chat, here’s the self-explanatory code:</p>


<table class="CodeRay"><tr>
  <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt></pre></td>
  <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># extend ActiveRecord::Base with find_or_create and find_or_initialize.</span><tt>
</tt><span class="co">ActiveRecord</span>::<span class="co">Base</span>.class_eval <span class="r">do</span><tt>
</tt>  include <span class="co">ActiveRecordExtensions</span><tt>
</tt><span class="r">end</span></pre></td>
</tr></table>


<table class="CodeRay"><tr>
  <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt>5<tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt>15<tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt>24<tt>
</tt>25<tt>
</tt>26<tt>
</tt>27<tt>
</tt>28<tt>
</tt>29<tt>
</tt><strong>30</strong><tt>
</tt>31<tt>
</tt>32<tt>
</tt>33<tt>
</tt>34<tt>
</tt>35<tt>
</tt>36<tt>
</tt>37<tt>
</tt>38<tt>
</tt>39<tt>
</tt><strong>40</strong><tt>
</tt>41<tt>
</tt></pre></td>
  <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="r">module</span> <span class="cl">ActiveRecordExtensions</span><tt>
</tt>  <span class="r">def</span> <span class="pc">self</span>.included(base)<tt>
</tt>    base.extend(<span class="co">ClassMethods</span>)<tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  <span class="r">module</span> <span class="cl">ClassMethods</span><tt>
</tt>    <span class="r">def</span> <span class="fu">find_or_initialize</span>(params)<tt>
</tt>      find_or_do(<span class="s"><span class="dl">'</span><span class="k">initialize</span><span class="dl">'</span></span>, params)<tt>
</tt>    <span class="r">end</span><tt>
</tt><tt>
</tt>    <span class="r">def</span> <span class="fu">find_or_create</span>(params)<tt>
</tt>      find_or_do(<span class="s"><span class="dl">'</span><span class="k">create</span><span class="dl">'</span></span>, params)<tt>
</tt>    <span class="r">end</span><tt>
</tt>    <tt>
</tt>    private<tt>
</tt>    <tt>
</tt>    <span class="c"># Find a record that matches the attributes given in the +params+ hash, or do +action+</span><tt>
</tt>    <span class="c"># to retrieve a new object with the given parameters and return that.</span><tt>
</tt>    <span class="r">def</span> <span class="fu">find_or_do</span>(action, params)<tt>
</tt>      <span class="c"># if an id is given just find the record directly</span><tt>
</tt>      <span class="pc">self</span>.find(params[<span class="sy">:id</span>])<tt>
</tt><tt>
</tt>    <span class="r">rescue</span> <span class="co">ActiveRecord</span>::<span class="co">RecordNotFound</span> =&gt; e<tt>
</tt>      attrs = {}     <span class="c"># hash of attributes passed in params</span><tt>
</tt><tt>
</tt>      <span class="c"># search for valid attributes in params</span><tt>
</tt>      <span class="pc">self</span>.column_names.map(&amp;<span class="sy">:to_sym</span>).each <span class="r">do</span> |attrib|<tt>
</tt>        <span class="c"># skip unknown columns, and the id field</span><tt>
</tt>        <span class="r">next</span> <span class="r">if</span> params[attrib].nil? || attrib == <span class="sy">:id</span><tt>
</tt><tt>
</tt>        attrs[attrib] = params[attrib]<tt>
</tt>      <span class="r">end</span><tt>
</tt><tt>
</tt>      <span class="c"># no valid params given, return nil</span><tt>
</tt>      <span class="r">return</span> <span class="pc">nil</span> <span class="r">if</span> attrs.empty?<tt>
</tt><tt>
</tt>      <span class="c"># call the appropriate ActiveRecord finder method</span><tt>
</tt>      <span class="pc">self</span>.send(<span class="s"><span class="dl">"</span><span class="k">find_or_</span><span class="il"><span class="dl">#{</span>action<span class="dl">}</span></span><span class="k">_by_</span><span class="il"><span class="dl">#{</span>attrs.keys.join(<span class="s"><span class="dl">'</span><span class="k">_and_</span><span class="dl">'</span></span>)<span class="dl">}</span></span><span class="dl">"</span></span>, *attrs.values)<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span><tt>
</tt><span class="r">end</span></pre></td>
</tr></table>
  
  <div class="extended">
    <p><a href="http://web.archive.org/web/20071124024600/http://sami.samhuri.net/2007/4/11/activerecord-base-find_or_create-and-find_or_initialize">Continue reading...</a></p>
  </div>
