Sunday, August 29, 2010

The Ruby Metaprogramming Spell Book

Here are all the spells from Metaprogramming Ruby, as link-friendly Gists.

March 2014 update: I updated this list to match the second edition of the book. I added three spells that are new to Ruby 2.x: Refinement, Refinement Wrapper and Prepended Wrapper. I also dropped a few spells that don't seem to be as relevant today as they used to be, at least in the context of this book: Argument Array, Named Arguments, Pattern Dispatch and Class Extension Mixin.

# ===================
# Spell: Around Alias
# ===================
# Call the previous, aliased version of a method from a redefined method.
class String
alias :old_reverse :reverse
def reverse
"x#{old_reverse}x"
end
end
"abc".reverse # => "xcbax"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw around_alias.rb hosted with ❤ by GitHub

# ===================
# Spell: Blank Slate
# ===================
# Remove methods from an object to turn them into Ghost Methods (http://gist.github.com/534776).
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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw blank_slate.rb hosted with ❤ by GitHub

# ======================
# Spell: Class Extension
# ======================
# Define class methods by mixing a module into a class’s singleton class
# (a special case of Object Extension - http://gist.github.com/534667).
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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==============================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw class_macro.rb hosted with ❤ by GitHub

# =================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw clean_room.rb hosted with ❤ by GitHub

# =====================
# Spell: Code Processor
# =====================
# Process Strings of Code (http://gist.github.com/535047) from an external source.
File.readlines("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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
1 + 1
3 * 2
Math.log10(100)

# ====================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==========================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =======================
# Spell: Dynamic Dispatch
# =======================
# Decide which method to call at runtime.
method_to_call = :reverse
obj = "abc"
obj.send(method_to_call) # => "cba"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ====================
# Spell: Dynamic Proxy
# ====================
# Forward to another object any messages that don’t match a method.
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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw flat_scope.rb hosted with ❤ by GitHub

# ===================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw ghost_method.rb hosted with ❤ by GitHub

# ==================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw hook_method.rb hosted with ❤ by GitHub

# ====================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =============================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ===================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw mimic_method.rb hosted with ❤ by GitHub

# ==================
# Spell: Monkeypatch
# ==================
# Change the features of an existing class.
"abc".reverse # => "cba"
class String
def reverse
"override"
end
end
"abc".reverse # => "override"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw monkeypatch.rb hosted with ❤ by GitHub

# ================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw namespace.rb hosted with ❤ by GitHub

# ================
# Spell: Nil Guard
# ================
# Override a reference to nil with an “or.”
x = nil
y=x || "avalue" # =>"avalue"
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw nil_guard.rb hosted with ❤ by GitHub

# =======================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw open_class.rb hosted with ❤ by GitHub

# ========================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =================
# Spell: Refinement
# =================
# Patch a class until the end of the file, or (from Ruby 2.1) 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw refinement.rb hosted with ❤ by GitHub

# =========================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# ==============
# Spell: 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 `delete' at level 2>
end
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw sandbox.rb hosted with ❤ by GitHub

# =================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw scope_gate.rb hosted with ❤ by GitHub

# =================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw self_yield.rb hosted with ❤ by GitHub

# ===================
# Spell: Shared Scope
# ===================
# Share variables among multiple contexts in the same Flat Scope (https://gist.github.com/nusco/535082).
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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby
view raw shared_scope.rb hosted with ❤ by GitHub

# =======================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: String of Code
# =====================
# Evaluate a string of Ruby code.
my_string_of_code = "1 + 1"
eval(my_string_of_code) # => 2
# For more information: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

# =====================
# Spell: 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: http://www.pragprog.com/titles/ppmetr/metaprogramming-ruby

2 comments:

  1. Would be great if this could be turned into a single cheatsheet. Awesome memory refresher, thanks!

    ReplyDelete
  2. Thanks, Andy! You can download the PDF appendix listing all the spells from the book's page (http://media.pragprog.com/titles/ppmetr/spells.pdf). Not really a cheat sheet, but it should do.

    ReplyDelete