Random Ruby Tricks: Class.new

9 September 2012

If you didn't know, classes are first-class objects in Ruby:

1.9.3p194 :001 > String.class
 => Class 
1.9.3p194 :002 > Class.class
 => Class 

How is this useful, though?

Inheritance

You may create very simple classes at times. This often happens when subclassing an error of some sort:

class MyException < StandardError
end

raise MyException

That whole thing is awkward to me, though. Why bother with the end if you don't need it? Turns out, it's easy to make your own Class object: just use Class.new:

1.9.3p194 :001 > Class.new
 => #<Class:0x007fee209f42f8> 

But that doesn't help us a ton. Turns out Class.new takes an argument: the class this new class inherits from:

1.9.3p194 :002 > Class.new(StandardError)
 => #<Class:0x007fee209ee2e0> 

We can then save this in our own constant:

1.9.3p194 :003 > MyException = Class.new(StandardError)
 => MyException 
1.9.3p194 :004 > MyException.new
 => #<MyException: MyException> 
1.9.3p194 :005 > MyException.ancestors
 => [MyException, StandardError, Exception, Object, Kernel, BasicObject] 

I think I like this notation a bit better than class...end. But many people may not be familiar with it.

Passing a block

Of course, you can pass a block to Class.new and it'll work like you'd expect:

1.9.3p194 :001 > Foo = Class.new do
1.9.3p194 :002 >     def bar
1.9.3p194 :003?>     puts "bar"
1.9.3p194 :004?>     end
1.9.3p194 :005?>   end
 => Foo 
1.9.3p194 :006 > Foo.new.bar
bar
 => nil 

The block gets class_eval'd. I haven't found a good use for this one, exactly, but it's good to know about!

EDIT: @pete_higgins has chimed in on Twitter. I knew I'd done this before!

new vs initialize

Ever wonder why you call Foo.new but define Foo.initialize? It's pretty simple. Here:

VALUE rb_class_new_instance(int argc, VALUE *argv, VALUE klass)
{
    VALUE obj;

    obj = rb_obj_alloc(klass);
    rb_obj_call_init(obj, argc, argv);

    return obj;
}

Obviously! This is the source for Foo.new. You might not read C, but it's pretty simple: it first allocates the space for the object using rb_obj_alloc, and then calls initialize using rb_obj_call_init.