Managing Instance Data

Problem

You want to associate a variable with an object. You may also want the variable to be readable or writable from outside the object.

Solution

Within the code for the object's class, define a variable and prefix its name with an at sign ( @). When an object runs the code, a variable by that name will be stored within the object.

An instance of the Frog class defined below might eventually have two instance variables stored within it, @name and @speaks_english:

	class Frog
	 def initialize(name)
	 @name = name
	 end

	 def speak
	 # It's a well-known fact that only frogs with long names start out
	 # speaking English.
	 @speaks_english ||= @name.size > 6
	 @speaks_english ? "Hi. I'm #{@name}, the talking frog." : 'Ribbit.'
	 end
	end

	Frog.new('Leonard').speak # => "Hi. I'm Leonard, the talking frog."

	lucas = Frog.new('Lucas')
	lucas.speak # => "Ribbit."

If you want to make an instance variable readable from outside the object, call the attr_reader method on its symbol:

	lucas.name
	# NoMethodError: undefined method 'name' for #

	class Frog
	 
attr_reader :name
	end
	lucas.name # => "Lucas"

Similarly, to make an instance variable readable and writable from outside the object, call the attr_accessor method on its symbol:

	lucas.speaks_english = false
	# => NoMethodError: undefined method 'speaks_english=' for #

	class Frog
	 
attr_accessor :speaks_english
	end
	lucas.speaks_english = true
	lucas.speak # => "Hi. I'm Lucas, the talking frog."

 

Discussion

Some programming languages have complex rules about when one object can directly access to another object's instance variables. Ruby has one simple rule: it's never allowed. To get or set the value of an instance variable from outside the object that owns it, you need to call an explicitly defined getter or setter method.

Basic getter and setter methods look like this:

	class Frog
	 def speaks_english
	 @speaks_english
	 end

	 def speaks_english=(value)
	 @speaks_english = value
	 end
	end

But it's boring and error-prone to write that yourself, so Ruby provides built-in decorator methods like Module#attr_reader and Module#attr_accessor. These methods use metaprogramming to generate custom getter and setter methods for your class. Calling attr_reader :speaks_english generates the getter method speaks_english and attaches it to your class. Calling attr_accessor :instance_variable generates both the getter method speaks_english and the setter method speaks_english=.

There's also an attr_writer decorator method, which only generates a setter method, but you won't use it very often. It doesn't usually make sense for an instance variable to be writable from the outside, but not readable. You'll probably use it only when you plan to write your own custom getter method instead of generating one.

Another slight difference between Ruby and some other programming languages: in Ruby, instance variables (just like other variables) don't exist until they're defined. Below, note how the @speaks_english variable isn't defined until the Frog#speak method gets called:

	michael = Frog.new("Michael")
	# => #
	michael.speak # => "Hi. I'm Michael, the talking frog."
	michael
	# => #

It's possible that one Frog object would have the @speaks_english instance variable set while another one would not. If you call a getter method for an instance variable that's not defined, you'll get nil. If this behavior is a problem, write an initialize that initializes all your instance variables.

Given the symbol for an instance variable, you can retrieve the value with Object#instance_variable_get, and set it with Object#instance_variable_set.

Because this method ignores encapsulation, you should only use it in within the class itself: say, within a call to Module#define_method.

This use of instance_variable_get violates encapsulation, since we're calling it from outside the Frog class:

	michael.instance_variable_get("@name") # => "Michael"
	michael.instance_variable_set("@name", 'Bob')
	michael.name # => "Bob"

This use doesn't violate encapsulation (though there's no real need to call define_method here):

	class Frog
	 define_method(:scientific_name) do
	 species = 'vulgaris'
	 species = 'loquacious' if instance_variable_get('@speaks_english')
	 "Rana #{species}"
	 end
	end
	michael.scientific_name # => "Rana loquacious"

 

See Also

  • Recipe 10.10, "Avoiding Boilerplate Code with Metaprogramming"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net