Brian Genisio
Brian Genisio

Reputation: 48137

How to know when an instance variable gets set in Ruby

Is there any way to override the setting of instance variables in Ruby?

Lets say I set an instance variable:

@foo = "bar"

Can I intercept that and do something (like record it or puts, for instance)

I suppose, I am trying to override the assignment operator for all types. Can this even be done?

The best I have come up with, so far, is this:

class Module
  def attr_log_accessor( *symbols )
    symbols.each { | symbol |
      module_eval( "def #{symbol}() @#{symbol}; end" )
      module_eval( "def #{symbol}=(val) 
                      @#{symbol} = val
                      puts \"#{symbol} has changed\"
                    end" )
    }
  end
end

Then, when I define the accessor and set it, my code gets executed:

class Test
  attr_log_accessor :foo

  def DoSomething
    self.foo = "bar"
  end
end

Unfortunately, this requires me to write self.foo = "bar", instead of @foo = "bar".

Any thoughts?

Upvotes: 7

Views: 1046

Answers (3)

Jim Schubert
Jim Schubert

Reputation: 20357

You can do this using method_missing()

example:

def method_missing(name, *args)
  attribute = name.to_s
  if attribute =~ /=$/
    @attributes[attribute.chop] = args[0]
  else
    @attributes[attribute]
  end
end

If you have processing in your getter/setter, this method would quickly become bloated.

For more information, check out the OpenStruct source. Also, I pulled this code from Metaprogramming Ruby (pg 52)

Also, note that this will catch all missing methods. If you only want to intercept obj.[*]= then, you'll have to change @attributes[attribute] to super

Upvotes: 0

Robert Speicher
Robert Speicher

Reputation: 15562

You could mess around with instance_variable_get, instance_variable_set and instance_variables. More can be found in this Ruby's Metaprogramming Toolbox article.

If you could describe why you're trying to do this we might be able to offer a better method.

Upvotes: 1

ryeguy
ryeguy

Reputation: 66851

No you can't do this directly. The best approach to this would be to only access the instance variables through getter/setter methods and override those.

Upvotes: 4

Related Questions