Just about every example we've given so far in this book has featured
assignment. Perhaps it's about time we said something about it.
An assignment statement sets the variable or attribute on its left
side (the
lvalue) to refer to the value on the right (the
rvalue).
It then returns that value as the result of the
assignment expression. This means that you can chain assignments and
that you can
perform assignments in some unexpected places.
a = b = 1 + 2 + 3
|
a
|
� |
6
|
b
|
� |
6
|
a = (b = 1 + 2) + 3
|
a
|
� |
6
|
b
|
� |
3
|
File.open(name = gets.chomp)
|
There are two basic forms of assignment in Ruby. The first assigns an
object reference to a variable or constant. This form of assignment
is hard-wired into the language.
instrument = "piano"
MIDDLE_A = 440
|
The second form of assignment involves having an object attribute or
element reference on the left-hand side.
aSong.duration = 234
instrument["ano"] = "ccolo"
|
These forms are special, because they are implemented by calling
methods in the lvalues, which means you can override them.
We've already seen how to define a writable object attribute. Simply
define a method name ending in an equals sign. This method receives as
its parameter the assignment's rvalue.
class Song
def duration=(newDuration)
@duration = newDuration
end
end
|
There is no reason that these attribute setting methods must
correspond with internal instance variables, or that there has to be
an attribute reader for every attribute writer (or vice versa).
class Amplifier
def volume=(newVolume)
self.leftChannel = self.rightChannel = newVolume
end
# ...
end
|
Sidebar: Using Accessors Within a Class
|
Why did we write self.leftChannel in the example on page
74? Well, there's a hidden gotcha with writable
attributes. Normally, methods within a class can invoke other
methods in the same class and its superclasses in functional form
(that is, with an implicit receiver of self ). However, this
doesn't work with attribute writers. Ruby sees the assignment and
decides that the name on the left must be a local variable, not a
method call to an attribute writer.
class BrokenAmplifier
|
attr_accessor :leftChannel, :rightChannel
|
def volume=(vol)
|
leftChannel = self.rightChannel = vol
|
end
|
end
|
|
ba = BrokenAmplifier.new
|
ba.leftChannel = ba.rightChannel = 99
|
ba.volume = 5
|
ba.leftChannel
|
� |
99
|
ba.rightChannel
|
� |
5
|
We forgot to put ``self. '' in front of the assignment to
leftChannel , so Ruby stored the new value in a local variable of
method volume= ; the object's attribute never got updated.
This can be a tricky bug to track down.
|