7

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?

2
  • there's trace_var (ruby-doc.org/core/classes/Kernel.html#M005937), but for global vars only. Commented Apr 18, 2010 at 19:38
  • There is also the Observable module, which may be as close to this as possible: oreillynet.com/ruby/blog/2006/01/… However, that may require using getters/setters instead of directly setting the instance variable (which is basically how you're doing it now) Commented Apr 21, 2010 at 1:58

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

I am trying to do this, because for specific properties on the class, I want to notify other objects when the properties change. More specifically, this is for integrating with .Net. I am using IronRuby and firing events on the INotifyPropertyChanged.PropertyChanged event. I'd like to declare a property as notifiable: attr_notifiable :foo, or something and whenever @foo is set, it fires the event. Ultimately, self.foo will work, but if I can get in at the ground floor (assignment to local var), then it will be even more useful to me.
0

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

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.