Metaprogramming Ruby 2: Program Like the Ruby Pros (2014)
Part 3. Appendixes
Appendix 3. Spell Book
Whenever someone says they have “a cool trick,” take them outside and slap them up.
Jim Weirich (1956--2014)
This appendix is a “spell book”—a quick reference to all the “spells” in the book, in alphabetical order. Most of these spells are metaprogramming related (but the ones from Appendix 1, Common Idioms, are arguably not that “meta”). Each spell comes with a short example and a reference to the page where it’s introduced. Go to the associated pages for extended examples and the reasoning behind each spell.
The Spells
Around Alias
Call the previous, aliased version of a method from a redefined method.
|
class String |
|
alias_method :old_reverse, :reverse |
|
|
|
def reverse |
|
"x#{old_reverse}x" |
|
end |
|
end |
|
|
|
"abc".reverse # => "xcbax" |
For more information, see page Around Alias.
Blank Slate
Remove methods from an object to turn them into Ghost Methods (Ghost Method).
|
class C |
|
def method_missing(name, *args) |
|
"a Ghost Method" |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.to_s # => "#<C:0x007fbb2a10d2f8>" |
|
|
|
class D < BasicObject |
|
def method_missing(name, *args) |
|
"a Ghost Method" |
|
end |
|
end |
|
|
|
blank_slate = D.new |
|
blank_slate.to_s # => "a Ghost Method" |
For more information, see page Blank Slate.
Class Extension
Define class methods by mixing a module into a class’s singleton class (a special case of Object Extension (Object Extension)).
|
class C; end |
|
|
|
module M |
|
def my_method |
|
'a class method' |
|
end |
|
end |
|
|
|
class << C |
|
include M |
|
end |
|
|
|
C.my_method # => "a class method" |
For more information, see page Class Extension.
Class Instance Variable
Store class-level state in an instance variable of the Class object.
|
class C |
|
@my_class_instance_variable = "some value" |
|
|
|
def self.class_attribute |
|
@my_class_instance_variable |
|
end |
|
end |
|
|
|
C.class_attribute # => "some value" |
For more information, see page Class Instance Variable.
Class Macro
Use a class method in a class definition.
|
class C; end |
|
|
|
class << C |
|
def my_macro(arg) |
|
"my_macro(#{arg}) called" |
|
end |
|
end |
|
|
|
class C |
|
my_macro :x # => "my_macro(x) called" |
|
end |
For more information, see page Class Macro.
Clean Room
Use an object as an environment in which to evaluate a block.
|
class CleanRoom |
|
def a_useful_method(x); x * 2; end |
|
end |
|
|
|
CleanRoom.new.instance_eval { a_useful_method(3) } # => 6 |
For more information, see page Clean Room.
Code Processor
Process Strings of Code (String of Code) from an external source.
|
File.readlines("a_file_containing_lines_of_ruby.txt").each do |line| |
|
puts "#{line.chomp} ==> #{eval(line)}" |
|
end |
|
|
|
# >> 1 + 1 ==> 2 |
|
# >> 3 * 2 ==> 6 |
|
# >> Math.log10(100) ==> 2.0 |
For more information, see page Code Processor.
Context Probe
Execute a block to access information in an object’s context.
|
class C |
|
def initialize |
|
@x = "a private instance variable" |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.instance_eval { @x } # => "a private instance variable" |
For more information, see page Context Probe.
Deferred Evaluation
Store a piece of code and its context in a proc or lambda for evaluation later.
|
class C |
|
def store(&block) |
|
@my_code_capsule = block |
|
end |
|
|
|
def execute |
|
@my_code_capsule.call |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.store { $X = 1 } |
|
$X = 0 |
|
|
|
obj.execute |
|
$X # => 1 |
For more information, see page Deferred Evaluation.
Dynamic Dispatch
Decide which method to call at runtime.
|
method_to_call = :reverse |
|
obj = "abc" |
|
|
|
obj.send(method_to_call) # => "cba" |
For more information, see page Dynamic Dispatch.
Dynamic Method
Decide how to define a method at runtime.
|
class C |
|
end |
|
|
|
C.class_eval do |
|
define_method :my_method do |
|
"a dynamic method" |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.my_method # => "a dynamic method" |
For more information, see page Dynamic Method.
Dynamic Proxy
Dynamically forward method calls to another object.
|
class MyDynamicProxy |
|
def initialize(target) |
|
@target = target |
|
end |
|
|
|
def method_missing(name, *args, &block) |
|
"result: #{@target.send(name, *args, &block)}" |
|
end |
|
end |
|
|
|
obj = MyDynamicProxy.new("a string") |
|
obj.reverse # => "result: gnirts a" |
For more information, see page Dynamic Proxy.
Flat Scope
Use a closure to share variables between two scopes.
|
class C |
|
def an_attribute |
|
@attr |
|
end |
|
end |
|
|
|
obj = C.new |
|
a_variable = 100 |
|
|
|
|
|
|
|
# flat scope: |
|
obj.instance_eval do |
|
@attr = a_variable |
|
end |
|
|
|
obj.an_attribute # => 100 |
For more information, see page Flat Scope.
Ghost Method
Respond to a message that doesn’t have an associated method.
|
class C |
|
def method_missing(name, *args) |
|
name.to_s.reverse |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.my_ghost_method # => "dohtem_tsohg_ym" |
For more information, see page Ghost Method.
Hook Method
Override a method to intercept object model events.
|
$INHERITORS = [] |
|
class C |
|
def self.inherited(subclass) |
|
$INHERITORS << subclass |
|
end |
|
end |
|
|
|
class D < C |
|
end |
|
|
|
class E < C |
|
end |
|
|
|
class F < E |
|
end |
|
|
|
$INHERITORS # => [D, E, F] |
For more information, see page Hook Method.
Kernel Method
Define a method in module Kernel to make the method available to all objects.
|
module Kernel |
|
def a_method |
|
"a kernel method" |
|
end |
|
end |
|
|
|
a_method # => "a kernel method" |
For more information, see page Kernel Method.
Lazy Instance Variable
Wait until the first access to initialize an instance variable.
|
class C |
|
def attribute |
|
@attribute = @attribute || "some value" |
|
end |
|
end |
|
|
|
obj = C.new |
|
obj.attribute # => "some value" |
For more information, see page Lazy Instance Variable.
Mimic Method
Disguise a method as another language construct.
|
def BaseClass(name) |
|
name == "string" ? String : Object |
|
end |
|
|
|
class C < BaseClass "string" # a method that looks like a class |
|
attr_accessor :an_attribute # a method that looks like a keyword |
|
end |
|
|
|
obj = C.new |
|
obj.an_attribute = 1 # a method that looks like an attribute |
For more information, see page Mimic Method.
Monkeypatch
Change the features of an existing class.
|
"abc".reverse # => "cba" |
|
|
|
class String |
|
def reverse |
|
"override" |
|
end |
|
end |
|
|
|
"abc".reverse # => "override" |
For more information, see page Monkeypatch.
Namespace
Define constants within a module to avoid name clashes.
|
module MyNamespace |
|
class Array |
|
def to_s |
|
"my class" |
|
end |
|
end |
|
end |
|
|
|
Array.new # => [] |
|
MyNamespace::Array.new # => my class |
For more information, see page Namespace.
Nil Guard
Override a reference to nil with an “or.”
|
x = nil |
|
y = x || "a value" # => "a value" |
For more information, see page Nil Guard.
Object Extension
Define Singleton Methods by mixing a module into an object’s singleton class.
|
obj = Object.new |
|
|
|
module M |
|
def my_method |
|
'a singleton method' |
|
end |
|
end |
|
|
|
class << obj |
|
include M |
|
end |
|
|
|
obj.my_method # => "a singleton method" |
For more information, see page Object Extension.
Open Class
Modify an existing class.
|
class String |
|
def my_string_method |
|
"my method" |
|
end |
|
end |
|
|
|
"abc".my_string_method # => "my method" |
For more information, see page Open Class.
Prepended Wrapper
Call a method from its prepended override.
|
module M |
|
def reverse |
|
"x#{super}x" |
|
end |
|
end |
|
|
|
String.class_eval do |
|
prepend M |
|
end |
|
|
|
"abc".reverse # => "xcbax" |
For more information, see page Prepended Wrapper.
Refinement
Patch a class until the end of the file, or until the end of the including module.
|
module MyRefinement |
|
refine String do |
|
def reverse |
|
"my reverse" |
|
end |
|
end |
|
end |
|
|
|
"abc".reverse # => "cba" |
|
using MyRefinement |
|
"abc".reverse # => "my reverse" |
For more information, see page Refinement.
Refinement Wrapper
Call an unrefined method from its refinement.
|
module StringRefinement |
|
refine String do |
|
def reverse |
|
"x#{super}x" |
|
end |
|
end |
|
end |
|
|
|
using StringRefinement |
|
"abc".reverse # => "xcbax" |
For more information, see page Refinement Wrapper.
Sandbox
Execute untrusted code in a safe environment.
|
def sandbox(&code) |
|
proc { |
|
$SAFE = 2 |
|
yield |
|
}.call |
|
end |
|
begin |
|
sandbox { File.delete 'a_file' } |
|
rescue Exception => ex |
|
ex # => #<SecurityError: Insecure operation at level 2> |
|
end |
For more information, see page Sandbox.
Scope Gate
Isolate a scope with the class, module, or def keyword.
|
a = 1 |
|
defined? a # => "local-variable" |
|
|
|
module MyModule |
|
b = 1 |
|
defined? a # => nil |
|
defined? b # => "local-variable" |
|
end |
|
|
|
defined? a # => "local-variable" |
|
defined? b # => nil |
For more information, see page Scope Gate.
Self Yield
Pass self to the current block.
|
class Person |
|
attr_accessor :name, :surname |
|
|
|
def initialize |
|
yield self |
|
end |
|
end |
|
|
|
joe = Person.new do |p| |
|
p.name = 'Joe' |
|
p.surname = 'Smith' |
|
end |
For more information, see page Self Yield.
Shared Scope
Share variables among multiple contexts in the same Flat Scope (Flat Scope).
|
lambda { |
|
shared = 10 |
|
self.class.class_eval do |
|
define_method :counter do |
|
shared |
|
end |
|
define_method :down do |
|
shared -= 1 |
|
end |
|
end |
|
}.call |
|
|
|
counter # => 10 |
|
3.times { down } |
|
counter # => 7 |
For more information, see page Shared Scope.
Singleton Method
Define a method on a single object.
|
obj = "abc" |
|
|
|
class << obj |
|
def my_singleton_method |
|
"x" |
|
end |
|
end |
|
|
|
obj.my_singleton_method # => "x" |
For more information, see page Singleton Method.
String of Code
Evaluate a string of Ruby code.
|
my_string_of_code = "1 + 1" |
|
eval(my_string_of_code) # => 2 |
For more information, see page String of Code.
Symbol To Proc
Convert a symbol to a block that calls a single method.
|
[1, 2, 3, 4].map(&:even?) # => [false, true, false, true] |
For more information, see page Symbol To Proc.