If you're a C++ programmer and need to work in
Ruby, you have a bit of learning to do. This article discusses six Ruby
features that the Ruby newbie is likely to misunderstand, especially if he
or she comes from a similar-but-not-quite environment like
C++:
- The Ruby class hierarchy
- Singleton methods in Ruby
- The
selfkeyword - The
method_missingmethod - Exception handling
- Threading
Note: All code in this article was tested and based on Ruby version 1.8.7.
Class hierarchy in Ruby can be tricky. Create a class of type
Cat and start to play with its hierarchy (see
Listing 1).
Listing 1. Implicit class hierarchy in Ruby
irb(main):092:0> class Cat
irb(main):093:1> end
=> nil
irb(main):087:0> c = Cat.new
=> #<Cat:0x2bacb68>
irb(main):088:0> c.class
=> Cat
irb(main):089:0> c.class.superclass
=> Object
irb(main):090:0> c.class.superclass.superclass
=> nil
irb(main):091:0> c.class.superclass.superclass.superclass
NoMethodError: undefined method `superclass' for nil:NilClass
from (irb):91
from :0
|
All objects in Ruby (even the user-defined objects) are descendants of the
Object class, as is obvious from Listing 1.
This is in sharp contrast to C++. There's
nothing like a plain datatype—for example,
C/C++ int or double.
Listing 2 shows the class hierarchy for the
integer 1.
Listing 2. Class hierarchy for 1
irb(main):100:0> 1.class => Fixnum irb(main):101:0> 1.class.superclass => Integer irb(main):102:0> 1.class.superclass.superclass => Numeric irb(main):103:0> 1.class.superclass.superclass.superclass => Object |
So far so good. Now you know that classes themselves are objects of type
Class. Class in turn
is ultimately derived from Object, as
demonstrated in Listing 3, with the Ruby built-in
String class.
Listing 3. Class hierarchy for classes
irb(main):100:0> String.class => Class irb(main):101:0> String.class.superclass => Module irb(main):102:0> String.class.superclass.superclass => Object |
Module is the base class for
Class, and it comes with the caveat that you
cannot instantiate user-defined Module objects
directly. If you don't want to get into Ruby internals, it's safe to
consider Module having similar characteristics
to a C++ namespace: You can define your own
methods, constants, and so on. You include a
Module inside a
Class, and voilà, all the elements of the
Module are now magically the elements of the
Class. Listing 4 provides
an example.
Listing 4. Modules cannot be directly instantiated and can be used only with classes
irb(main):020:0> module MyModule
irb(main):021:1> def hello
irb(main):022:2> puts "Hello World"
irb(main):023:2> end
irb(main):024:1> end
irb(main):025:0> test = MyModule.new
NoMethodError: undefined method `new' for MyModule:Module
from (irb):25
irb(main):026:0> class MyClass
irb(main):027:1> include MyModule
irb(main):028:1> end
=> MyClass
irb(main):029:0> test = MyClass.new
=> #<MyClass:0x2c18bc8>
irb(main):030:0> test.hello
Hello World
=> nil
|
Here's the recap, then: When you write
c = Cat.new in Ruby,
c is an object of type
Cat that is derived from
Object. The Cat
class is an object of type Class, which is
derived from Module, which is in turn derived
from Object. Both the object and its type are
thus valid Ruby objects.
Singleton methods and editable classes
Now, look at singleton methods. Suppose you want to model something
akin to the human society in C++. How would you
do it? Have a class called Human, and then have
millions of Human objects? That's more like
modeling a zombie society; each human must have some unique
characteristic. Ruby's singleton methods come in handy here, as explained
in Listing 5.
Listing 5. Singleton methods in Ruby
irb(main):113:0> y = Human.new
=> #<Human:0x319b6f0>
irb(main):114:0> def y.paint
irb(main):115:1> puts "Can paint"
irb(main):116:1> end
=> nil
irb(main):117:0> y.paint
Can paint
=> nil
irb(main):118:0> z = Human.new
=> #<Human:0x3153fc0>
irb(main):119:0> z.paint
NoMethodError: undefined method `paint' for #<Human:0x3153fc0>
from (irb):119
|
Singleton methods in Ruby are methods only associated with a
particular object and not available to the general class. They are
prefixed with the object name. In Listing 5, the
paint method is specific to the object
y and y alone;
z.paint results in an undefined method error.
You can figure out the list of singleton methods in an object by calling
singleton_methods:
irb(main):120:0> y.singleton_methods => ["paint"] |
There's yet another way of defining singleton methods in Ruby. Consider the code in Listing 6.
Listing 6. Yet another way of creating singleton methods
irb(main):113:0> y = Human.new => #<Human:0x319b6f0> irb(main):114:0> class << y irb(main):115:1> def sing irb(main):116:1> puts "Can sing" irb(main):117:1> end irb(main):118:1>end => nil irb(main):117:0> y.sing Can sing => nil |
Listing 5 also opens interesting possibilities about adding new methods to
user-defined classes and to built-in Ruby existing classes
like String. That's impossible in
C++ unless you have access to the source code
for the classes that you use. Look into the
String class once more (Listing 7).
Listing 7. Ruby allows you to modify an existing class
irb(main):035:0> y = String.new("racecar")
=> "racecar"
irb(main):036:0> y.methods.grep(/palindrome/)
=> [ ]
irb(main):037:0> class String
irb(main):038:1> def palindrome?
irb(main):039:2> self == self.reverse
irb(main):040:2> end
irb(main):041:1> end
irb(main):050:0> y.palindrome?
=> true
|
Listing 7 clearly illustrates how you can edit an existing Ruby class to
add the methods of your choice. Here, I added the
palindrome? method to the
String class. Ruby classes are therefore
editable at run time—a powerful property.
Now that you have some idea of Ruby's class hierarchy and singletons, let's
move on to the self. Note that I used
self while defining the
palindrome? method.
The most common use of the self keyword is
perhaps to declare a static method in a Ruby class, as in Listing 8.
Listing 8. Using self to declare class static methods
class SelfTest
def self.test
puts "Hello World with self!"
end
end
class SelfTest2
def test
puts "This is not a class static method"
end
end
SelfTest.test
SelfTest2.test
|
As you can see in the output from Listing 8—shown in Listing 9—you cannot invoke non-static
methods without objects. The behavior resembles
C++.
Listing 9. Error when non-static methods are called without objects
irb(main):087:0> SelfTest.test
Hello World with self!
=> nil
irb(main):088:0> SelfTest2.test
NoMethodError: undefined method 'test' for SelfTest2:Class
from (irb):88
|
Before moving on to more esoteric uses and meanings of
self, note that you can also define a static
method in Ruby by prefixing the class name before the method name:
class TestMe
def TestMe.test
puts "Yet another static member function"
end
end
TestMe.test # works fine
|
Listing 10 offers a more interesting but rather
difficult-looking use of self.
Listing 10. Using meta-class to declare static methods
class MyTest
class << self
def test
puts "This is a class static method"
end
end
end
MyTest.test # works fine
|
This code defines test as a class static method
in a slightly different way. To understand what's happening, you need to
look at the class << self syntax in some
detail. class << self … end creates a
meta-class. In the method lookup chain, the meta-class of an object is searched
before accessing the base class of the object. If you define a method in the
meta-class, it can be invoked on the class. This is similar to the notion
of static methods in C++.
Is it possible to access a meta-class? Yes: Just return
self from inside
class << self … end. Note that in a Ruby
class declaration, you are under no obligation to put only method
definitions. Listing 11 shows the meta-class.
Listing 11. Getting hold of the meta-class
irb(main):198:0> class MyTest irb(main):199:1> end => nil irb(main):200:0> y = MyTest.new => #< MyTest:0x2d43fe0> irb(main):201:0> z = class MyTest irb(main):202:1> class << self irb(main):203:2> self irb(main):204:2> end irb(main):205:1> end => #<Class: MyTest > irb(main):206:0> z.class => Class irb(main):207:0> y.class => MyTest |
Coming back to the code in Listing 7, you see that
palindrome is defined as
self == self.reverse. In this context,
self is no different from
C++. The methods in
C++ and likewise in Ruby need an object to act
upon to modify or extract state information.
self refers to that object here. Note that
public methods can optionally be called by prefixing the
self keyword to indicate the object on which
the method is acting, as in Listing 12.
Listing 12. Using self to invoke methods
irb(main):094:0> class SelfTest3 irb(main):095:1> def foo irb(main):096:2> self.bar() irb(main):097:2> end irb(main):098:1> def bar irb(main):099:2> puts "Testing Self" irb(main):100:2> end irb(main):101:1> end => nil irb(main):102:0> test = SelfTest3.new => #<SelfTest3:0x2d15750> irb(main):103:0> test.foo Testing Self => nil |
You cannot call private methods in Ruby with the
self keyword prefixed. For a
C++ developer, this might get pretty
confusing. The code in Listing 13 clearly shows that
self cannot be used with private methods: The
call to the private method must only be made with an implicit object.
Listing 13. self cannot be used with private method invocation
irb(main):110:0> class SelfTest4
irb(main):111:1> def method1
irb(main):112:2> self.method2
irb(main):113:2> end
irb(main):114:1> def method3
irb(main):115:2> method2
irb(main):116:2> end
irb(main):117:1> private
irb(main):118:1> def method2
irb(main):119:2> puts "Inside private method"
irb(main):120:2> end
irb(main):121:1> end
=> nil
irb(main):122:0> y = SelfTest4.new
=> #<SelfTest4:0x2c13d80>
irb(main):123:0> y.method1
NoMethodError: private method `method2' called for #<SelfTest4:0x2c13d80>
from (irb):112:in `method1'
irb(main):124:0> y.method3
Inside private method
=> nil
|
Because everything in Ruby is an object, here's what you get when you
invoke self on the
irb prompt:
irb(main):104:0> self => main irb(main):105:0> self.class => Object |
The moment you launch irb, the Ruby interpreter
creates the main object for you. This main object is also known as the
top-level context in the Ruby-related literature.
Enough of self. Let's move on to dynamic methods and the
method_missing method.
The mystery behind method_missing
Consider the Ruby code in Listing 14.
Listing 14. method_missing in action
irb(main):135:0> class Test
irb(main):136:1> def method_missing(method, *args)
irb(main):137:2> puts "Method: #{method} Args: (#{args.join(', ')})"
irb(main):138:2> end
irb(main):139:1> end
=> nil
irb(main):140:0> t = Test.new
=> #<Test:0x2c7b850>
irb(main):141:0> t.f(23)
Method: f Args: (23)
=> nil
|
Clearly, if voodoo is your thing, Listing 14 should bring smile to your
face. What happened here? You created an object of type
Test, and then called
t.f with 23 as an
argument. But Test did not have
f as a method, and you should get a
NoMethodError or similar error message. Ruby is
doing something fascinating here: Your method calls are being intercepted
and handled by method_missing. The first
argument to method_missing is the method name
that's missing—in this case, f. The
second and last argument is *args, which
captures the arguments being passed to f. Where
could you use something like this? Among other options, you could easily
forward method calls to an included Module or a
component object without explicitly providing a wrapper application
programming interface for each call in the top-level class.
Take a look at some more voodoo in Listing 15.
Listing 15. Using the send method to pass arguments to a routine
irb(main):142:0> class Test
irb(main):143:1> def method1(s, y)
irb(main):144:2> puts "S: #{s} Y: #{y}"
irb(main):145:2> end
irb(main):146:1> end
=> nil
irb(main):147:0>t = Test.new
irb(main):148:0> t.send(:method1, 23, 12)
S: 23 Y: 12
=> nil
|
In Listing 15, class Test has a method
called method1 defined. However, instead of
calling the method directly, you make a call to the
send method. send is
a public method of the class Object and
therefore available for Test (all classes are
derived from Object, remember). The first
argument to the send method is a symbol or
string that denotes the method name. What can the
send method do that in the normal course you
cannot? You can access private methods of a class using the
send method. Of course, whether this is a good
feature to have remains debatable. Look at the code in Listing 16.
Listing 16. Accessing class private methods
irb(main):258:0> class SendTest
irb(main):259:1> private
irb(main):260:1> def hello
irb(main):261:2> puts "Saying Hello privately"
irb(main):262:2> end
irb(main):263:1> end
=> nil
irb(main):264:0> y = SendTest.new
=> #< SendTest:0x2cc52c0>
irb(main):265:0> y.hello
NoMethodError: private method `hello' called for #< SendTest:0x2cc52c0>
from (irb):265
irb(main):266:0> y.send(:hello)
Saying Hello privately
=> nil
|
Throw and catch are not what they seem
If you come from a C++ background like I do and
have a tendency to try writing exception-safe code, then you will
probably start feeling at home the moment you see that Ruby has the
throw and catch
keywords. Unfortunately, throw and
catch have a completely different meaning in
Ruby.
Ruby typically handles exceptions using
begin…rescue blocks. Listing 17 provides an example.
Listing 17. Exception handling in Ruby
begin
f = File.open("ruby.txt")
# .. continue file processing
rescue ex => Exception
# .. handle errors, if any
ensure
f.close unless f.nil?
# always execute the code in ensure block
end
|
In Listing 17, if something bad happens while trying to open the file
(maybe a missing file or an issue with file permissions), the code in the
rescue block runs. The code in the
ensure block always runs, irrespective
of whether any exceptions were raised. Note that the presence of the
ensure block after the
rescue block is optional, however. Also, if an
exception must be thrown explicitly, then the syntax is
raise <MyException>. If you choose to
have your own exception class, you might want to derive the same from
Ruby's built-in Exception class to take
advantage of existing methods.
The catch-and-throw facility in Ruby is not really exception handling: You
can use throw to alter program flow. Listing 18 shows an example using
throw and catch.
Listing 18. Throw and catch in Ruby
irb(main):185:0> catch :label do irb(main):186:1* puts "This will print" irb(main):187:1> throw :label irb(main):188:1> puts "This will not print" irb(main):189:1> end This will print => nil |
In Listing 18, when the code flow hits the throw
statement, the execution is interrupted, and the interpreter begins
looking for a catch block that handles the
corresponding symbol. Execution is restarted from where the
catch block ends. Look at the example of
throw and catch in
Listing 19: Note that you can easily spread
catch and throw
statements across functions.
Listing 19. Exception handling in Ruby: nested catch blocks
irb(main):190:0> catch :label do irb(main):191:1* catch :label1 do irb(main):192:2* puts "This will print" irb(main):193:2> throw :label irb(main):194:2> puts "This won't print" irb(main):195:2> end irb(main):196:1> puts "Neither will this print" irb(main):197:1> end This will print => nil |
Some people have gone to the extent of saying that Ruby takes the
C
goto madness to altogether new heights
with its catch and
throw support. Given that there could be
several nested layers of functions with catch
blocks possible at every level, the goto
madness analogy does seem to have some steam here.
Ruby version 1.8.7 does not support true
concurrency. It really, truly does not. But you have the Thread construct
in Ruby, you say. Right you are. But that
Thread.new does not spawn a real operating
system thread every time you make a call to the same. What Ruby supports
is green threads: The Ruby interpreter uses a single operating
system thread to handle the workload from multiple application-level
threads.
This "green thread" concept is useful while some thread is waiting on some I/O to occur, and you can easily schedule a different Ruby thread to make good use of the CPU. But this construct cannot use a modern multi-core CPU. (Wikipedia provides an excellent piece that explains what green threads are. See Resources for a link.)
This final example (see Listing 20) proves the point.
Listing 20. Multiple threads in Ruby
#!/usr/bin/env ruby
def func(id, count)
i = 0;
while (i < count)
puts "Thread #{i} Time: #{Time.now}"
sleep(1)
i = i + 1
end
end
puts "Started at #{Time.now}"
thread1 = Thread.new{func(1, 100)}
thread2 = Thread.new{func(2, 100)}
thread3 = Thread.new{func(3, 100)}
thread4 = Thread.new{func(4, 100)}
thread1.join
thread2.join
thread3.join
thread4.join
puts "Ending at #{Time.now}"
|
Assuming that you have the top utility on your
Linux® or UNIX® box, run the code in a terminal, get the process
ID, and run top –p <process id>. When
top starts, press Shift-H to list the number of
threads running. You should see only a single thread confirming what you
knew anyway: concurrency in Ruby 1.8.7 is a myth.
All that said, green threads are not bad. They are still useful in heavy-duty I/O-bound programs, not to mention that this approach is probably the most portable across operating systems.
This article covered quite a few areas:
- Notions of class hierarchy in Rub
- nSingleton methods
- Deciphering the
selfkeyword andmethod_missingmethod - Exceptions
- Threading
Notwithstanding its quirks, Ruby is fun to program in and extremely powerful in its ability to do a lot with minimum code. No wonder, then, that large-scale applications like Twitter are using Ruby to harness their true potential. Happy coding in Ruby!
Learn
- Read Programming Ruby: The Pragmatic Programmers' Guide (Dave Thomas, Chad Fowler, and Andy Hunt; 2nd edition), a Ruby must-read that is popularly known as the Pickaxe book.
- Check out another invaluable Ruby resource, The Ruby Programming Language [Yukihiro "Matz" Matsumoto (Ruby's creator) and David Flanagan, O'Reilly, 2008].
- Visit To Ruby From C and C++, a great site for
C/C++programmers who want to learn Ruby. - Learn more about green threads in a good explanation on Wikipedia.
- In the Open Source area on developerWorks, find extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM products.
- In the developerWorks Linux zone, find hundreds of how-to articles and tutorials, as well as downloads, discussion forums, and a wealth of other resources for Linux developers and administrators.
- Stay current with developerWorks technical events and webcasts focused on a variety of IBM products and IT industry topics.
- Attend a free developerWorks Live! briefing to get up-to-speed quickly on IBM products and tools as well as IT industry trends.
- Listen to developerWorks podcasts for interesting interviews and discussions for software developers.
- Follow developerWorks on Twitter.
- Watch developerWorks demos that range from product installation and setup for beginners to advanced functionality for experienced developers.
Get products and technologies
- Access IBM trial software (available for download or on DVD) and innovate in your next open source development project using software especially for developers.
Discuss
- Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis. Help build the Real world open source group in the developerWorks community.
Arpan Sen is a lead engineer working on the development of software in the electronic design automation industry. He has worked on several flavors of UNIX, including Solaris, SunOS, HP-UX, and IRIX, as well as Linux and Microsoft Windows for several years. He takes a keen interest in software performance-optimization techniques, graph theory, and parallel computing. Arpan holds a post-graduate degree in software systems.




