3

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

2 Answers 2

11

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
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. Why will the [i] be eval'd at method definition rather than method execute time in your second example?
The [i] will work as desired because the method definition is a block, and that in Ruby, blocks are closures. HTH
4

Using method_missing is very difficult to maintain and unnecessary. The other alternative using define_method is better but leads to poorly performing code. The following 1 liner is all you need:

class Info
end

Info.class_eval 10.times.inject("") {|s,i| s += <<END}
  def user_field_#{i}
    puts "in user_field_#{i}"
  end
END

puts Info.new.user_field_4

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.