tag ruby, tips

Using Anonymous Classes and Modules in Ruby

One of my favorite aspects of Ruby is that just about everything is an object, even Class and Module. The ability to instantiate "anonymous" classes and modules can give you a great deal of power and help you out in situations where you otherwise might not have a clean solution.

What do Anonymous Things Look Like?

Anonymous classes and modules are just like other classes and modules but a little different. This can be seen best by example:

c = Class.new # => #<Class:0x000001009dea80>
c.name        # => nil (normally this would be something like "Object")
c.class       # => Class
c.new         # => #<#<Class:0x000001009dea80>:0x0000010096b3c8>

So what we did is create an instance of the Class class which is itself a class. Notice that by calling c.new I was able to create an instance of my anonymous class just like I would with a normal one. Now let's take a look at anonymous modules:

m = Module.new

c = Class.new
c.send :include, m

c.ancestors # => [#<Class:0x000001028640b8>, #<Module:0x00000100849030>, Object, Kernel, BasicObject]

Once you've created an anonymous module you can include it in a class just like you would with a normal module. Anonymous classes and modules behave just like the real thing, they just don't have constant names attached to them!

So now that you can recognize an anonymous class or module, how can you use them in the real world? Is this just a bunch of theoretical nonsense with no practical application?

Keep Specs Fresh With Anonymous Classes

Sometimes it can be difficult to test a module since you don't want to test it tied to a specific implementation. Let's say we have a module that looks like this:

module MyModule
  def something?
    true 
  end
end

How can we test it? One solution is to create a "test" class like this:

class MyModuleTest
  include MyModule
end

it 'should be something' do
  MyModuleTest.new.should be_something
end

But there's a problem with that approach: since your test class lives outside the scope of your test, it doesn't get torn down at the end of each test run without manual intervention. So what can we do instead?

let(:fresh_class) do
  Class.new{ include MyModule }
end

it 'should do something' do
  fresh_class.new.should be_something
end

Now we get a pristine class each and every test that we know doesn't have any state baggage. Anonymous classes to the rescue!

Anonymous Modules for Composite Functionality

So now we've seen an example of when anonymous classes can be used, but what about anonymous modules? Well here I'll show you a real piece of code from Grape, my framework for building REST-like APIs.

Grape allows users to define helper methods similar to Sinatra. When a user creates an endpoint for the API, Grape gives them access to all helper methods that were defined in the current context as well as parent contexts. We need a way to store all of the helpers that a user provides and be able to instantly create a module that is a composite of all of the helpers that were defined thus far. It sounds complicated (and it is, a little bit), but here's how we might use it in the end:

class MyAPI < Grape::API
  helpers do
    def user?; false end
  end

  namespace :authenticated do
    helpers do
      def user?; true end
    end

    get '/' do
      user? # => true
    end
  end

  get '/' do
    user? # => false
  end
end

Now here's the behind-the-scenes code that makes this possible (with some added comments to explain the usage of anonymous modules):

def helpers(mod = nil, &block)
  # If a block is given or a argument passed the user is
  # *setting* helpers.
  if block_given? || mod
    # Grab the existing anonymous module for this context
    # or create a new one if there isn't one yet.
    mod ||= settings.peek[:helpers] || Module.new

    # Evaluate the block passed to the `helpers` method
    # in the context of our anonymous module.
    mod.class_eval &block if block_given?

    set(:helpers, mod)

  # If no block or argument is passed the user is
  # *retrieving* helpers.
  else
    # Create a fresh anonymous module that isn't
    # tied to any existing context.
    mod = Module.new
    settings.stack.each do |s|
      # For each context in our stack, include the
      # defined helpers in order.
      mod.send :include, s[:helpers] if s[:helpers]
    end
    mod
  end
end

Hopefully that isn't too dense to make sense, but anonymous modules allow us to create on-the-fly mixins that are included in the endpoint code to give you access to the helpers you've defined.

Go Forth And Be Nameless

These are just a few examples of the usefulness of anonymous classes and modules; they are powerful tools that can give you more flexibility in designing and testing your Ruby code. Do you have a cool use case for anonymous classes and/or modules? Let me know in the comments!

Image Credit: Astrojunta on Wikipedia