Meet six misunderstood Ruby features

Learn about Ruby features likely to trip up C++ programmers

Suppose you are a C++ developer and you need to do some prototyping quickly in Ruby. When you pick up a Ruby reference book like the Pickaxe or browse the Ruby site, you see familiar constructs like class declaration, support for threads, and exception handling. Just when you thought you knew how Ruby works, you realize that concurrency in your Ruby code is not behaving like Boost threads, catch and throw are not what they seem, and others have used something called self all over the place in their Ruby script. Welcome to Ruby!

Share:

Arpan Sen (arpansen@gmail.com), Author, Independent

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.


developerWorks Contributing author
        level

01 May 2012

Also available in Chinese Russian Japanese

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 self keyword
  • The method_missing method
  • Exception handling
  • Threading

Note: All code in this article was tested and based on Ruby version 1.8.7.

Class hierarchy in Ruby

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.


Discovering self

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.


Threads in Ruby can be green

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.


Conclusion

This article covered quite a few areas:

  • Notions of class hierarchy in Rub
  • nSingleton methods
  • Deciphering the self keyword and method_missing method
  • 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!

Resources

Learn

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

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Linux
ArticleID=812075
ArticleTitle=Meet six misunderstood Ruby features
publish-date=05012012