Scope Kills 0

Posted by Matt Williams

First off, I'm not talking about bad breath. Rather, I'm talking about scope in ruby.

The previous entry, attributes with default values (on steroids), had an issue with it, due to the way in which ruby defines scope (as of 1.8, scope is supposed to change in 1.9). Here's the "broken" code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class Object
  def self.attribute(*arg,&block)
    (name,value) = arg
    self.send(:define_method, name) {
      if instance_variables.include? "@#{name}"
           self.instance_eval "@#{name}"
      else
        if block_given?
          instance_eval &block
        else
          value
        end
      end
    }
    self.send(:define_method, "#{name}="){ |value|
      self.instance_eval "@#{name} = value"
    }
  end
end

The problem is with the value variable. Because it is set within the Object#attribute method, the reference to value is the same throughout the entire method. So, when we change value, by setting it in the "setter" method, we are changing it for every instance of the variable. In addition, since we're setting it at the class level, any object which belongs to the class for which we are setting it has the same value. The solution is pretty simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

def self.attribute(*arg,&block)
    (name, default) = arg
    self.send(:define_method, name) {
      if instance_variables.include? "@#{name}"
           self.instance_eval "@#{name}"
      else
        if block_given?
          instance_eval &block
        else
          default
        end
      end
    }
    self.send(:define_method, "#{name}="){ |value|
      self.instance_eval "@#{name} = value"
    }
  end
end

Notice that I'm using default now within the accessor method. This removes the issue with value, and it now works properly.

Comments

Leave a response

Comment