Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; his 9-to-5 home is at the startup he founded, Tilde Inc.. There he works on Skylight, the smart profiler for Rails, and does Ember.js consulting. He is best known for his open source work, which also includes Thor and Handlebars. He travels the world doing open source evangelism and web standards work.

My 10 Favorite Things About the Ruby Language

I work with Ruby every single day, and over time have come to really enjoy using it. Here’s a list of some specific things that I really like about Ruby. Some of them are obvious, and some are shared with other languages. The purpose is to share things I like about Ruby, not to compare and contrast with any specific language.

1. Dynamic Typing

There are very good things about statically typed languages, such as compile-time verifiability and IDE support. However, in my experience, dynamic typing really helps get projects bootstrapped and smooths along changes, especially in the early to middle stages of a project.

I’m very happy that I don’t need to create a formal interface for my new objects to implement simply to enable me to easily swap out that class for another later on.

2. Duck Typing

This is effectively just an extension of Dynamic Typing. In Ruby, methods that expect to be able to operate on String objects don’t do checks for is_a?(String). They check whether the object respond_to?(:to_str) and then calls to_str on the Object if it does. Similarly, objects that represent Paths in Ruby can implement a to_path method to provide the path representation.

In Rails, we use this technique for objects that have “model” characteristics by expecting them to respond_to?(:to_model). This allows us to support any object in relevant contexts, provided those objects can supply us with a “model” representation of themselves.

3. Awesome Modules

Ruby provides a language feature similar to “traits” in Scala, Squeak, and Perl. Effectively, Ruby modules allow the dynamic addition of new elements of the class hierarchy at runtime. The use of super is dynamically evaluated at runtime to take into consideration any modules that might have been added, making it easy to extend functionality on a superclass as many times as desired, without being forced to decide where super will land at class declaration time.

Additionally, Ruby modules provide the lifecycle hooks append_features and included, which make it possible to use modules robustly to isolate extensions from one another and to dynamically extend classes on the basis of feature inclusion.

4. Class Bodies Aren’t Special

In Ruby, class bodies aren’t a special context. They’re simply a context where self points at the class object. If you’ve used Rails, you’ve probably seen code like this:

class Comment < ActiveRecord::Base
  validates_presence_of :post_id
end

It may look like validates_presence_of is a language feature, but it’s actually a method being called on Comment that is provided by ActiveRecord::Base.

That method can execute arbitrary code, also in the context of the class, including creating new methods, executing other pieces of code, or updating a class instance variable. Unlike Java annotations, which must be run at compile-time, Ruby class bodies can take runtime information into consideration, such as dynamically supplied options or the results of evaluating other code.

5. String Eval

This is likely a heresy. I’m not referring to arbitrary runtime String eval here, but rather String eval that is used to create methods early in the boot process of a Ruby application.

This can make it possible to take Ruby-defined structures, like Rails routes or AOP-definitions, and compile them into Ruby methods. Of course, it is possible to implement these things as add-ons to other languages, but Ruby makes it possible to implement these sorts of things in pure Ruby. It is, to a large degree, a self-hosting language.

6. Blocks and Lambdas

I’ve said this a few times and I’ll repeat myself: I don’t consider languages without anonymous lambdas to be powerful enough for me to use day-to-day. These constructs are actually extremely common, and found in languages as diverse as Ruby, JavaScript, Scala, Clojure, and of course Lisp.

They make it possible to implement block-scoped constructs that look like language features. The most common example usage is for File operations. In languages without lambdas, users are forced to use an inline “ensure” block every in the same lexical scope that they originally opened the file in, to ensure that the resource is closed.

In Java:

static void run(String in) 
throws FileNotFoundException {
  File input = new File(in);
  String line; Scanner reader = null;
  try {
    reader = new Scanner(input);
    while(reader.hasNextLine()) {
      System.out.println(reader.nextLine());
    }
  } finally { reader.close(); }
}

Among other things, the Java version needs to wrap the creation of the Scanner in a try block so it can be guaranteed to be closed. In contrast, the Ruby version:

def run(input)
  File.open(input, "r") do |f|
    f.each_line {|line| puts line }
  end
end

Because of the existence of blocks, it is possible to abstract away the need to close the File in a single location, minimizing programmer error and reducing duplication.

7. Combo Attack: Self-Hosting Language

The combination of several of the above features produce real-life examples of ways that we can “extend” the Ruby language in Rails. Consider the following:

  respond_to do |format|
    if @user.save
      flash[:notice] = 'User was successfully created.'
      format.html { redirect_to(@user) }
      format.xml { render :xml => @user, :status =>ted, :location => @user }
    else
      format.html { render :action => "new" }
      format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
    end
  end

In this case, we’re able to seamlessly mix methods (respond_to) with normal Ruby code (if and else) to produce a new block-scoped construct. Ruby’s semantics for blocks allow us to return or yield from inside the block, further blending the boundaries of the code-block and language constructs like if or while.

In Rails 3, we introduced:

class PeopleController < ApplicationController
  respond_to :html, :xml, :json
 
  def index
    @people = Person.find(:all)
    respond_with(@people)
  end
end

Here, respond_to is provided at the class-level. It tells Rails that respond_with (in index) should accept HTML, XML, or JSON as response formats. If the user asked for a different format, we automatically return a 406 error (Not Acceptable).

If you dig in a bit deeper, you can see that the respond_to method is defined as:

def respond_to(*mimes)
  options = mimes.extract_options!
 
  only_actions   = Array(options.delete(:only))
  except_actions = Array(options.delete(:except))
 
  mimes.each do |mime|
    mime = mime.to_sym
    mimes_for_respond_to[mime]          = {}
    mimes_for_respond_to[mime][:only]   = only_actions   unless only_actions.empty?
    mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
  end
end

This method is defined on the ActionController::MimeResponds::ClassMethods module, which is pulled into ActionController::Base. Additionally, mimes_for_respond_to is defined using class_inheritable_reader in the included lifecycle hook for the module. The class_inheritable_reader method (macro?) uses class_eval to add methods onto the class in question to emulate the built-in attr_accessor functionality.

Understanding all of the details isn’t important. What’s important is that using the Ruby features we described above, it’s possible to create layers of abstraction that can appear to add features to the Ruby language.

A developer looking at ActionController::MimeResponds need not understand how class_inheritable_reader works–he just needs to understand the basic functionality. And a developer looking at the API documentation need not understand how the class-level respond_to is implemented–she just needs to understand the provided functionality. With that said, peeling back each layer leads to simple abstractions that build on other abstractions. There’s no need to peel back the whole curtain at once.

8. Nice Literals

I often forget about this when programming in Ruby, only to crash back down to earth when using a language with fewer, less expressive literals.

Ruby has literals for just about everything:

  • Strings: single-line, double-line, interpolated
  • Numbers: binary, octal, decimal, hex
  • Null: nil
  • Boolean: true, false
  • Arrays: [1,2], %w(each word is element)
  • Hashes: {key => value} and in Ruby 1.9 {key: value}
  • Regular expressions: /hello/, %r{hello/path}, %r{hello#{interpolated}}
  • Symbols: :name and :”weird string”
  • Block: { block literal }

And I think I’m missing some. While it may seem academic, good, readable literals can increase the programmer’s ability to write short but extremely expressive code. It’s of course possible to achieve the same sorts of things as you can with literal Hashes by instantiating a new Hash object and pushing the keys and values on one at a time, but it reduces their utility as method parameters, for instance.

The terseness of the Hash literal has allowed Ruby programmers to effectively add a limited keyword argument feature to the language without having to get approval by the language designers. Yet another small example of self-hosting.

9. Everything is an Object, and All Code is Executed and Has a self

I showed this to some degree earlier, but a lot of the reason that Class bodies work the way they do is a consequence of the unfailing object orientation of the Ruby language. Inside of a class body, Ruby is simply executing code with a self pointing at the class. Additionally, nothing is special about the class context; it is possible to evaluate code in a class’ context from any location. Consider:

module Util
  def self.evaluate(klass)
    klass.class_eval do
      def hello
        puts "#{self} says Hello!" 
      end
    end
  end
end
 
class PersonName < String
  Util.evaluate(self)
end

This is exactly equivalent to:

class PersonName < String
  def hello
    puts "#{self} says Hello!" 
  end
end

By removing the artificial boundaries between code in different locations, Ruby reduces the conceptual overhead of creating abstractions. And this is the result of a strong, consistent object model.

One more example on this topic. This idiom is quite common in Ruby: possibly_nil && possibly_nil.method_name. Since nil is just an object in Ruby, sending it a message it does not understand will result in a NoMethodError. Some developers suggested the following syntax: possibly_nil.try(:method_name). This can be implemented in Ruby as follows:

class Object
  alias_method :try, :__send__
end
 
class NilClass
  def try
    nil
  end
end

Essentially, this adds the method try to every Object. When the Object is nil, try simply returns nil. When the object is not nil, try just calls the method in question.

Using targeted application of Ruby’s open classes, combined with the fact that everything in Ruby, including nil, is an object, we were able to create a new Ruby feature. Again, this isn’t such a big deal, but it’s another case where the right choices in the language can allow us to create useful abstractions.

10. Rack

I’m going to cheat a little bit since Rack isn’t part of the Ruby language, but it does demonstrate some useful things about it. First of all, the Rack library only hit 1.0 earlier this year, and already every single Ruby web framework is Rack compliant. If you use a Ruby framework, you can be guaranteed that it uses Rack, and any standard Rack middleware will work.

This was all done without any backward compatibility sacrifices, a tribute to the flexibility of the Ruby language.

Rack itself also leverages Ruby features to do its work. The Rack API looks like this:

Rack::Builder.new do
  use Some::Middleware, param
  use Some::Other::Middleware
  run Application
end

In this brief code snippet, a number of things are at work. First, a block is passed to Rack::Builder. Second, that block is evaluated in the context of a new instance of Rack::Builder, which gives it access to the use and run methods. Third, the parameter passed to use and run is a class literal, which in Ruby is a simple object. This allows Rack to call passed_in_middleware.new(app, param), where new is just a method call on the Class object Some::Middleware.

And in case you think the code to implement that would be heinous, here it is:

class Rack::Builder
  def initialize(&block)
    @ins = []
    instance_eval(&block) if block_given?
  end
 
  def use(middleware, *args, &block)
    @ins << lambda { |app| middleware.new(app, *args, &block) }
  end
 
  def run(app)
    @ins << app #lambda { |nothing| app }
  end
end

This is all that’s needed to implement the code I showed above the creates a new Rack application. Instantiating the middleware chain is a simple affair as well:

def to_app
  inner_app = @ins.last
  @ins[0...-1].reverse_each { |app| inner_app = app.call(inner_app) }
  inner_app
end
 
def call(env)
  to_app.call(env)
end

First, we take the last element from the chain, which is our endpoint. We then loop over the remaining elements in reverse, instantiating each middleware with the next element in the chain, and return the resulting object.

Finally, we define a call method on the Builder, which is required by the Rack specifically, that calls to_app and passes the environment through, kicking off the chain.

Through the use of a number of the techniques described in the post, we were able to create a Rack-compliant application that supports Rack middleware in under two dozen lines of code.

26 Responses to “My 10 Favorite Things About the Ruby Language”

Agree completely with #10. Rack in many ways distinguishes the ruby web world from the rest of web-languages. Sure you can do everything in rails in other languages(php,python,erlang,whatever), but without rack you have a commonality that the other languages lack. In the end its a win for ruby as a language because *most* ruby web developers are familiar with *several* of the ruby frameworks. Just like ruby gems, the easy integration of rack offers exposure of ruby systems that other languages can’t provide(well, not as good as ruby). Thus, rack = better developers, community wide.

Number 11 should definitely be the fact that Ruby has a very positive, supportive, and passionate community. Something without no language would ever get that far into, let’s say it, “mainstream” usage.

Don’t forget character literals (?a, ?b, ?c) and the ability to use underscores in numeric literals (1_000_000). The latter is one of those little touches that really sets the language apart in valuing readability. Backquotes or %x{} can also be considered a literal in its own right.

@Tobias: Touche…the community around ruby and rails is dynamic and passionate about what they do.

Great list, Yehuda. I agree with what you’ve listed here and many are reasons that I’ve enjoyed working in Python the past few years (I do think Python should grow an anonymous function syntax but also recognize why it hasn’t…) I’ve been heavily involved in JavaScript lately, and enjoy many of these same traits as well. I *am* curious to try a “modern” static (or hybrid language like Fan) one of these days because I’m sure the experience is very different than working in Java.

@Blake: Before Rack, Python had WSGI and while there are some specifics that are different, Python very much has that same level of commonality. In fact, WSGI is so useful that I no longer pick up a web framework when starting a new project — I just pick up the parts that I want and use WSGI as the glue. (This despite the fact that I founded one of the big Python frameworks.. ;)

method_missing is my personal favorite. The ability to add behavior to objects and classes at runtime is what makes Rails so elegant. I especially love being able to declare a database table, and then create an ActiveRecord model without having to specify anything about the database schema other than the relationships.

This was the one feature that compelled me to abandon Java in favor of Ruby on Rails three years ago, and I’ve never looked back.

@kevin Unfortunately, Django, the heavyweight in the Python world, has eschewed tight integration with WSGI for an NIH approach that results in a split community for things like middlewares. That might be solved over time, but it’s unfortunate in my opinion :)

Some comment to the sentence “If you use a Ruby framework, you can be guaranteed that it uses Rack, and any standard Rack middleware will work.” That’s only true for Ruby *web*-frameworks. In other contexts Rack dosn’t make sense.

+1 for method_missing. Letting the object, rather than the VM, determine whether a method invocation throws an exception is a big deal. It opens up whole new worlds of functionality.

Great post, thanks.

The big problem (in my book anyway) about method_missing is that it is so easy to get absolutely wrong, resulting in code that performs poorly and is overly hard to read – even for a rubyist used to definition and re-definition of methods on the fly. I wouldn’t want to do without method_missing in Ruby as I agree that it adds a lot of functionality to the language, but I really do think it should come with a warning label.

@robin yep — it’s really easy to misuse powerful features. Powerful abstraction capabilities far outweigh the risk in the long-term though.

@robin
>> ‘UNIX was never designed to keep people from doing stupid things, because that policy would also keep them from doing clever things.’.gsub /UNIX/, ‘Ruby’
=> “Ruby was never designed to keep people from doing stupid things, because that policy would also keep them from doing clever things.”

@wycats While you *can* use WSGI middleware with Django, I totally agree that the community there is definitely far more NIH. I think it’s a shame, but their plan is working for them.

While no single Python framework has the pull of Django, there are a great many Python folks using a full WSGI stack.

@wycats actually Django fully supports WSGI. You can use any WSGI middleware with it and run it with any WSGI server. It *also* supports a separate form of middleware, but it supported that form before WSGI ever existed. The NIH label is often used, but ignores that django predates most of the modular components that it “should” have been built with.

> Unlike Java annotations, which must be run at compile-time, [...]

This isn’t true – Java annotations can be (and often are) evaluated at runtime using reflection. Otherwise a fantastic article. I recently started doing JRuby development after years doing Java, and your list seems spot-on.

@teepark Of course. I wasn’t trying to imply that Django doesn’t support WSGI, but just that WSGI is not “the way” on Django, splitting the community. Like Django, Rails existed before Rack ever did, but we’ve undergone a fairly significant retooling effort to ensure that our framework is using the same stuff as every other Ruby web framework. Just two different philosophies of how a framework should interact with the larger language community that hosts it.

How about the fact that the language is expression-oriented? Making such beauties as these possible:

conf = if foo > 3
“bar.xml”
else
“baz.xml”

It saves only maybe 1 line of code, but it increases the readability of the logic immensely when used in the right places.

My favorite thing about ruby is actually something pretty minor.. I really love that parenthesis are optional.

Hi Yehuda,

I listened to you at Oxente Rails, in Brazil, and I started reading your posts. Now, I want to ask for permission to you to translate this post to Portuguese and put it in my blog, with credits and a link to your website. Can I do it? Please e-mail me.

Thanks.

Don’t forget proper boolean coercion rules, unlike nearly every other language.

Hey designers of dynamically typed languages: you don’t need a ‘false’ value for each data type IF YOU CAN PUT AN ACTUAL FALSE IN ANY VARIABLE. Think about it.

Great Synthesis that comes after several years of programming ruby. I made also a list some times ago, and I include Rake and RSpec, which are quite unique. I always miss these tools when I switch to another language.

@Derek,

You can also use Ruby’s ternary operator there:

conf = foo > 3 ? “bar.xml” : “baz.xml”

Much nicer.

Unfortunately I dont’ believe Ruby will be there big time in 10 years from now i.e it will still be a small niche player.

I have played with ruby a little bit, on and off since 2006 , I know I am a late comer. these are my thoughts about Ruby.

1) after so many years, it’s still like 2% in the Tiobe ranking

2) its weak typing, many people have said this is an asset, but I don’t. agree. Weak typing is a dsadvantage in my experience (working with very large apps in a big Financial Institution, running 24×7 ). Rubyists have always countered with a “do more testing” approach , but I don’t agree this will catch all your problems. I believe you have to have strong typing combined with a “do more testing” approach is better, especially when you are working in large teams where every one can touch any code.

3) speed is still a problem after so many years, everyone knows it’s a problem and yet the community is so slow to address that.

4) by the time, the ruby language and framework, has fixed its problems, the fad will have died and tne crowd will have moved on to the next big thing.

5) features in ruby is being and will be copied in existing languages and new languages

6) there are too many ways of doing the same thing in Ruby and this is a disadvantage as well, as this means if you are working in a big team, you have to learn every little nut and bolt of the language to be able support an application. that means, the learning curve is actually higher. In a one man team this is ok, because that one man picks one style and sticks to it and doesn’t have to worry about other people’s coding. It’s like driving on the street, you don’t have to think if the car next to you is going to drive left or right, and this prevents accidents. can you imagine for one moment that people are allowed to drive left and right in any direction ? what will happen?
I am aware that Saphire is trying to redo Ruby, with only one way of doing things. maybe this will help.

7) I think it’s safer in terms of longevity of languages to stay with Java and .net. because at some point Ruby will be forked and a new variant with a lot of differnt things added, will come out, fragmenting the market even more, which will reduce the current 2% to even less.

8) the big institutions are not doing Ruby. only startups, with basic CRUD applications.

9) the absence of a specification hurts the language. by the time the specs will come out and “accepted” by the community, it will be too late. Languages like Scala will already be well established.

10) the community looks and acts too amateurish generally speaking.

anyway these are my thoughts. sure people may disagree with what I said, but every one is free to one’s own opinion.
Let’s wait and see, only the future will tell whether one is right or not. why not touch base again in 5 years
from now

Inspired by item #6, the same code but with C#:

static void Run(string inp){
File.ReadLines(inp).ForEach(Console.WriteLine);
}

http://chaliy.name/blog/2009/9/read_line_in_java_ruby_and_csharp

I’ve just watched again Date Thomas’ Keynote about the “Ruby Object Model” (Scotland on Rails 2009 : http://scottishrubyconference.com/pages/previous)

It’s about the fourth time I’m watching this and each time I’m amazed how beautiful and simple – yet powerful – that is.

Leave a Reply

Archives

Categories

Meta