How to make a dynamically named method in Rails?
I have a method in my class that takes a parameter that's a number. It looks something like this:
def with_at_least_x_posts(min_posts)
self.where("posts_counter >开发者_开发问答= ?", min_posts)
end
I'd like to make a method that takes that parameter inside its name, rather than in parenthesis, so instead of calling
User.with_at_least_x_posts(10)
I can call
User.with_at_least_10_posts
This would require that method to be defined via some sort of regular expression mechanism. I know that the find_by
method works that way (i.e. find_by_some_column
), so it should be possible?
Could someone save me some time by telling me how to achieve that in Ruby 1.9.2 and Rails 3.1 without digging into the Rails core and finding out for myself?
Thanks!
Update: while waiting for an answer, I've been digging into the Rails core. Seems like they're overriding the method_missing
method in ActiveRecord::Base and delegating the handling of custom methods to a module called DynamicFinderMatch. Interesting stuff!
Would be REALLY nice to be able to define methods in Ruby like this:
def /with_at_least_[0-9]+_posts/
x = $0
end
but I guess that's not really possible at this point in time. Interesting stuff!
You should define a call within the general Ruby method method_missing
def with_at_least_x_posts(min_posts)
self.where("posts_counter >= ?", min_posts)
end
def method_missing(sym, *args, &block)
begin
if sym.to_s =~ /with_at_least_(\d+)_posts/
return send :with_at_least_x_posts, *(args.push($1.to_i)), &block
end
rescue NoMethodError => e
raise "Unable to find method: #{e.inspect}"
end
return super
end
From here, you'll be able to call myObj.with_at_least_10_posts
. Give it a go! You can read more here.
P.S. This is common in Ruby (it's what ActiveRecord does). Beware that if you get a StackOverflow error, it's because a method_missing
error raised in method_missing
will lead to an infinate recursion. Be cautious!
You could do this with method_missing
, but this is a dangerous method that can lead to very messy code. Instead, how about
User.with_more_posts_than(10)
which is much cleaner (and much easier to work with and maintain). It still reads correctly.
As one specific point among many objections, you shouldn't convert 10
to a string and back. This brings in a huge new class of potential errors which you will later have to deal with.
精彩评论