Checking Class or Module Membership

Problem

You want to see if an object is of the right type for your purposes.

Solution

If you plan to call a specific method on the object, just check to see whether the object reponds to that method:

	def send_as_package(obj)
	 if obj.respond_to? :package
	 packaged = obj.package

	 else
	 $stderr.puts "Not sure how to package a #{obj.class}."
	 $stderr.puts 'Trying generic packager.'
	 package = Package.new(obj)
	 end
	 send(package)
	end

If you really can only accept objects of one specific class, or objects that include one specific module, use the is_a? predicate:

	def multiply_precisely(a, b)
	 if a.is_a? Float or b.is_a? Float

	 raise ArgumentError, "I can't do precise multiplication with floats."
	 end
	 a * b
	end

	multiply_precisely(4, 5) # => 20
	multiply_precisely(4.0, 5)
	# ArgumentError: I can't do precise multiplication with floats.

 

Discussion

Whenever possible, you should use duck typing (Object#respond_to?) in preference to class typing (Object#is_a?). Duck typing is one of the great strengths of Ruby, but it only works if everyone uses it. If you write a method that only accepts strings, instead of accepting anything that supports to_str, then you've broken the duck typing illusion for everyone who uses your code.

Sometimes you can't use duck typing, though, or sometimes you need to combine it with class typing. Sometimes two different classes define the same method (especially one of the operators) in completely different ways. Duck typing makes it possible to silently do the right thing, but if you know that duck typing would silently do the wrong thing, a little class typing won't hurt.

Here's a method that uses duck typing to see whether an operation is supported, and class typing to cut short a possible problem before it occurs:

	def append_to_self(x)
	 unless x.respond_to? :<<
	 raise ArgumentError, "This object doesn't support the left-shift operator."
	 end
	 if x.is_a? Numeric
	 raise ArgumentError,
	 "The left-shift operator for this object doesn't do an append."
	 end
	 x << x
	end

	append_to_self('abc') # => "abcabc"
	append_to_self([1, 2, 3]) # => [1, 2, 3, […]]

	append_to_self({1 => 2})
	# ArgumentError: This object doesn't support the left-shift operator.

	append_to_self(5)
	# ArgumentError: The left-shift operator for this object doesn't do an append.
	5 << 5 # => 160
	# That is, 5 * (2 ** 5)

An alternative solution approximates the functionality of Java's interfaces. You can create a dummy module for a given capability, have all appropriate classes include it, and use is_a? to check for inclusion of the module. This requires that each participating class signal its ability to perform a certain task, but it doesn't tie you to any particular class hierarchy, and it saves you from calling the wrong method just because it has the right name.

	module ShiftMeansAppend
	 def <<(x)
	 end
	end

	class String
	 include ShiftMeansAppend
	end

	class Array
	 include ShiftMeansAppend
	end

	def append_to_self(x)
	 unless x.is_a? ShiftMeansAppend
	 raise ArgumentError, "I can't trust this object's left-shift operator."
	 end
	 x << x
	end
	append_to_self 4
	# ArgumentError: I can't trust this object's left-shift operator.

	append_to_self '4' # => "44"

 

See Also

  • Recipe 1.12, "Testing Whether an Object Is String-Like"


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