Checking Whether an Object Has Necessary Attributes

Problem

You're writing a class or module that delegates the creation of some of its instance variables to a hook method. You want to be make sure that the hook method actually created those instance variables.

Solution

Use the Object#instance_variables method to get a list of the instance variables. Check them over to make sure all the necessary instance variables have been defined. This Object#must_have_instance_variables method can be called at any time:

	class Object
	 def must_have_instance_variables(*args)
	 vars = instance_variables.inject({}) { |h,var| h[var] = true; h }
	 args.each do |var|
	 unless vars[var]
	 raise ArgumentError, %{Instance variable "@#{var} not defined"}
	 end
	 end
	 end
	end

The best place to call this method is in initialize or some other setup method of a module. Alternatively, you could accept values for the instance variables as arguments to the setup method:

	module LightEmitting
	 def LightEmitting_setup
	 must_have_instance_variables :light_color, :light_intensity
	 @on = false
	 end

	 # Methods that use @light_color and @light_intensity follow…
	end

You can call this method from a class that defines a virtual setup method, to make sure that subclasses actually use the setup method correctly:

	class Request
	 def initialize
	 gather_parameters # This is a virtual method defined by subclasses
	 must_have_instance_variables :action, :user, :authentication
	 end

	 # Methods that use @action, @user, and @authentication follow…
	end

 

Discussion

Although Object#must_have_instance_variables is defined and called like any other method, it's conceptually a "decorator" method similar to attr_accessor and private. That's why I didn't use parentheses above, even though I called it with multiple arguments. The lack of parentheses acts as a visual indicator that you're calling a decorator method, one that alters or inspects a class or object.

Here's a similar method that you can use from outside the object. It basically implements a batch form of duck typing: instead of checking an object's instance variables (which are only available inside the object), it checks whether the object supports all of the methods you need to call on it. It's useful for checking from the outside whether an object is the "shape" you expect.

	class Object
	 def must_support(*args)
	 args.each do |method|
	 unless respond_to? method
	 raise ArgumentError, %{Must support "#{method}"}
	 end
	 end
	 end
	end

	obj = "a string"
	obj.must_support :to_s, :size, "+".to_sym
	obj.must_support "+".to_sym, "-".to_sym
	# ArgumentError: Must support "-"

 

See Also

  • Recipe 10.16, "Enforcing Software Contracts"


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