[Review] Metaprogramming Ruby
Book | Metaprogramming Ruby 2: Program Like the Ruby Pros |
Author | Paolo Perrotta |
Link | pragprog.com/book/ppmetr2/metaprogramming-ruby-2 |
The Object Model
An object contains its instance variables and a reference to a class.
Instance variable
Instance variables just spring into existence when you assign them a value, so you can have objects of the same class that carry different instance variables.
Instance method
You can get a list of an object’s methods by calling Object#methods
.
When you talk about the class, you call it an instance method, and when you talk about the object, you simply call it a method.
1 2 |
|
An object’s instance variables live in the object itself, and an object’s methods live in the object’s class. That’s why objects of the same class share methods but don’t share instance variables.
Truth about classes
The truth about classes: classes themselves are nothing but objects.
The methods of an object are also the instance methods of its class. In turn, this means that the methods of a class are the instance methods of Class
:
1
|
|
What’s the difference between class
and superclass
?
class
is about the type which an object belongs to.superclass
is about inheritance, between classes.
The superclass
of Class is Module—which is to say, every class is also a module. To be precise, a class is a module with three additional instance methods (new, allocate, and superclass) that allow you to create objects or arrange classes into hierarchies.
Which to pick between Class and Module?
Usually, you pick a module when you mean it to be included somewhere, and you pick a class when you mean it to be instantiated or inherited.
What’s an Object, and what’s a Class?
-
What’s an object? It’s a bunch of instance variables, plus a link to a class. The object’s methods don’t live in the object—they live in the object’s class, where they’re called the instance methods of the class.
-
What’s a class? It’s an object (an instance of Class), plus a list of instance methods and a link to a superclass. Class is a subclass of Module, so a class is also a module.
What’s the difference between load
and require
?
You use load to execute code, and you use require to import libraries.
- use
require
, no need to appends ‘.rb’. require
loads only once.
As load
executes codes, how does load
avoid conlicts?
load('motd.rb', true)
If you load a file this way, Ruby creates an anonymous module, uses that module as a Namespace to contain all the constants from motd.rb, and then destroys the module.
Constant
classes are nothing but objects, class names are nothing but constants.
How is a constant really different from a variable?
The one important difference has to do with their scope.
Constants are arranged in a tree similar to a file system, where the names of modules and classes play the part of directories and regular constants play the part of files.
Method lookup
What does Ruby do, when you call a method?
- It finds the method. This is a process called method lookup.
- It executes the method. To do that, Ruby needs something called
self
.
How does Ruby lookup methods?
“one step to the right, then up” rule: go one step to the right into the receiver’s class, and then go up the ancestors chain until you find the method.
What does ancestor chain look like, when prepend
or include
multiple modules?
The prepend
method. It works like include
, but it inserts the module below the including class.
1 2 3 4 5 6 7 8 9 10 11 |
|
What does Ruby do, when prepend
or include
a module multiple times?
If that module is already in the chain, Ruby silently ignores the second inclusion. As a result, a module can only appear once in the same chain of ancestors.
Basic ancestor chain
1 2 |
|
What private
really means?
private
methods come from two rules working together:
- you need an explicit receiver to call a method on an object that is not yourself.
private
methods can be called only with an implicitself
.
Put these two rules together, and you’ll see that you can only call a private
method on yourself. You can call this the “private rule.“
What’s the env when you start the irb
?
As soon as you start a Ruby program, you’re sitting within an object named main
that the Ruby interpreter created for you.
Refinement
Refinements are similar to Monkeypatches, but they’re not global. A Refinement is only active in two places:
- The
refine
block itself. - The code starting from the place where you call
using
until the end of the module definition (if you’re in a module definition) or the end of the file (if you’re at the top level).
Which has the precedence, Refinement or Method lookup?
Refinements are like pieces of code patched right over a class, and they override normal method lookup. On the other hand, a Refinement works in a limited area of the program: the lines of code between the call to using
and the end of the file, or the end of the module definition.
Code in an active Refinement takes precedence over code in the refined class, and also over code in modules that are included or prepended by the class. Refining a class is like slapping a patch right onto the original code of the class.
A trivia example about Refinement:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Even if you call another_method
after the using
, the call to my_method
itself happens before the using
—so it calls the original, unrefined version of the method.
A help reference, Refinements in Ruby by The timeless repository.
Methods
Dynamic Dispatch
Why would you use send
instead of the plain old dot notation? Because with send
, the name of the method that you want to call becomes just a regular argument. You can wait literally until the very last moment to decide which method to call, while the code is running. This technique is called Dynamic Dispatch.
An example of Dynamic Dispatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
What’s the concern about send
?
You can call any method with send
, including private methods.
You can use public_send
instead. It’s like send, but it makes a point of respecting the receiver’s privacy.
Dynamic Method
There is one important reason to use Module#define_method
(private) over the more familiar def keyword: define_method
allows you to decide the name of the defined method at runtime.
Ghost Method
BasicObject#method_missing
(private)
Ghost Methods are usually icing on the cake, but some objects actually rely almost exclusively on them. They collect method calls through method_missing
and forward them to the wrapped object.
How can respond_to?
missing methods?
refernced from Method_missing, Politely by Marc Andre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
You can specialize respond_to?
, but it doesnot make a missing method behaves exactly like a method.
1 2 3 |
|
Ruby 1.9.2 introduces respond_to_missing?
that provides for a clean solution to the problem. Instead of specializing respond_to?
one specializes respond_to_missing?
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
What about the constant missing?
Module#const_missing
(public)
What’s the concern about method_missing
?
This is a common problem with Ghost Methods: since unknown calls become calls to method_missing
, your object might accept a call that’s just plain wrong. Finding a bug like this one in a large program can be pretty painful.
To avoid this kind of trouble, take care not to introduce too many Ghost Methods.
Ghost Methods can be dangerous. You can avoid most of their problems by following a few basic recommendations (always call super
, always redefine respond_to_missing?
)
And you may call some methods Object
or some others classes in ancestor chain defined.
How to solve it? Blank Slate!
Remove methods from an object to turn them into Ghost Methods.
- Inheriting from
BasicObject
is the quicker way to define a Blank Slate in Ruby.
1 2 |
|
-
Inheriting from
Object
by default, and remove method inherited.Don’t hide
instance_eval
or any method beginning with__
. One example of a reserved method isBasicObject#__send__
, that behaves the same as send, but gives you a scary warning when you try to remove it.
1 2 3 4 5 |
|
- Or you can write a BlankSlate class to inherit.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
What’s the difference between undef_method
and remove_method
?
- The drastic
undef_method
removes any method, including the inherited ones. - The kinder
remove_method
removes the method from the receiver, but it leaves inherited methods alone.
What’s the boiling down facts between Ghost Method and really methods?
Ghost Method are just a way to intercept method calls. Because of this fact, they behave different than actual methods.
What’s the choice between define_method
and method_missing
?
There are times when Ghost Methods are your only viable option. This usually happens
- When you have a large number of method calls.
- When you don’t know what method calls you might need at runtime.
Use Dynamic Methods if you can, and Ghost Methods if you have to.
Callables
the Callables
Package code, and call it later
- block, evaluated in the scope which they’re defined.
- proc, which is basically a block turned object, and evaluated in the scope which they’re defined.
- lambda, which is a slight variation on a proc.
- method, bound to an object, which are evaluated in that object’s scope. They can also be unbound from their scope and rebound to another object or class.
block is not an object, while proc and lambda are Proc objects, and method is in question.
Blocks
The main point about blocks is that they are all inclusive and come ready to run. They contain both the code and a set of bindings.
When you define the block, it simply grabs the bindings that are there at that moment, and then it carries those bindings along when you pass the block into a method.
1 2 3 4 5 6 7 |
|
What if I define additional bindings inside a block?
They disappear after the block ends.
What’s the meaning of “a block is a closure”?
a block is a closure, this means a block captures the local bindings and carries them along with it.
scope
You can see bindings all over the scope.
class
, module
, and def
, respectively. Each of these keywords acts like a Scope Gate.
A subtle difference between class
, module
and def
The code in a class
or module
definition is executed immediately. Conversely, the code in a method definition is executed later, when you eventually call the method.
Whenever the program changes scope, some bindings are replaced by a new set of bindings. RIGHT?
For local_varialbes
, that’s right, but instance_variables
, class_variables
and global_variables
can go through the Scope Gate.
What’s the difference between Global Variables and Top-Level Instance Variables?
when it comes to global variables, use them sparingly, if ever.
You can access a top-level instance variable whenever main
takes the role of self. When any other object is self
, the top-level instance variable is out of scope.
How to cross the Scope Gate, and Why?
You can use these techniques:
- Use Flat Scope (
Class.new
,Module.new
, anddefine_method
) - Use Shared Scope
- Use Context Probe (
BasicObject#instance_eval
)
to mix code and bindings at will.
What is Flat Scope?
“flattening the scope,” meaning that the two scopes share variables as if the scopes were squeezed together. For short, you can call this spell a Flat Scope.
Use Class.new
, Module.new
and define_method
.
What is Shared Scope?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
If you define multiple methods in the same Flat Scope, maybe protected by a Scope Gate, all those methods can share bindings. That’s called a Shared Scope
What is Context Probe?
instance_eval
has a slightly more flexible twin brother named instance_exec
, that allows you to pass arguments to the block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
However, instance variables depend on self
, so when instance_eval
switches self
to the receiver, all the instance variables in the caller fall out of scope.
Clean Room
Blank Slates are good candidates for Clean Room.
The ideal Clean Room doesn’t have many methods or instance variables, because the names of those methods and instance variables could clash with the names in the environment that the block comes from. For this reason, instances of BasicObject
usually make for good Clean Rooms, because they’re Blank Slates.
You might think of using a BasicObject
instead of an Object for your Clean Room. However, remember that BasicObject
is also a Blank Slate, and as such it lacks some common methods such as puts.
What’s the different use for Shared Scope and Clean Room?
Shared Scope is used in definition, to make variables(bindings) shared between methods, while Clean Room is used to run code, to help reduce the modifications on shared variables(like instance variables).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
When is yield
not enough to use?
- You want to pass the block to another method (or even another block).
- You want to convert the block to a
Proc
.
Proc Objects
as blocks are not objects, Ruby provides the standard library class Proc
. A Proc
is a block that has been turned into an object. You can create a Proc
by passing the block to Proc.new
. Later, you can evaluate the block-turned-object with Proc#call
.
Deferred Evaluation
1 2 3 |
|
4 ways to create Procs explicitly
1 2 3 4 |
|
Ways to create Procs implicitly
use &
to convert block into a proc:
1 2 3 4 5 |
|
4 ways to call Procs
1 2 3 4 |
|
Use &
to convert a block to Proc
1 2 3 4 5 6 7 |
|
Use &
to convert a Proc to block
1 2 3 4 5 6 |
|
What’s a lambda?
Procs created with lambda
are called lambdas, while the others are simply called procs. (You can use the Proc#lambda?
method to check whether the Proc
is a lambda).
Procs vs. Lambdas
-
return
lambda
returns just returns from the lambda, while a proc returns from the scope where the proc itself was defined. -
arity
Call a
lambda
with the wrong arity, and it fails with anArgumentError
, while if there are too many arguments, a proc drops the excess arguments. If there are too few arguments, it assignsnil
to the missing arguments.
Generally speaking, lambdas
are more intuitive than procs because they’re more similar to methods. They’re pretty strict about arity, and they simply exit when you call return
.
About the tolerance on arguments
method == lambda < proc == block
Methods
By calling Kernel#method
, you get the method itself as a Method object, which you can later execute with Method#call
. In Ruby 2.1, you also have Kernel#singleton_method
, that converts the name of a Singleton Method to a Method object.
Conversions between methods and procs
- Use
Method#to_proc
to convert a method into proc. - Use
define_method
to convert a proc into method.
An important difference between methods and procs
a lambda
is evaluated in the scope it’s defined in (it’s a closure, remember?), while a Method
is evaluated in the scope of its object.
Unbound Methods
UnboundMethods
are like Methods
that have been detached from their original class or module.
You can’t call an UnboundMethod
, but you can use it to generate a normal method that you can call.
generate
- use
Method#unbind
1 2 3 |
|
- use
Module#instance_method
1 2 3 4 5 6 7 8 |
|
Note: instance_methods
is totally different, it’s like methods
, just return an array of symbols.
usage
- bind the UnboundMethod to an object with
UnboundMethod#bind
. UnboundMethods that come from a class can only be bound to objects of the same class (or a subclass), while UnboundMethods that come from a module have no such limitation from Ruby 2.0 onwards. - use an UnboundMethod to define a brand new method by passing it to
Module#define_method
.
1 2 3 4 5 |
|
example
In ActiveSupport, the ‘autoloading’ system includes a Loadable
module, which redefines the standard Kernel#load
. If a class includes Loadable
, then Loadable#load
gets lower then Kernel#load
on its chain of ancestors.
And what if you want to stop using Loadable#load
and go back to the plain vanilla Kernel#load
?
1 2 3 4 5 |
|
Class Definitions
In Java and C#, defining a class is like making a deal between you and the compiler.
In Ruby, class definitions are different. When you use the class keyword, you aren’t just dictating how objects will behave in the future. On the contrary, you’re actually running code.
The Current Class
Wherever you are in a Ruby program, you always have a current object: self
. Likewise, you always have a current class (or module). As The Ruby interpreter always keeps a reference to the current class (or module), when you define a method, that method becomes an instance method of the current class.
Although you can get a reference to the current object through self
, there’s no equivalent keyword to get a reference to the current class.
How to keep track of the current class?
- At the top level of your program, the current class is
Object
, the class ofmain
. (That’s why, if you define a method at the top level, that method becomes an instance method ofObject
).
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 |
|
-
In a method, the current class is the class of the current object.
-
When you open a class with the
class
keyword (or a module with themodule
keyword), that class becomes the current class.
How can you open the class if you don’t know its name?
Use Module#class_eval
.
What’s the advantage of Module#class_eval
other than class
?
Module#class_eval
is actually more flexible than class. You can useclass_eval
on any variable that references the class, whileclass
requires a constant.class
opens a new scope, losing sight of the current bindings, whileclass_eval
has a Flat Scope.
When to use class_eval
, and when instance_eval
?
Use instance_eval
to open an object that is not a class, and class_eval
to open a class definition and define methods with def
.
What’s the difference between class_eval
and instance_eval
?
Both changes self
, and class_eval
changes the current class to the caller’s class, while instance_eval
changes the current class to the caller’s singleton class.
An interesting knowledge about Class.new
1 2 3 4 5 6 7 8 |
|
When you assign an anonymous class to a constant, Ruby understands that you’re trying to give a name to the class, and it does something special: it turns around to the class and says, “Here’s your new name.” Now the constant references the Class, and the Class also references the constant. If it weren’t for this trick, a class wouldn’t be able to know its own name
Class Instance Variables
They’re just regular instance variables that happen to belong to an object of class Class
. Because of that, a Class Instance Variable can be accessed only by the class itself— not by an instance or by a subclass.
No weird behaviors like Class Variables, which subclass and class share the same variable.
Singleton Methods
1 2 |
|
A method which is specific to a single object, is called Singleton Methods.
You can also use Object#define_singleton_method
: (Remeber Module#define_method
?)
1
|
|
What is the Class Macro?
A method such as attr_accessor
is called a Class Macro. Class Macros look like keywords, but they’re just regular class methods that are meant to be used in a class definition.
Singleton Class
A singleton class is where an object’s Singleton Methods live
How to get?
- Use the
<<
syntax.
1 2 |
|
1 2 3 4 5 6 7 8 9 10 |
|
- Use
Object#singleton_class
1
|
|
Singleton classes and inheritance
7 rules of the Ruby object model
The Ruby object model is a beautiful place,” Bill notes, with a dreamy expres- sion on his face. “There are classes, singleton classes, and modules. There are instance methods, class methods, and singleton methods.
- There is only one kind of object—be it a regular object or a module.
- There is only one kind of module—be it a regular module, a class, or a singleton class.
- There is only one kind of method, and it lives in a module—most often in a class.
- Every object, classes included, has its own “real class,” be it a regular class or a singleton class.
- Every class, with the exception of
BasicObject
, has exactly one ancestor— either a superclass, or a module. This means you have a single chain of ancestors from any class up toBasicObject
. - The superclass of the singleton class of an object is the object’s class. The superclass of the singleton class of a class is the singleton class of the class’s superclass.
- When you call a method, Ruby goes “right” in the receiver’s real class and then “up” the ancestors chain. That’s all there is to know about the way Ruby finds methods.
Can object’s Singleton Class touch the its Instance Variable?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Yes, it can. So I can assume the obj’s Instance Variable lives in its Singleton Class.
Can a module’s singleton methods be included?
No!
When a class includes a module, it gets the module’s instance methods—not the class methods. Class methods stay out of reach, in the module’s singleton class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
What’s the difference between Module#include
and Object#extend
?
Module#include
includes a module in the receiver’s class.Object#extend
includes a module in the receiver’s singleton class.
Method Wrapper
- Around Alias
- Refinement Wrapper
- Prepended Wrapper(
Module#prepend
)
Selection: Prepended Wrapper > Refinement Wrapper > Around Alias
Around Alias
alias
vs. alias_method
1 2 3 4 |
|
alias
is a keyword.alias_method
is an instance method of Module.Module#alias_method
Can you alias a method before it defined?
No!
1 2 3 4 5 6 |
|
Can aliases break the class’s encapsulation?
Yes!
1 2 3 4 5 6 7 8 9 |
|
How to write an Around Alias?
- You alias a method.
- You redefine it.
- You call the old method from the new method.
Downsides
- They pollute your classes with one additional method name. You can fix this small problem somehow by making the old version of the method
private
after you alias it. - The loading issue. You should never load an Around Alias twice, unless you want to end up with an exception when you call the method.
The main issue with Around Aliases, however, is that they are a form of Monkeypatching. Like all Monkeypatches, they can break existing code that wasn’t expecting the method to change.
Refinement Wrapper
Advantage over Around Alias
If you call super
from a refined method, you will call the original, unrefined method.
1 2 3 4 5 6 7 8 9 10 |
|
Prepended Wrapper
A method in a prepended module can override a method in the includer, and call the non- overridden version with super
.
1 2 3 4 5 6 7 8 9 10 11 |
|
Advantage over Refinement Wrapper and Around Alias?
It’s generally considered cleaner and more explicit than both a Refinement Wrapper and an Around Alias.
An interesting quiz solving by Around Alias
Make it work:
1
|
|
You can solve this quiz with an Open Class. Just reopen Fixnum
, and redefine +
so that (x+y)
becomes (x+y+1)
. This is not as easy as it seems, however. The new version of + relies on the old version of +, so you need to wrap your old version with the new version.
1 2 3 4 5 6 7 |
|
Eval and Binding
Kernel#eval
Evaluates the Ruby expression(s) in string. If binding is given, which must be a Binding
object, the evaluation is performed in its context.
1 2 3 4 5 6 7 |
|
Kernel#binding
Returns a Binding
object, describing the variable and method bindings at the point of call. This object can be used when calling eval to execute the evaluated command in this environment.
Ruby also provides a predefined constant named TOPLEVEL_BINDING
, which is just a Binding of the top-level scope.
You can also use Proc#binding
to return a Binding
object.
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 |
|
What’s to concern when using eval
?
-
As it only accepts strings of codes but not blocks, it’s not editor friendly(syntax highlighting) and hard to trace syntax errors.
-
Code Injection.
What’s the soluction Ruby provided for eval
insecurity?
-
Tainted Objects,
Object#tainted?
,Object#untaint
-
Safe Levels,
$SAFE
- 0, “hippie commune”, where you can hug trees and format hard disks.
- Any safe level greater than 0 also causes Ruby to flat-out refuse to evaluate tainted string.
- 2, disallows most file-related operations.
- 3, “military dictatorship,” where every object you create is tainted by default.
How to write a Sandbox for eval
?
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Updated 2014-09-28
As I was looking API and fiddling around, I found 3 new methods only supported by Ruby 2.1.
1 2 3 4 5 6 7 8 9 10 11 12 |
|