Jakub Zak Jakub Zak - 7 months ago 13
Ruby Question

Ruby dynamically create methods and variables

How can I replace add_entry method with something more sensible?

class MyStorageClass

def add_entry key, value
eval "(@#{key} ||= []) << value; def #{key}; @#{key}; end"
end

end


So then I can retrieve value as follows:

def get_entry key
begin
self.send key.to_sym
rescue NoMethodError
nil
end
end

Answer

[EDIT - I should have used define_singleton_method, not define_method]

Rather than an instance variable per key, which requires some bulky code IMO, why not just a single Hash like below. Also, define_method and define_singleton_method can be your friend to avoid bad bad evals.

class MyStorageClass
  def initialize
    @data = {}
  end

  def add_entry(key, value)
    (@data[key] ||= []) << value
    define_singleton_method(key){ @data[key] }
  end

  def get_entry(key)
    @data.key?(key) or raise NoMethodError
    @data[key]
  end
end

You may want to check that you're not overriding a predefined method first (!@data.key?(key) && self.respond_to?(key) at the top of the add_entry method would do), but that's for another conversation. Could be bad if someone tried to add a key called inspect, class, or, oh, get_entry, for example!