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|
if key=conditions_hash.keys.find(/(.+)_less_than$/) and val.is_a?
(Numeric)
[ "#{$1} < ?", val ]
end
end
Zach
P.S. - please CC my email in response to this
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.
>
> If you want to skip to the paste/diff, it's here: http://pastie.caboo.se/89252
>
> The current way:
>
> def find(*args)
> options = args.extract_options!
> validate_find_options(options)
> set_readonly_option!(options)
>
> case args.first
> when :first then find_initial(options)
> when :all then find_every(options)
> else find_from_ids(args, options)
> end
> end
>
>
> A new way?
>
> def find(*args)
> options = args.extract_options!
> validate_find_options(options)
> set_readonly_option!(options)
>
> finder.dispatch(args, options)
> end
>
>
> # The Finder class allows you to easily define your own custom
> find implementations.
> # Simply define this class in your model, and any of its methods
> will be available
> # to your #find call as the first symbol. For example,
> #
> # User.find(:last, :conditions => ['name = ?', 'John'])
> #
> # is implemented as
> #
> # class User < ActiveRecord::Base
> # class Finder
> # def last(options)
> # @base.find(:first, options.update(:order => 'id desc'))
> # end
> # end
> # end
> #
> #
> # Note: if you want to refer to any methods on Base other than
> find, you'll need to
> # use the @base proxy object
> #
> # def last(options)
> # @base.find_by_sql(...)
> # end
> #
> class Finder
> def initialize(base_instance)
> @base = base_instance
> end
>
> def dispatch(args, options)
> finder_method = args.first
> if finder_method.is_a?(Symbol) and respond_to?(finder_method)
> send(finder_method, options)
> else
> from_ids(args, options)
> end
> end
>
> def find(*args)
> @base.find(*args)
> end
>
> def all(options)
> @base.send(:find_every, options)
> end
>
> def first(options)
> @base.send(:find_initial, options)
> end
>
> def from_ids(ids, options)
> @base.send(:find_from_ids, ids, options)
> end
>
> end
>
> def finder
> @finder ||= Finder.new(self)
> end
>
> ...
>
> This will also allow some fun things, like, keeping state in the
> finder, or even recording the last SQL statistics.
>
> @user = User.find(2)
> User::Finder.records_returned => 25
> 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.
>
>
> Courtenay
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby
on Rails: Core" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-core?hl=en
-~----------~----~----~----~------~----~------~--~---