Ruby - the good parts

Ruby is a programming language designed by Yukihiro “Matz” Matsumoto, with a focus on programmers productivity and fun. The author stated it himself many times in interviews and talks, such as Google Tech Talk in 2008: “I hope to see Ruby help every programmer in the world to be productive, and to enjoy programming, and to be happy. That is the primary purpose of Ruby language.”

I believe Matz did a great job in achieving his goals. Let’s have a look at some of the things that Ruby got right. This short list can serve either as a reminder for those who already know Ruby, or as a set of arguments for convincing you that it’s worth investing your time to learn Ruby, if you haven’t done so yet.

Object model

“Everything is an object” - you have probably heard this statement repeated over and over for every object oriented language, like Java or C#, only to find out later that these languages have many exception to this rule, such as primitive types - numbers or boolean values. Ruby’s object model, greatly inspired by Smalltalk, is different. Numbers, boolean values, string literals etc. are all objects. You can call methods on them:

5.even?                     # => false 
1.upto(4).to_a              # => [1, 2, 3, 4]
"A string literal".length   # => 16

You can list their methods:

42.methods
=> [:to_s,
 :inspect,
 :-@,
 :+,
 :-,
 :*,
 :/,
 :div,
 :%,
...

You can also check their class:

1.class     # => Fixnum
1.3.class   # => Float
true.class  # => TrueClass

What might be suprising at first if you’re new to Ruby, is that classes are objects too! They are instances of the class Class:

Fixnum.is_a?(Object)  # => true
Fixnum.class          # => Class

We can inspect the inheritance hierarchy of Class using ancestors method:

Class.ancestors   # => [Class, Module, Object, Kernel, BasicObject] 

So, does it all mean that really everything is an object? Well, even in Ruby there are some exceptions. Perhaps the most notable are methods and blocks. They are not objects themselves, but they can be wrapped by one (Proc).

Metaprogramming

Metaprogramming is a technique that allows programs to manipulate themselves or another programs at a runtime. In other words, metaprogramming makes it possible to write programs that write programs. Thanks to its dynamic nature and expressive syntax, Ruby’s metaprogramming capabilities are truly astounding. With these techniques we can write programs that are cleaner and easier to mantain. Here are some examples of things we can do in Ruby:

Singleton methods

Ruby allows you to define a method, that is available only for a specific object. We call these methods singleton methods. When you add such method to an object, it can be invoked only from this particular object. Other instances of the same class can’t access it. Let’s see it in action:

object = Object.new           # => #<Object:0x00000001671b68> 
my_object = Object.new        # => #<Object:0x0000000168b9a0> 

def my_object.custom_method
  "Singleton method called"
end

my_object.custom_method       # => "Singleton method called" 
object.custom_method          # => NameError: undefined local variable or method `object' for main:Object

In the above example we created two instances of class Object. Next, we’ve added a method, called custom_method to one of them - my_object. Finally, we verified that the method is indeed available only for the object it was defined for. The other instance throws an exception when we try to invoke custom_method on it.

method_missing

Ruby gives you a way of handling calls to methods that don’t really exist. To understand how it works, let’s first have a look at the method lookup path. When object receives a message, Ruby searches for a right method in the following order:

  1. singleton class
  2. class
  3. modules (starting from the one included last)
  4. superclass
  5. superclass’ modules
  6. repeating steps 4 and 5 until the BasicObject (or Object, for Ruby versions below 1.9) is reached

If the invoked method can’t be found, the dispatch starts again. This time, however, it looks for a special method, called method_missing. Once it’s found, the method_missing is called with following arguments:

  • original method name
  • parameters passed to originally called method
  • block passed to the original method

Let’s see it in action:

class A
  def method_missing(method, *args, &block)
    "method_missing captured a call to #{method} with arguments: #{args.join(', ')}"
  end
end

obj = A.new

obj.random_method(1, 'abc', true)
# => "method_missing captured a call to random_method with arguments: 1, abc, true"

This technique allows you to ‘create’ methods dynamically. An example of method_missing usage in practice can be (now deprecated) Rails dynamic finders, such as find_by_name`.

Writing code dynamically

It’s possible to write a code that writes code. For example, imagine that you need your object to have a few methods that have a very similar behavior. You can create these methods using define_method:

class A
  METHOD_NAMES = ['abc', 'def', 'ghi', 'jkl', 'mno']

  METHOD_NAMES.each do |name|
    define_method(name) do |arg|
      "Called method #{name} with argument: #{arg}"
    end
  end
end

obj = A.new
obj.abc(42)        # => "Called method abc with argument: 42"
obj.mno('arg')     # => "Called method mno with argument: arg"

Note about risks

Metaprogrammin adds great flexibility to the language. It allows us to write more with less code. It can help in reducing repetition or just writing cleaner and more concise code. We can’t, however, forget that all these posiibilities come at a certain cost. With great power comes great responsibility. While properly used metaprogramming techniques can save you much effort, overusing them can give the opposite results. You might end up with a code that is complex and difficult to debug. Use above techniques with care!

Modules

Ruby’s modules are perhaps one of the language’s most powerful features. They allow to modify class hierarchy dynamically at runtime. Let’s create a very simple module M:

module M
  def mod_method
    "Module method invoked"
  end
end

Module methods can be mixed in as a class methods, using extend keyword:

class A
  extend M
end

A.mod_method    # => "Module method invoked"

or as instance methods, with include keyword:

class B
  include M
end

B.new.mod_method    # => "Module method invoked"

Mixins are very clean and elegant equivalents of multiple inheritance or traits found in another languages.

Blocks, Procs and lambdas

Blocks in Ruby are, in simple terms, a chunks of code grouped together by enclosing braces or a pair of do-end keywords. They can be passed to methods, which can call a block using yield statement. When the block is passed to method, it’s not executed immediately. Instead, the context in which it was encountered by Ruby interpreter is remembered (self, local variables etc.). When it’s then called from within the method, it is executed in that context. This concept is called closure in computer science. Let’s see a live example:

var = "something"

def meth
  var = "something else"
  yield
end

meth { puts var }   # => "something"

Passing blocks to methods is a very common practice in Ruby. For example, it’s the most typical way of iterating over collections.

squares = [1, 2, 3].map{|i| i**2}         # => [1, 4, 9]

even = [1,2,3,4,5,6].select{|i| i.even?}  # => [2, 4, 6]

This syntax feels very natural and is extremely expressive.

So far we’ve been passing anonymous blocks to the methods when they were called. While it’s a very useful and simple technique, there are times where it would be more convenient to store the block in a variable and be able to reuse it later. Ruby has a feature that allows you to do just that. You can wrap your blocks of code into Proc, making it first-class object.

square = Proc.new do |i|
  i**2
end

class Array
  def iterate(my_proc)
    self.each_with_index do |element, index|
      self[index] = my_proc.call(element)
    end
  end
end

[1, 2, 3, 4].call(square)    # => [1, 4, 9, 16]

There’s one more construct in Ruby that is very similar to Proc. It’s called lambda. Let’s first look at the usage of lambda and then talk about how it’s different from Proc

square_lambda = lambda{|i| i**2}

class Array
  def iterate(my_proc)
    self.each_with_index do |element, index|
      self[index] = my_proc.call(element)
    end
  end
end

[1, 2, 3, 4].call(square_lambda)    # => [1, 4, 9, 16]

As you can see, lambas can be used in pretty much the same way as Procs. There are, however, two main differences. First, Procs don’t check the number of arguments passed, while lambdas do.

print_numbers_proc = Proc.new {|a, b| "Your numbers are: #{a} and #{b}"}
print_numbers_lambda = lambda {|a, b| "Your numbers are: #{a} and #{b}"}

print_numbers_proc.call(1,2)     # => "Your numbers are: 1 and 2"
print_numbers_proc.call(1)       # => "Your numbers are: 1 and "

print_numbers_lambda.call(1,2)   # => "Your numbers are: 1 and 2"
print_numbers_lambda.call(1)     # => ArgumentError: wrong number of arguments (1 for 2)

The second difference is in the way return works in both of them. If you execute lambda containing return instruction from within a method, execution flow will be continued from the line right after the lambda was called. In case of Proc, return will finish execution of the outer method. As always, let’s see an example:

def test_lambda_return
  lambda {
    return "inside lambda"
  }.call
  return "inside test_lambda_return method"
end

def test_proc_return
  Proc.new {
    return "inside Proc"
  }.call
  return "inside test_proc_return method"
end

test_lambda_return     #=> "inside test_lambda_return method"
test_proc_return       #=> "inside Proc"

Emphasis on testing

Ruby community puts large emphasis on using methodologies that are supposed to ensure good code quality. Perhaps the most popular of such development processes is Test Driven Development, or TDD in short. One of the best resources for people who start to learn ruby is RubyKoans. It teaches you Ruby by writing tests, so by the time you become proficient in writing Ruby code, you also know how to test it.

The emphasis on testing can also be seen in abundance of tools designed for this purpose. Most popular testing frameworks include RSpec, Minitest and Test::Unit. For mocking you can use Rspec-mocks or Mocha. Simulating a browser is possible with Capybara or Webrat. And that’s just a tip of the iceberg.

Libraries

The power of a language lies not only within the language itself, but also the community built around it. Ruby has a thriving community, that creates a lot of great open-source libraries distributed as gems.

The most popular Ruby-based library, and also the one that contributed greatly to its popularity, is of course Rails. However, it’s not the only web framework written in Ruby. Two other most notable examples are Sinatra and Padrino. More exhaustive list of Ruby web frameworks can be found here.

What if you’re not looking for a web framework, but and ORM library? You can choose from ActiveRecord, Sequel, DataMapper or Squeel.

Need background jobs in your app? Have a look at Resque, Delayed Job or sidekiq.

The list could go on and on.

Whatever you’re looking for, thare’s a good chance that there’s already at least one library that will help you solve your problem with ease.

When you find the gem you would like to use in your project, installation is also a breeze. Either run gem install gem_name, or add the gem to you Gemfile and run bundle install command, if you’re using Bundler. It doesn’t get any simpler than this!

Syntax

Last but not least, one of the things I love about Ruby is its syntax. It’s elegant and feels very natural. Blocks, implicit return and dynamic typing result in uncluttered, clean and expressive code. A great thing about Ruby syntax is multitude of syntactic sugar - construct that serve a purpose of making the code easier and faster to write and read, while not necessarily following normal rules.

Below I listed a couple of interesting Ruby syntax elements:

  • common usage of blocks
  • optional return statements - last statement value in a method is returned by default
  • shorthand for one line conditional statements
do_something if condition

instead of

if condition
  do_something
end
  • multiple assignment
def my_method(*args)
  first_name, last_name = args
end

my_method('Han', 'Solo')
  • one line rescue - you can use rescue statement after single line instruction to provide return value in case an exception is thrown
arr.count rescue 0
  • optional brackets in method calls - help in writing code that reads more like natural language

  • convention naming rules for different variable types allow you to know the variable’s scope just by looking at its name

local_variable
@instance_variable
@@class_variable
$global_variable

by Kamil Bielawski

comments powered by Disqus