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