Search code examples
rubyoopproc-object

How do I using instance variables from within a lambda/Proc defined in a class variable?


I wrote the following code:

class Actions
  def initialize
    @people = []
    @commands = {
      "ADD" => ->(name){@people << name },
      "REMOVE" => ->(n=0){ puts "Goodbye" },
      "OTHER" => ->(n=0){puts "Do Nothing" }
    }
  end
  def run_command(cmd,*param)
    @commands[cmd].call param if @commands.key?(cmd)
  end
  def people
    @people
  end
end
act = Actions.new

act.run_command('ADD','joe')
act.run_command('ADD','jack')
puts act.people

This works, however, when the @commands hash is a class variable, the code inside the hash doesn't know the @people array.

How can I make the @commands hash be a class variable and still be able to access the specific object instance variables?


Solution

  • You could use instance_exec to supply the appropriate context for the lambdas when you call them, look for the comments to see the changes:

    class Actions
      # Move the lambdas to a class variable, a COMMANDS constant
      # would work just as well and might be more appropriate.
      @@commands = {
        "ADD"    => ->(name)  { @people << name   },
        "REMOVE" => ->(n = 0) { puts "Goodbye"    },
        "OTHER"  => ->(n = 0) { puts "Do Nothing" }
      }
      def initialize
        @people = [ ]
      end
      def run_command(cmd, *param)
        # Use instance_exec and blockify the lambdas with '&'
        # to call them in the context of 'self'. Change the
        # @@commands to COMMANDS if you prefer to use a constant
        # for this stuff.
        instance_exec(param, &@@commands[cmd]) if @@commands.key?(cmd)
      end
      def people
        @people
      end
    end