Spell Book - Appendixes - Metaprogramming Ruby 2: Program Like the Ruby Pros (2014)

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.