Metaprogramming Ruby Part 4
| rubyNoting highlights from the book Metaprogramming Ruby (2nd Edition).
Chapter 4. Wednesday: Blocks
- A block is a closure, it captures bindings of current environment and carries them around. It is NOT an object.
Scope
- When program changes scope, some bindings are replaced by a new set of bindings
- Scope Gates: Places where a program leaves the previous scope behind and opens a new scope
- Class definitions
- Module definitions
- Methods
- Flat Scope: Passing objects through scope gates
Class.new do; end
Module.new do; end
define_method :holla do; end
instance_eval and instance_exec
instance_evalevaluates a block in the context of an objectinstance_execis similar toinstance_evalbut can also pass arguments to block- with
instance_evalivars switch on the context of self, e.g., the ivars of the caller fall out of scope onceinstance_evalswitches the value of self to the receiver. instance_execcan be used to keep the bindings of the caller visible to the receiver
Proc Objects
- Blocks can be converted to Procs for later execution via
Proc#call - There are five ways to declare Proc objects
p = Proc.new { |x| x + 1 }
p = lambda { |x| x + 1 } (via Kernel)
p = proc { |x| x + 1 } (via Kernel)
p = ->(x) { x + 1 } (lambda)
&some_block
&operator: block must be passed in as last argument to a method and prepended with & operator, converts the block to Proc
method_missing
- Private instance method defined in
BasicObjectand inherited by all classes - Overriding
method_missing: takes method, arguments, and block
Procs vs Lambdas
- Procs created with
lambdaare called lambdas, the rest are simply called procs - Return statements in lambdas simply return from the lambda, while in procs a return statement returns from the scope where the proc itself is defined
- When using procs avoid explicit returns, returning from top-level scope fails with
LocalJumpError: unexpected return - Lambdas check arity, will fail with
ArgumentErrorif arity is wrong - Procs fits argument list to its own expectations, it will drop excess arguments and assign
nilto missing arguments - Rubyists tend to use lambdas, they behave more like methods since they care about arity and return keyword behavior is the same
Method Objects
- A method is also a callable object
Kernel#methodandKernel#singleton_methodcan store a certain method as an object to be used byKernel#calllater on- Two handy methods to convert between method/proc:
Method#to_proc(method to proc),define_method(proc to method) - Key difference between method objects and the other callable objects: method objects are evaluated in scope of its object, while callable objects are evaluated in the scope where they are defined
Unbound Methods
UnboundMethod#bindandModule#instance_methodcan bind an unbound method to an object- Methods that come from a class can only be bound to same class or subclass, methods originating from module do not have this limitation
Module#define_methodcan also be used- Methods can become unbound from their class/module and be used to create normal callable methods by using
Method#unbindorModule#instance_method - ActiveSupport autoloading feature uses this unbound method trick if a particular class does not want to autoload itself
module Loadable
def self.exclude_from(base)
base.class_eval { define_method(:load, Kernel.instance_method(:load) }1
end
end
Any class passed to exclude_from has the load method added to it. This new load method comes as an UnboundMethod using instance_method to unbind Kernel#load.