# Refactoring AR::Base.find

**URL:** <https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195>\
**Category:** rubyonrails-core\
**Created:** [August 20, 2007, 8:50am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195 "2007-08-20T08:50:57Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Courtenay](https://avatars.discourse-cdn.com/v4/letter/c/59ef9b/32.png) [@Courtenay](https://discuss.rubyonrails.org/u/Courtenay)\
**Post date:** [August 20, 2007, 8:50am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/1 "2007-08-20T08:50:57Z")

</div>

Hi guys, what do you think about this implementation (before I go off and actually make it work). The plan is to make it easier to write extensions or plugins for find, as well as define your own options for the precious first-level argument, :first, :all, etc.

If you want to skip to the paste/diff, it's here: [Parked at Loopia](http://pastie.caboo.se/89252)

The current way:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def find(\*args) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options = args.extract\_options! &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validate\_find\_options(options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set\_readonly\_option!(options)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case args.first &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when :first then find\_initial(options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when :all then find\_every(options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else find\_from\_ids(args, options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

A new way?

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def find(\*args) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;options = args.extract\_options! &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;validate\_find\_options(options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set\_readonly\_option!(options)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;finder.dispatch(args, options) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# The Finder class allows you to easily define your own custom find implementations. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Simply define this class in your model, and any of its methods will be available &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# to your #find call as the first symbol. For example,

---

<div class="post-metadata">

**Author:** ![Michael\_Koziarski1](https://avatars.discourse-cdn.com/v4/letter/m/aca169/32.png) [@Michael\_Koziarski1](https://discuss.rubyonrails.org/u/Michael_Koziarski1)\
**Post date:** [August 20, 2007, 9:52am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/2 "2007-08-20T09:52:14Z")

</div>

> Hi guys, what do you think about this implementation (before I go off and actually make it work). The plan is to make it easier to write extensions or plugins for find, as well as define your own options for the precious first-level argument, :first, :all, etc.

There's definitely some scope for tidying up some of those .... interesting bits of Base which generate the queries.

> This will also allow some fun things, like, keeping state in the finder, or even recording the last SQL statistics.
> 
> &nbsp;&nbsp;@user = User.find(2) &nbsp;&nbsp;User::Finder.records\_returned =\> 25 &nbsp;&nbsp;User::Finder.benchmark =\> 0.025
> 
> What do you guys think? All the AR tests still pass with this method, since it's a fairly simple proxy, but it allows for much greater customization.

What kind of customisation did you have in mind?

I've been kicking around a few more changes to AR::Base lately and perhaps we could do something along these lines. My thoughts on our querying at present are:

\* It should be in / near the adapters.

There's already some code that needs to be different but is currently implemented in base, by checking what the adapters support:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Should primary key values be selected from their corresponding &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# sequence before the insert statement? If true, next\_sequence\_value &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# is called before each insert to set the record's primary key. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# This is false for all adapters but Firebird. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def prefetch\_primary\_key?(table\_name = nil) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;false &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

That could ideally simply be handled with polymorphism

\* It should be easier to inject conditions into the generated queries.

Basically the only reason that people are currently using with\_scope is to merge :conditions arguments. It should be easier to define scopes, inject conditions into queries etc. This probably means some kind of query class.

Combining these two would also aid databases like oracle which get serious performance gains from the use of prepared statements.

---

<div class="post-metadata">

**Author:** ![Pratik1](https://avatars.discourse-cdn.com/v4/letter/p/6bbea6/32.png) [@Pratik1](https://discuss.rubyonrails.org/u/Pratik1)\
**Post date:** [August 20, 2007, 10:27am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/3 "2007-08-20T10:27:14Z")

</div>

> I've been kicking around a few more changes to AR::Base lately and perhaps we could do something along these lines. My thoughts on our querying at present are:
> 
> \* It should be in / near the adapters.

Exactly. So, every query from AR should look like @adapter.execute() - which would make it very simple to extend/optimize.

> There's already some code that needs to be different but is currently implemented in base, by checking what the adapters support:
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Should primary key values be selected from their corresponding &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# sequence before the insert statement? If true, next\_sequence\_value &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# is called before each insert to set the record's primary key. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# This is false for all adapters but Firebird. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def prefetch\_primary\_key?(table\_name = nil) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;false &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

This reminds me to ask, are we gonna get rid of most of the adapters from core for 2.0 ? I guess, only mysql and postgres and may be sqlite deserves to be in core. Other adapter might be better off as external libs.

> That could ideally simply be handled with polymorphism
> 
> \* It should be easier to inject conditions into the generated queries.
> 
> Basically the only reason that people are currently using with\_scope is to merge :conditions arguments. It should be easier to define scopes, inject conditions into queries etc. This probably means some kind of query class.

I kinda like the approach taken by scope-out plugin - [Google Code Archive - Long-term storage for Google Code Project Hosting.](http://code.google.com/p/scope-out-rails/) Instead of query class, I guess we need a Condition class first. What Courtnay suggested, looks sweet too.

Just my few cents.

---

<div class="post-metadata">

**Author:** ![Piyush\_Ranjan](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/piyush_ranjan/32/13578_2.png) [@Piyush\_Ranjan](https://discuss.rubyonrails.org/u/Piyush_Ranjan)\
**Post date:** [August 20, 2007, 11:59am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/4 "2007-08-20T11:59:47Z")

</div>

> This reminds me to ask, are we gonna get rid of most of the adapters from core for 2.0 ? I guess, only mysql and postgres and may be sqlite deserves to be in core. Other adapter might be better off as external libs.

Shouldn’t we also have Oracle adapter in the core?

With an ever increasing popularity of Rails a lot of big companies are now using it for their production quality apps and most of these run on oracle.

I do agree that it won’t much of a difference whether Rails has oracle libs in built or are provided as external libs.

However it would be a little easy to convince the CTOs to tip the favour towards Rails if it oracle support is provided out of the box.

---

<div class="post-metadata">

**Author:** ![Tarmo\_Tanav](https://avatars.discourse-cdn.com/v4/letter/t/3bc359/32.png) [@Tarmo\_Tanav](https://discuss.rubyonrails.org/u/Tarmo_Tanav)\
**Post date:** [August 20, 2007, 1:21pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/5 "2007-08-20T13:21:46Z")

</div>

This ticket does remove "prefetch\_primary\_key?" and: [http://dev.rubyonrails.org/ticket/9253](http://dev.rubyonrails.org/ticket/9253)

It's just waiting for works-with-Oracle confirmation from Michael Schoen.

---

<div class="post-metadata">

**Author:** ![Hugh\_Sasse](https://avatars.discourse-cdn.com/v4/letter/h/d6d6ee/32.png) [@Hugh\_Sasse](https://discuss.rubyonrails.org/u/Hugh_Sasse)\
**Post date:** [August 20, 2007, 1:34pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/6 "2007-08-20T13:34:51Z")

</div>

> \> \> \> This reminds me to ask, are we gonna get rid of most of the adapters \> from core for 2.0 ? I guess, only mysql and postgres and may be sqlite \> deserves to be in core. Other adapter might be better off as external \> libs.
> 
> Shouldn't we also have Oracle adapter in the core?

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[...]

> However it would be a little easy to convince the CTOs to tip the favour towards Rails if it oracle support is provided out of the box.

If stuff is being taken out to make this light, then I'd suggest that a "batteries included" distribution be made available, so you can say to 95% of people, "If you get the batteries include distro you will (most likely) be covered".

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hugh

---

<div class="post-metadata">

**Author:** ![Luke\_Redpath1](https://avatars.discourse-cdn.com/v4/letter/l/e19b73/32.png) [@Luke\_Redpath1](https://discuss.rubyonrails.org/u/Luke_Redpath1)\
**Post date:** [August 20, 2007, 1:51pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/7 "2007-08-20T13:51:47Z")

</div>

This looks like a definite improvement. Cleaner separation of concerns, easier to extend and probably far easier to test. +1

Courtenay wrote:

---

<div class="post-metadata">

**Author:** ![James\_Adam](https://avatars.discourse-cdn.com/v4/letter/j/3be4f8/32.png) [@James\_Adam](https://discuss.rubyonrails.org/u/James_Adam)\
**Post date:** [August 20, 2007, 2:14pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/8 "2007-08-20T14:14:02Z")

</div>

Beyond sharing the option-santizing methods, why is find(:first, ...), find(:all, ...) preferable to find\_first and find\_all? As this patch highlights, utilising the first argument like this makes it more difficult to add new behaviour, but adding another dispatch layer to lengthen the stack trace might be tackling the wrong problem to reach the goal of making AR's internals easier to enhance.

The patched code is definitely nice, and definitely makes it simpler to add new behaviour to the find() method. However, it also seems like a lot of hoop-jumping to preserve a customised method-dispatch mechanism, when it might be more sensible in the long term to figure out a better way for concrete methods (find\_first, find\_all, find\_with\_some\_other\_behaviour) to work with the option wrangling code.

---

<div class="post-metadata">

**Author:** ![Dr\_Ernie\_Prabhakar1](https://avatars.discourse-cdn.com/v4/letter/d/8c91f0/32.png) [@Dr\_Ernie\_Prabhakar1](https://discuss.rubyonrails.org/u/Dr_Ernie_Prabhakar1)\
**Post date:** [August 20, 2007, 2:45pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/9 "2007-08-20T14:45:40Z")

</div>

+1

This is something I've been wanting for a long time...

-- Ernie P.

---

<div class="post-metadata">

**Author:** ![Julian\_Julik\_Tarkhan](https://avatars.discourse-cdn.com/v4/letter/j/5e9695/32.png) [@Julian\_Julik\_Tarkhan](https://discuss.rubyonrails.org/u/Julian_Julik_Tarkhan)\
**Post date:** [August 20, 2007, 3:42pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/10 "2007-08-20T15:42:24Z")

</div>

And it should be much easier to autocompose :select lists so that lazy-loaded attributes could be implemented (perhaps by piggybacking the blobs onto the :include option)...

---

<div class="post-metadata">

**Author:** ![Courtenay](https://avatars.discourse-cdn.com/v4/letter/c/59ef9b/32.png) [@Courtenay](https://discuss.rubyonrails.org/u/Courtenay)\
**Post date:** [August 20, 2007, 8:31pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/11 "2007-08-20T20:31:07Z")

</div>

> \> \> This looks like a definite improvement. Cleaner separation of \> concerns, easier to extend and probably far easier to test. +1 \> \> Courtenay wrote: \> \> Hi guys, what do you think about this implementation (before I go off \> \> and actually make it work). The plan is to make it easier to write \> \> extensions or plugins for find, as well as define your own options for \> \> the precious first-level argument, :first, :all, etc. \>
> 
> Beyond sharing the option-santizing methods, why is find(:first, ...), find(:all, ...) preferable to find\_first and find\_all? As this patch highlights, utilising the first argument like this makes it more difficult to add new behaviour, but adding another dispatch layer to lengthen the stack trace might be tackling the wrong problem to reach the goal of making AR's internals easier to enhance.

It looks cooler, and hides all the find stuff behind a nice interface.

> The patched code is definitely nice, and definitely makes it simpler to add new behaviour to the find() method. However, it also seems like a lot of hoop-jumping to preserve a customised method-dispatch mechanism, when it might be more sensible in the long term to figure out a better way for concrete methods (find\_first, find\_all, find\_with\_some\_other\_behaviour) to work with the option wrangling code.

Zing! You're right, of course. But my patch was specifically written to be a simple drop-in replacement for the existing code, rather than a huge freaking rewrite.

I actually tried rewriting it, but there are just too many dependencies with the rest of the library, includling all that alias\_method\_chain stuff.

---

<div class="post-metadata">

**Author:** ![James\_Adam](https://avatars.discourse-cdn.com/v4/letter/j/3be4f8/32.png) [@James\_Adam](https://discuss.rubyonrails.org/u/James_Adam)\
**Post date:** [August 20, 2007, 9:41pm UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/12 "2007-08-20T21:41:55Z")

</div>

> Zing! You're right, of course. But my patch was specifically written to be a simple drop-in replacement for the existing code, rather than a huge freaking rewrite.

Yup, and I think that's totally fine; I definitely strayed from the central topic of the thread. Considering the benefits gained vs. the amount of code changed, I think your patch has definite value and I hope it does get committed.

That said, and continuing my aside -

> I actually tried rewriting it, but there are just too many dependencies with the rest of the library, includling all that alias\_method\_chain stuff.

If it's felt that ActiveRecord\* would benefit from some more extensive work, what's the best way to approach this? My feeling is that large patches are very unlikely to see any commit action, which is fair because they're difficult to grasp.

The typical approach within a development team might be to create a branch, but I'm not sure that works within the Rails development process either. Any thoughts?

James

\* or any other Rails framework for that matter, but most people seem to express this more-radical-refactoring urge with ActiveRecord

---

<div class="post-metadata">

**Author:** ![Michael\_Koziarski1](https://avatars.discourse-cdn.com/v4/letter/m/aca169/32.png) [@Michael\_Koziarski1](https://discuss.rubyonrails.org/u/Michael_Koziarski1)\
**Post date:** [August 21, 2007, 9:23am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/13 "2007-08-21T09:23:57Z")

</div>

> The typical approach within a development team might be to create a branch, but I'm not sure that works within the Rails development process either. Any thoughts?

There's two questions here, technical and process. The process side of it is harder, if you're interested in undertaking some major refactoring you should probably grab someone with commit access in irc, and talk over the changes first. 'Refactoring' is a tough sell without some kind of performance or functional improvement, especially given its tendencies to break the hell out of plugins 😉

As for the technical question... My personal 'major changes' tend to happen in my git repository, then I commit them to svn when ready to go. I'm happy to take patches or proposed patches via git instead of svn diffs, but I can't promise anyone else will agree 😉

git clone git://git.koziarski.com/rails [http://git.koziarski.com/?p=.git;a=summary](http://git.koziarski.com/?p=.git;a=summary)

---

<div class="post-metadata">

**Author:** ![zdennis](https://avatars.discourse-cdn.com/v4/letter/z/d78d45/32.png) [@zdennis](https://discuss.rubyonrails.org/u/zdennis)\
**Post date:** [August 23, 2007, 3:05am UTC](https://discuss.rubyonrails.org/t/refactoring-ar-base-find/17195/14 "2007-08-23T03:05:22Z")

</div>

One important feature of an ActiveRecord find refactoring should be the ability to extend functionality without having to modify a core class, although this perhaps does not come first.

After releasing ActiveRecord::Extensions 0.5.0 I started refactoring AR and found a lot of nastiness. I like the dispatch pattern, and there should also be a way to subscribe to the Finder to give it more things to dispatch to.

Perhaps a callback for each key/value pair in the conditions hash. The below would give you the ability to handle things suffixed with "\_in", ie: age\_less\_than =\> 50

IE:

ActiveRecord::Base.finder.new\_find\_condition do |key,val| &nbsp;&nbsp;&nbsp;if key=conditions\_hash.keys.find(/(.+)\_less\_than$/) and val.is\_a? (Numeric) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;["#{$1} \< ?", val] &nbsp;&nbsp;&nbsp;end end

Zach P.S. - please CC my email in response to this

Courtenay wrote:
