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:
into something like this:
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_methodclass_evalmethod_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.
You can also pass args and blocks to define_method:
And you can use define_method to execute blocks:
class_eval
Use class_eval to reopen a class and add whatever you want to it from anyplace in your application:
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:
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.
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 |
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:
becomes
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:
... and then I have a subclass that needs that base functionality but adds to it:
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:
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:
Here is that code:
So full of dynamic class-level string interpolations, mmmmm. Now you can extend a class with that module, and get that functionality:
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