开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜