views:

253

answers:

1

I'm defining my own AR class in Rails that will include dynamically created instance methods for user fields 0-9. The user fields are not stored in the db directly, they'll be serialized together since they'll be used infrequently. Is the following the best way to do this? Alternatives?

Where should the start up code for adding the methods be called from?

class Info < ActiveRecord::Base

end

# called from an init file to add the instance methods
parts = []
(0..9).each do |i|
   parts.push "def user_field_#{i}"     # def user_field_0
   parts.push   "get_user_fields && @user_fields[#{i}]"
   parts.push "end"
end

Info.class_eval parts.join
+3  A: 

One nice way, especially if you might have more than 0..9 user fields, would be to use method_missing:

class Info
  USER_FIELD_METHOD = /^user_field_(\n+)$/
  def method_missing(method, *arg)
    return super unless method =~ USER_FIELD_METHOD
    i = Regexp.last_match[1].to_i
    get_user_fields && @user_fields[i]
  end

  # Useful in 1.9.2, or with backports gem:
  def respond_to_missing?(method, private)  
    super || method =~ USER_FIELD_METHOD
  end
end        

If you prefer to define methods:

10.times do |i|
  Info.class_eval do
    define_method :"user_field_#{i}" do
      get_user_fields && @user_fields[i]
    end
  end
end
Marc-André Lafortune
Thank you. Why will the [i] be eval'd at method definition rather than method execute time in your second example?
Larry K
The `[i]` will work as desired because the method definition is a block, and that in Ruby, blocks are closures. HTH
Marc-André Lafortune