Ruby's instance_eval

InstanceEval is what you use to create a DSL. It's the engine behind RSpec, Sinatra and Rails, and everything simple you take for granted. It's nothing like class_eval. InstanceEval is what you use to make things simple.

Defining Dynamic Class and Instance Methods in Ruby

Lately I've been having fun figuring out how to minimize the size of some classes I'm working on for an authentication gem. It feels almost like art. I start with what I want it to look like and see if I can make it like that. It's also gotta actually be useful though.

Here's the example scenario. You want to write code that converts all Oauth API responses into a unified interface. There's Facebook, Github, Twitter, LinkedIn, MySpace, Gowalla, Foursquare, Freshbooks, Google, Yahoo, and 100 others. You know they're probably going to change their api's soon so if they do you want it to be a mindless change in your code base.

So the goal is to have it so that the code in each service class is entirely for that service, nothing else. When I mean nothing else, I mean minimizing even def method_name calls, which, if you have 100 classes each with 3 methods, will add the word def 300 times to your code base.

So how do you get rid of def? Executable class bodies. Executable class bodies are the engine behind Rails' associations like has_many and belongs_to, RSpec's describe, Rake's task, and Sinatra's get, which are all class methods.

So with this Oauth example, we want to turn a class like this:

class Twitter < Oauth
def key
access_token.params[:user_id]
end
def settings
{:site => "http://api.twitter.com"}
end
end

into something like this:

class Twitter < Oauth
key :user_id
settings "http://api.twitter.com"
end

Which one do you prefer?

We can do this by defining those two methods key and settings in the superclass, using a ton of Ruby meta programming.

Dynamic instance methods in Ruby

Dynamic instance methods in ruby are easy to use. There are 3 main ways to do this and I'm sure a hundred other less common ways to do it:

  • define_method
  • class_eval
  • method_missing

define_method

Use define_method to dynamically define a method on a class in one of two key ways: inside of a method, or in the class body.

class DynamicMethods
# method version
def add_methods(*names, &block)
names.each do |name|
self.class.send(:define_method, name) do
puts block.call(name)
end
end
end

# class version
[:x, :y, :z].each do |name|
define_method name do
puts "called #{name}"
end
end
end

foo = DynamicMethods.new
foo.add_methods(:a, :b, :c) do |name|
"called #{name} from block"
end

foo.a
foo.b
foo.c
foo.x
foo.y
foo.x

You can also pass args and blocks to define_method:

define_method name do |*args|
# *args for any number of attributes
end

And you can use define_method to execute blocks:

class DynamicMethods
def add_methods(*names, &block)
names.each do |name|
self.class.send(:define_method, name) do
puts block.call(name)
end
end
end
end

foo = DynamicMethods.new
foo.add_methods(:a, :b, :c) do |name|
"#{name} was called"
end
foo.a
foo.b
foo.c

class_eval

Use class_eval to reopen a class and add whatever you want to it from anyplace in your application:

ActiveRecord::Base.class_eval do
def me
self
end
end

method_missing

Use method_missing to catch calls for methods that don't exist. You can do this to match regular expressions to make fun things:

class DynamicMethods
def services
%w(facebook twitter)
end

def profile_for(service)
# profiles.each...
end

# 'facebook' and 'facebook?', for each service
def method_missing(method, *args, &block)
if method.to_a =~ /(#{services.join('|')})\?/
return profile_for($1)
end
super(method, *args, &block)
end
end

Dynamic class methods in Ruby

Everything described above only works for dynamic instance methods. So how do you define dynamic class methods? You must define dynamically interpolated strings.

class DynamicClassMethods
def add_class_methods(*names, &block)
names.each do |name|
self.class.class_eval <<-EOF
def self.#{name}
"#{block.call(name)}"
end
EOF
end
end
end

foo = DynamicClassMethods.new
foo.add_class_methods(:a, :b, :c) do |name|
"called class method '#{name}' from a block"
end

puts DynamicClassMethods.a #=> called class method 'a' from a block
puts DynamicClassMethods.b #=> called class method 'b' from a block
puts DynamicClassMethods.c #=> called class method 'c' from a block

Convoluted? Yes. Would you ever use that? I don't know, would you? I actually have a use case for that now. People always get worried like this kind of a thing is a bad thing because somebody might mess with the program? I don't understand. I think that's perfectly okay if you know what you're doing.

instance_eval

Use instance_eval to change the scope in which your method executes. Whaaaaa? That's so cool. That means I can create a method in one place, and execute it in a totally different one. Meaning I can create class methods and execute them as instance methods! Nice.

Here's an example. First, create a base class with a class and instance method called key, which we'll have take either a Proc or a non-Proc. The class method will be our DSL for defining key, but we want the key in the instance.

class Oauth
def self.key(value = nil, &block)
if block_given?
@key = block
elsif value
@key = value
end

@key
end

def key
unless @key
if self.class.key.is_a?(Proc)
@key = instance_eval(&self.class.key)
else
@key = self.class.key
end
end

@key
end
end

class Twitter < Oauth
key :user_id
end

puts Twitter.key.inspect #=> :user_id
puts Twitter.new.key.inspect #=> :user_id

class Facebook < Oauth
key do
hash
end

def hash
{:id => "123"}
end
end

puts Facebook.key.inspect #=> #<Proc:[email protected]:33>
puts Facebook.new.key.inspect #=> {:id=>"123"}

Notice the Facebook example. When you call Facebook.new.key, it's calling a block that was defined with a class-level scope, but it's returning the instance's value {:id=>"123"}. That is my favorite thing about Ruby I think. It makes me so happy.

With that, I can define blocks at the class level, and retrieve the value from at the instance level, in the instance's scope! This means that this:

# a
def key
# logic
end

becomes

# b
key do
# logic
end

Both work exactly the same.

Then what's the benefit? The benefit is that you can abstract out a lot of the common logic that you'd have to write in a so that you only have to code just the bare minimum. The executable-class-body version cuts out the need for calling super too. Imagine I had a superclass that processed the key like this:

class Superclass
def key(value)
raise "key can't be nil" if value.nil?
end
end

... and then I have a subclass that needs that base functionality but adds to it:

class Subclass < Superclass
def key(value)
value = {:id => value} if !value.is_a?(Hash)
super(value)
end
end

This is a simple case, but imagine if we applied it to this 100-class Oauth problem and the logic was 10x more complicated. There'd be lots of overrides and it just wouldn't be that fun to read.

How about this instead for the subclass:

class Subclass < Superclass
key do |value|
value = {:id => value} if !value.is_a?(Hash)
end
end

That reads a lot better to me. How about you?

Dynamic Class-Instance Methods - The Example

So now that everything's dynamic, we can get to making that Twitter < Oauth class. Let's make something so we can dynamically define those class/instance methods from above, so we can do this:

class Oauth
accessible :key, :data, :settings, :profile
end

Here is that code:

module Accessible
attr_accessor :accessibles

def accessible(*methods, &block)
self.accessibles ||= {}
methods.each do |name|
self.accessibles[name.to_sym] = block
class_eval <<-EOF
def self.#{name}(*args, &block)
return @#{name} unless block_given? || !args.empty?

# i don't like this but I haven't figured out
# how to do it another way yet.
unless self.accessibles
i = 1
while self.accessibles.nil? && i < self.ancestors.length
break unless self.ancestors[1].respond_to?(:accessibles)
self.accessibles = self.ancestors[i].accessibles
i = i + 1
end
end

if parent_block = self.accessibles[:#{name.to_sym}]
args = parent_block.call(*args)
end

@#{name} = args.empty? ? block : args
end

def #{name}
unless @#{name}
if self.class.#{name}.is_a?(Proc)
@#{name} = instance_eval(&self.class.#{name})
else
@#{name} = self.class.#{name}
end
end

@#{name}
end
EOF
end

self.accessibles
end
end

So full of dynamic class-level string interpolations, mmmmm. Now you can extend a class with that module, and get that functionality:

class Oauth
extend Accessible

accessible :settings do |key, hash|
hash ||= {}
hash[:site] = key
hash
end

accessible :key, :data, :profile
end

class Twitter < Oauth
key :user_id
settings "https://api.twitter.com"
data do |access_token|
JSON.parse(access_token.get('/1/account/verify_credentials.json').body)
end
profile do |data|
{
'nickname' => data['screen_name'],
'name' => data['name'],
'location' => data['location'],
'image' => data['profile_image_url'],
'screen_name' => data['screen_name'],
'description' => data['description'],
'urls' => data['url']
}
end
end

t = Twitter.new
t.settings #=> {:site => "https://api.twitter.com"}

How about that. We would then extend those executable class methods in the superclass to make it work like it looks. I think that's just great. I like you ruby.

What are your thoughts?

This looks interesting:

http://stackoverflow.com/questions/752717/how-do-i-use-define-method-to-create-class-methods