automatically call function on attribute set

J

Joe Laughlin

I have a class that represents an airplane. This class has a bunch of data
members: x/y/z positions, x/y/z velocities, pitches, rolls, etc. What I did
is let the user configure what different attributes the airplane had in a
YAML config file, then have the airplane class read in that file and setup
attr_accessors for each attribute in the file.

I have another object (call it airplane_drawing) that is responsible for
drawing this airplane on the screen. It needs to have access to some of the
attributes of the airplane, and it needs have some of its own attributes
changed whenever airplane's attributes changes.

What is the best way to get airplane_drawing to be notified whenever some
attribute of airplane is changed? Should I have a thread (or similar) that
checks the airplane object to see if anything's changed?

I hope I've explained this well enough for people to understand... I feel
like this is a fairly common thing to do, but I'm not sure of the best way
to do it.

Thanks,
Joe
 
A

Ara.T.Howard

I have a class that represents an airplane. This class has a bunch of data
members: x/y/z positions, x/y/z velocities, pitches, rolls, etc. What I did
is let the user configure what different attributes the airplane had in a
YAML config file, then have the airplane class read in that file and setup
attr_accessors for each attribute in the file.

I have another object (call it airplane_drawing) that is responsible for
drawing this airplane on the screen. It needs to have access to some of the
attributes of the airplane, and it needs have some of its own attributes
changed whenever airplane's attributes changes.

What is the best way to get airplane_drawing to be notified whenever some
attribute of airplane is changed? Should I have a thread (or similar) that
checks the airplane object to see if anything's changed?

I hope I've explained this well enough for people to understand... I feel
like this is a fairly common thing to do, but I'm not sure of the best way
to do it.

Thanks,
Joe

check out

observer.rb in ruby's lib dir.

it has an example in it.

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| When you do something, you should burn yourself completely, like a good
| bonfire, leaving no trace of yourself. --Shunryu Suzuki
===============================================================================
 
M

Michael Neumann

I have a class that represents an airplane. This class has a bunch of data
members: x/y/z positions, x/y/z velocities, pitches, rolls, etc. What I did
is let the user configure what different attributes the airplane had in a
YAML config file, then have the airplane class read in that file and setup
attr_accessors for each attribute in the file.

I have another object (call it airplane_drawing) that is responsible for
drawing this airplane on the screen. It needs to have access to some of the
attributes of the airplane, and it needs have some of its own attributes
changed whenever airplane's attributes changes.

What is the best way to get airplane_drawing to be notified whenever some
attribute of airplane is changed? Should I have a thread (or similar) that
checks the airplane object to see if anything's changed?

You should use an Observer.

require 'observer'

class Airplane
include Observable

def initialize
@x = 0
end

def fly
# airplane changes it's coordinate
@x += 10

changed
notify_observers(self)
end
end

class AirplaneDrawing
def update(obj)
# ...
end
end

airbus = Airplane.new
drawing = AirplaneDrawing.new
airbus.add_observer(drawing)
airbus.fly

Regards,

Michael
 
J

Joel VanderWerf

Joe said:
I have a class that represents an airplane. This class has a bunch of data
members: x/y/z positions, x/y/z velocities, pitches, rolls, etc. What I did
is let the user configure what different attributes the airplane had in a
YAML config file, then have the airplane class read in that file and setup
attr_accessors for each attribute in the file.

I have another object (call it airplane_drawing) that is responsible for
drawing this airplane on the screen. It needs to have access to some of the
attributes of the airplane, and it needs have some of its own attributes
changed whenever airplane's attributes changes.

What is the best way to get airplane_drawing to be notified whenever some
attribute of airplane is changed? Should I have a thread (or similar) that
checks the airplane object to see if anything's changed?

I hope I've explained this well enough for people to understand... I feel
like this is a fairly common thing to do, but I'm not sure of the best way
to do it.

Another approach is the observable lib on RAA
(http://raa.ruby-lang.org/list.rhtml?name=observable).


require 'observable'

class Airplane
extend Observable

observable :x, :y, :z

def initialize(x = 0, y = 0, z = 0)
@x, @y, @z = x, y, z
end

def fly
self.x += 1 # don't use @x=, or else no notification
end
end

class Drawing
def initialize(airplane)
airplane.when_x do
puts "moving in x dimension to #{airplane.x}"
end
end
end

airplane = Airplane.new
drawing = Drawing.new(airplane) # ==> moving in x dimension to 0
airplane.fly # ==> moving in x dimension to 1
airplane.fly # ==> moving in x dimension to 2


The observer.rb in the standard library is better if you want more
control over sending the notification. For example, if x,y,z all change,
but you only want to send the notification once for all three. (Or you
could use an attribute that contains the whole x,y,z point, and make
that observable.)
 
J

Joe Van Dyk

Joe said:
I have a class that represents an airplane. This class
has a bunch of data members: x/y/z positions, x/y/z
velocities, pitches, rolls, etc. What I did is let the
user configure what different attributes the airplane had
in a YAML config file, then have the airplane class read
in that file and setup attr_accessors for each attribute
in the file.

I have another object (call it airplane_drawing) that is
responsible for drawing this airplane on the screen. It
needs to have access to some of the attributes of the
airplane, and it needs have some of its own attributes
changed whenever airplane's attributes changes.

What is the best way to get airplane_drawing to be
notified whenever some attribute of airplane is changed?
Should I have a thread (or similar) that checks the
airplane object to see if anything's changed?

I hope I've explained this well enough for people to
understand... I feel like this is a fairly common thing
to do, but I'm not sure of the best way to do it.

Thanks,
Joe

Aha! Thanks for the response you guys. I knew that there was a pattern
that would help me, but I just couldn't think of it.
 
J

Jacob Fugal

What is the difference between self.x and @x?

self.x, sends either the :x or :x= method to self, depending if it's
an lvalue or rvalue respectively. @x just reads/writes to the instance
variable directly. If you're just using attr_accessor :x, they're the
same thing, but if you have, for instance:

class Foo
def bar
raise "@bar and @baz out of sync" unless @bar == @baz
@bar
end
def bar=( new_bar )
puts "setting @bar and @baz"
@bar = @baz = new_bar
end
def test1
self.bar = "test1"
end
def test2
@bar = "test2"
end
end

foo = Foo.new
foo.bar = "test"
# => setting @bar and @baz

puts foo.bar
# => test

foo.test1
# => setting @bar and @baz

puts foo.bar
# => test1

foo.test2
puts foo.bar
# RuntimeError: @bar and @baz out of sync

Using self.bar= keeps @bar and @baz in sync, while setting @bar
directly doesn't update @baz. Generally, if you have non-trivial
accessors for instance variables, you should use them even inside the
object rather than accessing the instance variables directly. I
usually use them even if the accessor is trivial, so that if the
accessor becomes non-trivial later I don't have to go find and fix all
the places where the instance variables are currently accessed
directly.

Jacob Fugal
 
J

Joel VanderWerf

Joe said:
What is the difference between self.x and @x?

In ruby, assigning to an instance variable @x like

@x += 1

does not invoke any methods (except the + method of the Integer class,
in this case). So there's no way for the Airplane class to detect the
assignment and (in this case) notify observers.

By contrast, the code

self.x += 1

forces a call to the method "x=", which has been defined by "observable
:x" to both set the value of @x and to notify observers.

And just for completeness, the code

x += 1

can only assign to a local variable, even if "x=" has been defined as a
method.
 
J

Joe Van Dyk

Joel VanderWerf said:
In ruby, assigning to an instance variable @x like

@x += 1

does not invoke any methods (except the + method of the Integer class,
in this case). So there's no way for the Airplane class to detect the
assignment and (in this case) notify observers.

By contrast, the code

self.x += 1

forces a call to the method "x=", which has been defined by "observable
:x" to both set the value of @x and to notify observers.

And just for completeness, the code

x += 1

can only assign to a local variable, even if "x=" has been defined as a
method.

Ok. In general, it's best to use self.x as opposed to @x?
 
J

Joel VanderWerf

Joe said:
Ok. In general, it's best to use self.x as opposed to @x?

Depends. I tend to use @x as rarely as possible, so that subclasses can,
if necessary, define methods that get/set the value in some other way.
Or if I later decide that the class itself needs to do something else
and not just go directly to the instance variable (for instance, logging
code). But accessing @x is faster than sending the #x method to self.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,161
Messages
2,570,892
Members
47,427
Latest member
HildredDic

Latest Threads

Top