Skip to content

Instantly share code, notes, and snippets.

@alekpopovic
Created August 12, 2022 12:07
Show Gist options
  • Save alekpopovic/ec28867c59708d481298948cc946ddbd to your computer and use it in GitHub Desktop.
Save alekpopovic/ec28867c59708d481298948cc946ddbd to your computer and use it in GitHub Desktop.

Ruby Metaprogramming techniques

Use the singleton-class

Many ways of manipulating single objects are based on manipulations on the singleton class and having this available will make metaprogramming easier. The classic way to get at the singleton class is to execute something like this:

  sclass = (class << self; self; end)

Kernel#singleton_class with this definition:

module Kernel
  def singleton_class
    class << self; self; end
  end
end

Write DSL's using class-methods that rewrite subclasses

When you want to create a DSL for defining information about classes, the most common trouble is how to represent the information so that other parts of the framework can use them. Take this example where I define an ActiveRecord model object:

 class Product < ActiveRecord::Base
   set_table_name 'produce'
 end

In this case, the interesting call is set_table_name. How does that work? Well, there is a small amount of magic involved. One way to do it would be like this:

 module ActiveRecord
   class Base
     def self.set_table_name name
       define_attr_method :table_name, name
     end

    def self.define_attr_method(name, value)
      singleton_class.send :alias_method, "original_#{name}", name
        singleton_class.class_eval do
          define_method(name) do
            value
          end
        end
      end
    end
 end

What's interesting here is the define_attr_method. In this case we need to get at the singleton-class for the Product class, but we do not want to modify ActiveRecord::Base. By using singleton_class we can achieve this. We have to use send to alias the original method since alias_method is private. Then we just define a new accessor which returns the value. If ActiveRecord wants the table name for a specific class, it can just call the accessor on the class. This way of dynamically creating methods and accessors on the singleton-class is very common, and especially so in Rails.

Create classes and modules dynamically

Ruby allows you to create and modify classes and modules dynamically. You can do almost anything you would like on any class or module that isn't frozen. This is very useful in certain places. The Struct class is probably the best example, where

 PersonVO = Struct.new(:name, :phone, :email)
 p1 = PersonVO.new(:name => "Ola Bini")

will create a new class, assign this to the name PersonVO and then go ahead and create an instance of this class. Creating a new class from scratch and defining a new method on it is as simple as this:

 c = Class.new
 
c.class_eval do

  define_method :foo do
    puts "Hello World"
  end
  
end

c.new.foo    # => "Hello World"

Apart from Struct, examples of creating classes on the fly can be found in SOAP4R and Camping. Camping is especially interesting, since it has methods that creates these classes, and you are supposed to inherit your controllers and views from these classes. Much of the interesting functionality in Camping is actually achieved in this way. From the unabridged version:

 def R(*urls); Class.new(R) { meta_def(:urls) { urls } }; end

This makes it possible for you to create controllers like this:

 class View < R '/view/(\d+)'
 
 def get post_id
 end

end

You can also create modules in this way, and include them in classes dynamically.

Use method_missing to do interesting things

Apart from blocks, method_missing is probably the most powerful feature of Ruby. It's also one that is easy to abuse. Much code can be extremely simplified by good use of method_missing. Some things can be done that aren't even possible without. A good example (also from Camping), is an extension to Hash:

 class Hash
def method_missing(m,*a)
if m.to_s =~ /=$/
  self[$`] = a[0]
elsif a.empty?
  self[m]
else
  raise NoMethodError, "#{m}"
end
end
end

This code makes it possible to use a hash like this:

 x = {'abc' => 123}
x.abc # => 123
x.foo = :baz
x # => {'abc' => 123, 'foo' => :baz}

As you see, if someone calls a method that doesn't exist on hash, it will be searched for in the internal collection. If the method name ends with an =, a value will be set with the key of the method name excluding the equal sign.

Another nice method_missing technique can be found in Markaby. The code I'm referring to makes it possible to emit any XHTML tags possible, with CSS classes added into it. This code:

 body do
h1.header 'Blog'
div.content do
'Hellu'
end
end

will emit this XML:

  <body>
<h1 class="header">Blog</h1>
<div class="content">
Hellu
</div>
</body>

Most of this functionality, especially the CSS class names is created by having a method_missing that sets attributes on self, then returning self again.

Dispatch on method-patterns

This is an easy way to achieve extensibility in ways you can't anticipate. For example, I recently created a small framework for validation. The central Validator class will find all methods in self that begin with check_ and call this method, making it very easy to add new checks: just add a new method to the class, or to one instance.

methods.grep /^check_/ do |m|
self.send m
end

This is really easy, and incredibly powerful. Just look at Test::Unit which uses this method all over the place.

Replacing methods

Sometimes a method implementation just doesn't do what you want. Or maybe it only does half of it. The standard Object Oriented Way (tm) is to subclass and override, and then call super. This only works if you have control over the object instantiation for the class in question. This is often not the case, and then subclassing is worthless. To achieve the same functionality, alias the old method and add a new method-definition that calls the old method. Make sure that the previous methods pre- and postconditions are preserved.

 class String
alias_method :original_reverse, :reverse

def reverse
 puts "reversing, please wait..."
 original_reverse
end
end

Also, a twist on this technique is to temporarily alias a method, then returning it to before. For example, you could do something like this:

 def trace(*mths)
add_tracing(*mths) # aliases the methods named, adding tracing
  yield
remove_tracing(*mths) # removes the tracing aliases
end

This example shows a typical way one could code the add_tracing and remove_tracing methods. It depends on singleton_class being available, as per tip #1:

 class Object
  def add_tracing(*mths)
    mths.each do |m|
      singleton_class.send :alias_method, "traced_#{m}", m
      singleton_class.send :define_method, m do |*args|
        $stderr.puts "before #{m}(#{args.inspect})"
        ret = self.send("traced_#{m}", *args)
        $stderr.puts "after #{m} - #{ret.inspect}"
        ret
      end
    end
  end
 
  def remove_tracing(*mths)
    mths.each do |m|
      singleton_class.send :alias_method, m, "traced_#{m}"
    end
  end
end

"abc".add_tracing :reverse

If these methods were added to Module (with a slightly different implementation; see if you can get it working!), you could also add and remove tracing on classes instead of instances.

Use NilClass to implement the Introduce Null Object refactoring

In Fowlers Refactorings, the refactoring called Introduce Null Object is for situations where an object could either contain an object, or null, and if it's null it will have a predefined value. A typical exampel would be this:

name = x.nil? ? "default name" : x.name

Now, the refactoring is based on Java, which is why it recommends to create a subclass of the object in question, that gets set when it should have been null. For example, a NullPerson object will inherit Person, and override name to always return the "default name" string. But, in Ruby we have open classes, which means you can do this:

 def nil.name; "default name"; end
x # => nil
name = x.name # => "default name"

Learn the different versions of eval

There are several versions of evaluation primitives in Ruby, and it's important to know the difference between them, and when to use which. The available contestants are eval, instance_eval, module_eval and class_eval. First, class_eval is an alias for module_eval. Second, there's some differences between eval and the others. Most important, eval only takes a string to evaluate, while the other can evaluate a block instead. That means that eval should be your absolutely last way to do anything. It has it's uses but mostly you can get away with just evaluating blocks with instance_eval and module_eval.

Eval will evaluate the string in the current environment, or, if a binding is provided in that environment. (See tip #11).

Instance_eval will evaluate the string or the block in the context of the reveiver. Specifically, this means that self will be set to the receiver while evaluating.

Module_eval will evaluate the string or the block in the context of the module it is called on. This sees much use for defining new methods on modules or singleton classes. The main difference between instance_eval and module_eval lies in where the methods defined will be put. If you use String.instance_eval and do a def foo inside, this will be available as String.foo, but if you do the same thing with module_eval you'll get String.new.foo instead.

Module_eval is almost always what you want. Avoid eval like the plague. Follow these simple rules and you'll be OK.

Introspect on instance variables

A trick that Rails uses to make instance variables from the controller available in the view is to introspect on an objects instance variables. This is a grave violation of encapsulation, of course, but can be really handy sometimes. It's easy to do with instance_variables, instance_variable_get and instance_variable_set. To copy all instance_variables from one object to another, you could do it like this:

 from.instance_variables.each do |v|
to.instance_variable_set v, from.instance_variable_get(v)
end

Create Procs from blocks and send them around

Materializing a Proc and saving this in variables and sending it around makes many API's very easy to use. This is one of the ways Markaby uses to manage those CSS class definitions. As the pick-axe details, it's easy to turn a block into a Proc:

 def create_proc(&p); p; end
create_proc do
puts "hello"
end       # => #<Proc ...>

Calling it is as easy:

p.call(*args)

If you want to use the proc for defining methods, you should use lambda to create it, so return and break will behave the way you expect:

 p = lambda { puts "hoho"; return 1 }
define_method(:a, &p)

Remember that method_missing will provide a block if one is given:

 def method_missing(name, *args, &block)
block.call(*args) if block_given?
end

thismethoddoesntexist("abc","cde") do |*args|
p args
end  # => ["abc","cde"]

Use binding to control your evaluations

If you do feel the need to really use eval, you should know that you can control what variables are available when doing this. Use the Kernel-method binding to get the Binding-object at the current point. An example:

get_b; binding; end
foo = 13
eval("puts foo",get_b) # => NameError: undefined local variable or method
This technique is used in ERb and Rails, among others, to set which instance variables are available. As an example:
 class Holder
def get_b; binding; end
end

h = Holder.new
h.instance_variable_set "@foo", 25
eval("@foo",h.get_b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment