Nassredean Nasseri
Nassredean Nasseri

Reputation: 23

Define memoized getter dynamically in ruby

Using Ruby, I want to dynamically create class-level instance variables inside corresponding getters. For two of them, I use attr_reader. But for those which need to be initialized to an empty array, I do the following:

class MatchMake
  class << self
    attr_reader :local_data, :remote_data
    ["type1", "type2"].each do |elem|
      define_method "#{elem}_matches".to_sym do
        instance_variable_set("@#{elem}_matches", [])
      end
    end
  end
  ...
end

from my understanding this code is equivalent to:

class MatchMake
  class << self
    def local_data
      @local_data
    end
    def remote_data
      @remote_data
    end
    def type1_matches
      @type1_matches = []
    end
    def type2_matches
      @type2_matches = []
    end
  end
  ...
end

Firstly I would like to know if I am correct in my understanding. Secondly, I would like to know if there is a way to memoize the variables, as in the following:

def type1_matches
  @type1_matches ||= []
end

Upvotes: 2

Views: 1161

Answers (2)

Ritikesh
Ritikesh

Reputation: 1228

Here is a sample pattern that I wrote for such use-cases: https://gist.github.com/ritikesh/09384fec25c4b05cfdec8674ce3a9076

here's the code from it:

# memoize db/cache results in instance variable dynamically
def memoize_results(key)
  return instance_variable_get(key) if instance_variable_defined?(key)
  instance_variable_set key, yield  
end

# usage
MY_CONSTANT = [:active, :inactive]
MY_CONSTANT.each { |key|
  define_method("#{key}_users") do
    memoize_results("@#{key}_users") do
      User.send(key).all # assumes that user responds to active, inactive(via scope/filter etc..)
    end
  end
}

Upvotes: 3

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

First of all, you define type1_matches, not type1. Secondly, define_method accepts strings, #to_sym is superfluous. The last, but not least, you define getter being in fact a setter. So, to define type1 as you wanted:

define_method "#{elem}=", value do
  instance_variable_set("@#{elem}", value)
end

Now, for getter, lazily instantiated to empty array:

define_method "#{elem}" do
  instance_variable_set("@#{elem}", []) \
      unless instance_variable_defined?("@#{elem}")
  instance_variable_get("@#{elem}")
end

Upvotes: 2

Related Questions