[RAILS] Read Metaprogramming Ruby (Part I, until Wednesday)

Chapter 1 Initial M

--Metaprogramming is to write the code that writes the code.

What is Introspection?

In programming and Ruby, introspection is generally the ability to see objects, classes, etc. at runtime to know them.

cf. Ruby introspection

Example:

#Below is the class definition

class A
   def a; end
end

module B
   def b; end
end

class C < A
   include B
   def c; end
end

##Introspection
# Q.What is a C instance method?
# A. C.instance_methods # [:c, :b, :a, :to_json, :instance_of?...]

# Q.What is an instance method that declares only C?
# A. C.instance_methods(false) # [:c]

# Q.What are Class C ancestors?
# A. C.ancestors # [C, B, A, Object,...]

# Q.C super class?
# A. C.superclass # A

Metaprogramming

--You want to create an ORM class.

# the_m_word/orm.rb
class Entity
  attr_reader :table, :ident

  def initialize(table, ident)
    @table = table
    @ident = ident
    Database.sql "INSERT INTO #{@table} (id) VALUES (#{@ident})"
  end

  def set(col, val)
    Database.sql "UPDATE #{@table} SET #{col}='#{val}' WHERE id=#{@ident}"
  end

  def get(col)
    Database.sql("SELECT #{col} FROM #{@table} WHERE id=#{@ident}")[0][0]
  end
end

Even if you do not write the above class You can write code that defines accessor methods at runtime simply by inheriting ActiveRecord :: Base.

--Rather than writing an accessor method for each attribute of the class, just inherit ActiveRecord :: Base and write code that defines the accessor method at runtime (* write code that writes code * )be able to.

Chapter 2: Monday: Object Model

Open class (monkey patch)

--The main task of class is to take you into the context of the class. --It's more like a scope operator than a class declaration. --You can reopen existing classes, including standard classes, and modify them on the fly.

--Easy patches to classes, such as when you overwrite the original method of the same name, are called monkey patches.

Instance variables

--There is no connection between Ruby object classes and instance variables. --Instance variables only appear when a value is assigned. --The names and values of instance variables are like hash keys and values. --Both keys and values can vary from object to object. --Instance variables live in objects and methods live in classes.

Method

--Even if it is the same method, it is called an instance method when focusing on a class, and it is called a method when focusing on an object.

class

--Class is an object --A class is an object, and the class of the class is the Class class. --The instance of the Class class is the class itself.

#Of the argument"false"Means "ignore inherited methods"
Class.instance_methods(false) # => [:allocate, :new, :superclass]

module

--The superclass of the Class class is Module --A class is a module that adds three instance methods (new, allocalte, superclass) for creating objects and inheriting classes.

Class.superclass # => Module

--Usually, select a module when including it somewhere, and select a class when creating or inheriting an instance.

constant

--All capitalized references, including class and module names, are constants. --You can also change the value of the constant. --You can also change the value of the String class to break Ruby.

--Similarity between Ruby constants and files --All the constants in the program are arranged in a tree like a file system. --Modules and classes are directories and constants are files.

Constant path

--Constant paths are separated by two colons. --When you are in the back of the constant tree, you can specify an external constant with an absolute path by starting with two colons indicating the root.

--Instance method Module # constants --Returns all constants in the current scope. # => [: C,: Y] --Something like the filesystem ls command. --Class method Module.constants --Returns the top-level constants of the current program. --Class method Module.nesting --Returns a constant containing the path. # => [M :: C :: M2, M :: C, M]

Summary of objects and classes

What is an object?

--A collection of instance variables with a link to a class. --Object methods live in the object's class, not the object, and are called class instance methods.

What is a class?

--An object (instance of Class class) with a list of instance methods and a link to the superclass. --The Class class is a subclass of the Module class. --The Class class has an instance method. --Example: new --Use the class name to refer to the class.

Namespace

--Wrap the class in a namespace to avoid conflicts between class and module names. ――If you apply the monkey patch easily, you will get unexpected results.

load and require

load

--Used to load files and execute code. --Variables go out of scope when loading a file, but not out of scope. --Execute the file each time you call it.

require

--Used to import the library. --Read the file only once.

Ruby object model

Method search

--Go up the inheritance chain until Ruby enters the receiver's class and finds a method. ―― "One step to the right, then up" rule --Go one step to the right towards the receiver class and go up the inheritance chain until you find a method.

MySubClass.ancestors # => [MySubclass, MyClass, Object, Kernel, BasicObject]

Receiver

--The object to which the method to call belongs

Inheritance chain

--Example of class inheritance chain --Class → Superclass → Superclass ... (Continue to BasicObject)

Module and method search

--When you include a module in a class (or another module), Ruby inserts the module into the inheritance chain. ――It goes into ** directly above ** of the class to include.

module M1
  def my_method
    'My#my_method()'
  end
end

class C
  include M1
end

class D < C; end

D.ancestors # => [D, C, M, Object, Kernel, BasicObject]

prepend method

--It works the same as include, but the module is inserted under ** under ** of the include lower class.

Method execution

--When the method is called, remembering the receiver reference will remind you who is the receiver when the method is executed.

self keyword

--Ruby code is executed inside ** current object self **. --When calling a method, the receiver of the method becomes self. --From that point on, all instance variables become self instance variables. --All method calls that do not specify the receiver are calls to self. --If you explicitly call another object and call the method, that object becomes self this time.

Top level context

--The state when the method is not called, or when all the called methods are returned.

self # => main
self.class # => Object

Class definition and self

--Inside the definition of a class or module (outside the method), the role of self is the class or module itself.

class MyClass
  self # => MyClass
end

private keyword

--You cannot call a private method with an explicit receiver --The private method must be for the implicit receiver, self.

--Only you can call methods with private.

Refinements

--Similar to a monkey patch, but a way to prevent changes from reaching globally.

module StringExtensions
  refine String do
    def reverse
      "esrever"
    end
  end
end

module StringStuff
  using StringExtensions
  "my_string".reverse # => "esrever"
end

"my_string".reverse # => "gnirts_ym"

--Refinations are only enabled in two places.

  1. The refine block itself and
  2. From where you called using to the end of the module (if you are in the module definition) or to the end of the file (if you are at the top level)

--Refinements are the same as open classes and monkey patches within a limited scope with Refinements enabled. --You can define new methods, redefine existing methods, and include and prepend modules. --Code with Refinements enabled takes precedence over the code in the refined class. --Refining a class is like pasting a patch into the original code.

Chapter 2 Summary

-An object consists of multiple instance variables and links to classes.
-The object's methods live in the object's class (from the class's perspective, it's called an instance method).
-A class is an object of the Class class. Classmates are just constants.
-Class is a subclass of Module. A module is basically a collection of methods. In addition, classes can be instantiated with new or created hierarchically with superclass.
-The constants are arranged on a tree like a file system. Modules and classes are named directories, and regular constants are files.
-Each class has an inheritance chain that extends to BasicObject.
-When you call the method, Ruby goes one step to the right towards the receiver's class and then up the inheritance chain. It continues until you discover a method or end the inheritance chain.
-When you include a module in a class, the module is inserted directly above the inheritance chain for that class. Pre-penning a module inserts it directly under the inheritance chain for that class.
-When calling the method, the receiver becomes self.
-When defining a module (or class), that module becomes self.
-Instance variables are always considered instance variables of self.
-If you call a method without explicitly specifying a receiver, it is considered to be a self method.
-Refinements are like patching class code and overriding normal method search. However, Refinements are only valid in a limited area from the point where using is called to the point where the definition of the file or module ends.

Chapter 3: Tuesday: Method

Eliminate duplicate code

  1. Dynamic method
  2. method_missing

1. Dynamic method

-"Calling a method means sending a message to the object."

--Use dot notation or Object # send to call the method.

obj = MyClass.new
obj.my_method(3) # => 6
obj.send(:my_method, 3) # => 6

Dynamic dispatch

--By using send, the method name you want to call becomes a normal argument, and you can decide which method to call when the code is executed.

--Example of Pry # refresh

def refresh(options={})
  defaults = {}
  attributes = [ :input, :output, :commands, :print, :quiet,
                 :exception_handler, :hooks, :custom_completions,
                 :prompt, :memory_size, :extra_sticky_locals ]

  attributes.each do |attribute|
    defaults[attribute] = Pry.send attribute
  end
  # ...
  defaults.merge!(options).each do |key, value|
    # memory_size=The accessor of the attribute such as is called.
    send("#{key}=", value) if respond_to?("#{key}=")
  end

  true
end

--Code example with dynamic dispatch added

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def mouse
    component :mouse
  end

  def cpu
    component :cpu
  end

  def keyboard
    component :keyboard
  end

  def component(name)
    info = @data_source.send "get_#{name}_info", @id
    price = @data_source.send "get_#{name}_price", @id
    result = "#{name.capitalize}: #{info} ($#{price})"
    return "* #{result}" if price >= 100
    result
  end
end

Method name and symbol

--Symbols are immutable (immutable), unlike the characters in strings, so they are suitable for names.

Dynamic method

--A technique for defining methods at run time. --The method name can be determined at runtime.

Module#define_method

class MyClass
  define_method :my_method do |my_arg|
    my_arg * 3
  end
end

obj = MyClass.new
obj.my_method(2) # => 6

--Code example with define_method added

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def self.define_component(name)
    define_method(name) do
      info = @data_source.send "get_#{name}_info", @id
      price = @data_source.send "get_#{name}_price", @id
      result = "#{name.capitalize}: #{info} ($#{price})"
      return "* #{result}" if price >= 100
      result
    end
  end

  define_component :mouse
  define_component :cpu
  define_component :keyboard
end

--Example of introspecting data_source and further refactoring

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
    # Array#If you pass a block to grep, the block will be evaluated for all elements that match the regular expression.
    #The string that matches the parentheses of the regular expression is a global variable$Stored in 1.
    #Example: data_source is get_cpu_info and get_mouse_If you have two methods of info,
    # Computer.define_You will call component twice (pass the strings "cpu" and "mouse" respectively)
    data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1 }
  end

  def self.define_component(name)
    define_method(name) do
      # ...
    end
  end
end

method_missing

--When searching for a method, if the method is not found, call the method_missing method to accept the loss. --method_missing is the private instance method of BasicObject inherited by all objects.

nick.send :method_missing, :my_method
# => NoMethodError: undefined method `my_method` for #<Lawyer:0x007f801b0f4978>
class Lawyer
  def method_missing(method, *args)
    puts "Called:#{method}(#{args.join(', ')})"
    puts "(I also passed the block)" if block_given? 
  end
end

bob = Lawyer.new
bob.talk_simple('a', 'b') do
  #block
end
# =>Called: talk_simple(a, b)
#(I also passed the block)

--The response of BasiObject # method_missing will be NoMethodError. --The place where unaddressed messages end up, and where NoMethodError is born.

method_missing override

Ghost method

--This is a message processed by method_missing, which looks like a normal call to the caller, but the corresponding method is not found on the receiver side.

--Hashie's example

module Hashie
  class Mash < Hashie::Hash
    def method_missing(method_name, *args, &blk)
      #If the name of the called method is a hash key[]Call the method and return the corresponding value.
      return self.[](method_name,&blk)ifkey?(method_name)
      #The method name ends with "="If,"=To retrieve the name of the attribute, then retain its value.
      #If neither matches, method_missing returns the default value.
      match = method_name.to_s.match(/(.*?)([?=!]?)$/)
      case match[2]
      when "="
        self[match[1]] = args.first
      else
        default(method_name, *args, &blk)
      end
    end
  end
end
Dynamic proxy

--A wrapped object that concentrates method calls on method_missing, supplements the ghost method, and forwards it to another object.

--Example of Ghee (a library with easy access to HTTP API on GitHub)

class Ghee
  class ResourceProxy
    # ...
 
    def method_missing(message, *args, &block)
      # my_gist.Method calls like url, Hashee::Mash#method_Forward to missing.
      subject.send(message, *args, &block)
    end

    def subject
      #Receive a GitHub object in JSON and Hasie::Convert to Mash.
      @subject ||= connection.get(path_prefix){|req| req.params.merge!params }.body
    end
  end
end

--Two points of Ghee

  1. Hold the GitHub object in a dynamic hash. Hash attributes can be accessed by calling ghost methods such as url and description.
  2. Wrap these hashes in a proxy object. The proxy object provides additional methods. The proxy does two things.
  3. Implementation of a method that requires processing such as star.
  4. Transfer a method that just reads data such as url to the wrapped hash.

--Gee keeps the code simple with this two-step design. --There is a ghost method when reading data, so there is no need to define a method. --Define methods only when you need specific code like star.

--Another advantage of these dynamic methods is that they can automatically respond to changes in the GitHub API. --Even if GitHub adds a new field, it's also a ghost method, so Ghee can support the call without modifying the source code.

--Computer class refactoring using method_missing

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  # BasicObject#method_Override missing
  def method_missing(name)
    super if !@data_source.respond_to?("get_#{name}_info")
    info = @data_source.send("get_#{name}_info", @id)
    price = @data_source.send("get_#{name}_price", @id)
    result = "#{name.capitalize}: #{info} ($#{price})"
    return "* #{result}" if price >= 100
    result
  end

  #Respond to see if there is a ghost method_to_missing?Is properly overridden.
  def respond_to_missing?(method, include_private = false)
    @data_source.respond_to?("get_#{method}_info") || super
  end
end

Module#const_missing

--A method that allows you to use old classmates without a new classmate namespace.

--Example when Task is renamed to Rake :: Task due to upgrade

class Module
  def const_missing(const_name)
    case const_name
    when :Task
      #Warn you about using obsolete class names.
      Rake.application.const_warning(const_name)
      Rake::Task
    when :FileTask
      Rake.application.const_warning(const_name)
      Rake::FileTask
    when :FileCreationTask
      # ...
    end
  end
end

Buggy method_missing

--The variable number is defined in the block and is out of scope on the last line of method_missing, and when the last line is executed, Ruby doesn't realize that number is a variable and self I think it's a method call without parentheses. --Normally, NoMethodError is explicitly generated and the problem becomes clear, but here, since you have defined method_missing yourself and called number in it, the same event chain Occurs over and over again, eventually causing a stack overflow.

class Roulette
  def method_missing(name, *args)
    person = name.to_s.capitalize
    3.times do
      number = rand(10) + 1
      puts "#{number}..."
    end
    "#{person} got a #{number}"
  end
end

--Do not introduce ghost methods when you don't need them. --Start by writing a regular method, make sure your code is working, and then refactor it to use method_missing.

Blank slate

--If the name of the ghost method (method defined by method_missing) conflicts with the name of the inherited real method, the latter wins. --A class in which inherited methods are deleted and there are only a minimum number of methods is called a blank slate.

--BasicObject, which is the root of Ruby's class hierarchy, has only the minimum necessary methods, and in order to quickly define a blank slate in Ruby, BasicObject should be inherited.

Chapter 3 Summary

-The ghost method is the basic advice (always call super, always respond_to_missing?Redefines), which avoids most problems, but still causes complex bugs.
-The ghost method is not a real method, so it behaves differently than the actual method.
-There are some situations where only ghost methods can be used, such as when there are a large number of method calls or when the method to be called is known only at runtime.
-"Use dynamic methods if possible, ghost methods if you can't help"

Chapter 4: Wednesday: Block

--Block is a member of a large family of "callable objects". ――Brock's family has a different lineage from object-oriented programming, and follows the trend of "functional programming languages" such as LISP.

Block basics

--Blocks can only be defined when calling a method. --The block is passed to the method, which calls back the block using the yield keyword. --When you call back a block, you can pass arguments just like a method. --The block returns the result of evaluating the last line, just like a method. --Inside the method, you can use the Kernel # block_given? Method to check for blocks.

Block is closure

--In order to execute the code of the block, you need an environment such as local variables, instance variables, and self, and prepare for execution after putting them together. -** A block contains both code and a collection of bindings **.

Blocks and bindings

--Define a block and get the binding at that point. --When you pass a block to a method, take that binding with you.

def my_method
  #The bindings in the method are not visible to the block.
  x = "Goodbye"
  yield("cruel")
end

x = "Hello"
#Wrap a local binding like x and then pass the block to the method.
my_method = {|y| "#{x}, #{y} world"} # => "Hello, cruel world"

scope

--Language such as Java and C # has a mechanism to refer to variables from "internal scope" to "external scope", but Ruby does not have such a nested structure of visibility, and scopes are properly distinguished. -** When entering the new scope (when the program changes scope), the old binding is replaced with the new binding **.

Global variables and top-level instance variables

#Global variables can be accessed from any scope.
def a_scope
  $var = "some value"
end

def another_scope
  $var
end

a_scope
another_scope # => "some value"
#Top-level instance variables are instance variables of the main object at the top level.
#You can call it from anywhere the main becomes self.
#If the other object becomes self, the top-level instance variable goes out of scope.
@var = "Top level variables@var"

def my_method
  @var
end

my_method # => "Top level variables@var"

class MyClass
  def my_method
    @var = "Top level variables@Not var!"
  end
end

Scope gate

--There are three places where the program switches scopes and opens a new scope, and the keyword that marks each is called the scope gate.

  1. Class definition (class)
  2. Module definition (module)
  3. Method (def)

Flattening the scope

--How to pass bindings across scope gates so that variables in other scopes are visible. ――This magic is also called ** nested lexical scope **. --The magic of pushing two scopes together and sharing variables is called ** flatscope **.

my_var = "success"

MyClass = Class.new do  # class MyClass
  puts "In the class definition#{my_var}!"

  define_method :my_method do  # def my_method
    "Also in the method definition#{my_var}!"
  end
end

Shared scope

--How to share variables while protecting them with scope gates.

def define_methods
  shared = 0

  Kernel.send :define_method, :counter do
    shared
  end

  Kernel.send :define_method, :inc do |x|
    shared += x
  end
end

define_methods

counter # => 0
inc(4)
counter # => 4

Closure summary

-There are many bindings on Ruby scope.
    -Scopes are separated by scope gates such as class, module, and def.
-Blocks (closures) can be used when you want to jump over a scope gate and sneak into a binding.
    -By defining a block, you can wrap and carry the constraints in your current environment.
        -Replace the scope gate with a method call, wrap the current binding in a closure, and pass that closure to the method.
-class is Class.new, module is Module.new and def are Modules.define_Can be replaced with method.
    -This is the flat scope, the basic magic of closures.
-You can share bindings by defining multiple methods in the same flat scope and protecting them with scope gates.
    -This is called a shared scope.

instance_eval

--BasicObject # instance_eval ** evaluates the block in the context of the object **. - --The block passed to instance_eval is called a context searcher. --Because it's code that explores the inside of an object and does something there.

class MyClass
  def initialize
    @v = 1
  end
end

obj = MyClass.new

# instance_The block passed to eval is evaluated after making the receiver self, so
#Receiver private methods and@You can also access instance variables such as v.
obj.instance_eval do
  self  # => #<MyClass:0x3340dc @v=1>
  @v    # => 1
end

Difference between instance_eval and instance_exec

class C
  def initialize
    @x = 1
  end
end

# instance_eval
class D
  def twisted_method
    @y = 2
    # instance_When eval changes self to a receiver
    #The instance variable of the caller (D) falls out of the block.
    C.new.instance_eval { "@x: #{@x}, @y: #{@y}" }
  end
end

D.new.twisted_method # => "@x: 1, @y: "

# instance_exec
class D
  def twisted_method
    @y = 2
    #You can put C instance variables and D instance variables in the same scope.
    C.new.instance_exec(@y) {|y| "#{@x}, @y: #{y}" }
  end
end
D.mew.twisted_method # => "@x: 1, @y: 2"

About the destruction of encapsulation

--If you use the context searcher (block passed to instance_eval), you can break the encapsulation, so the use cases are limited in the following cases. --If you want to see the contents of the object from irb --For testing

--Padrino test example

describe "PadrinoLogger" do
  context 'for logger functionality' do
    context "static asset logging" do
      ...
      should 'allow turning on static assets logging' do
        Padrino.logger.instance_eval { @log_static = true } 
        # ...
        get "/images/something.png "
        assert_equal "Foo", body
        assert_match /GET/, Padrino.logger.log.string
        Padrino.logger.instance_eval { @log_static = false }
      end
    end
  end
end

Clean room

--Create an object just to evaluate the block. An environment for evaluating blocks.

Where code can be stored outside of blocks

--Callable object --In Ruby, "Save the code and call it later" method --In Proc. This is a block that has become an object. --In lambda. This is a variant of Proc. --In the method.

Proc object

--In Ruby, almost everything is an object, but blocks are different.

inc = Proc.new {|x| x + 1 }
inc.call(2) # => 3

Convert blocks to Proc

--Two kernel methods to convert blocks to Proc - lambda - proc

dec = lambda {|x| x - 1 }
# dec = ->(x) { x - 1 }
dec.class # => Proc
dec.call(2) # => 1

& Qualify

--A block is like an anonymous argument passed to a method. --Case where yield is not enough. --When you want to pass a block to another method --When you want to convert a block to Proc

-** Assign bindings to blocks (give a "name" to point to the block) & ** --Place at the end of the method argument sequence and put a & mark in front of the name. --Adding & means that you want to receive the block passed to the method and convert it to Proc.

--Example of converting a block to Proc

def math(a, b)
  yield(a, b)
end

# &After qualifying, it takes the block passed to the method and converts it to Proc!
def do_math(a, b, &operation) 
  math(a, b, &operation)
end

do_math(2, 3) {|x, y| x * y } # => 6
def my_method(&the_proc)
  the_proc
end

p = my_method {|name| "Hello, #{name}!" }
p.class # => "Proc"
p.call("Bill") # => "Hello, Bill!"

--Example of returning Proc to a block -** Convert Proc to Block & **

def my_method(greeting)
  "#{greeting}, #{yield}"
end

my_proc = proc { "Bill" }
my_method("Hello", &my_proc)

-Proc conversion by & is called "Proc coercion" (coercion: coercion, coercion, etc.). -It is very similar to * in multiple assignment in that it can be used on both the give side and the receive side of one operator, and it works exactly the opposite.

lambda

--Proc made with lambda is different from other Proc. --A Proc object created with lambda is called lambda. ――The other is simply called Proc. --You can check if Proc is lambda by using Proc # lambda? method.

--The difference between Proc and lambda

  1. Thing about return keyword
  2. Argument check

Proc, lambda and return

--The meaning of the return keyword is different between lambda and Proc.

#For lambda, return simply returns from lambda.
def double(callable_object)
  callable_object.call * 2
end

1 = lambda { return 10 }
double(1) # => 20

#For Proc, return from the scope in which Proc is defined.
def another_double
  p = Proc.new { return 10 }
  result = p.call
  return result * 2 #I won't come this far!
end

another_double # => 10

def double(callable_object)
  callable_object.call * 2
end

p = Proc.new { return 10 }
double(p)  # => LocalJumpError

p = Proc.new { 10 }
double(p) # => 20

Proc, lambda and number of terms

--In general, lambda is stricter in handling arguments than Proc (or ordinary block) --If you call lambda with a different number of terms, you get an ArgumentError. --On the other hand, Proc matches the argument sequence to your expectations.

p = Proc.new
p.call(1, 2, 3) # => [1, 2]
p.call(1)  # => [1, nil]
```

 Proc vs lambda

 --Generally speaking, lambda is said to be more intuitive than Proc because it's more like a method.
 --Also, the number of terms is strict, and when you call return, it just ends.
 --Many Rubyists choose lambda first, unless you need Proc functionality.


### Method object

 --The method will also be a callable object like lambda.

```rb
class MyClass
  def initialize(value)
    @x = value
  end

  def my_method
    @x
  end
end

object = MyClass.new(1)
m = object.method :my_method  #You can get the method itself as a Method object
m.call  # => 1  # Object#The object obtained by method is Method#Can be executed using call
```

 --`Method` can be converted to` Proc` with `Method # to_proc`, and blocks can be converted to methods with` define_method`.

 --lambda is evaluated in the defined scope (closure), but Method is evaluated in the scope of the object to which it belongs.

 UnboundMethod

 --Something like a method separated from the original class or module.

```rb
module MyModule
  def my_method
    42
  end
end

unbound = MyModule.instance_method(:my_method)
unbound.class  # => UnboundMethod
```

### Summary of callable objects

```
-Block (not "object" but "callable"): evaluated in the defined scope
-Proc: An object of the Proc class. Like a block, it is evaluated in a defined scope.
-lambda: This is also a Proc class object, but it's slightly different from a normal Proc. Like blocks and procs, it is a closure and is evaluated within a defined scope.
-Method: Bind to the object and evaluate in the scope of the object. You can also pull it out of the scope of an object and bind it to another object.

-For methods and lambdas, return returns from the callable object.
    -On the other hand, Proc and blocks return from the original context of the callable object.
-The method is strict with respect to the difference in the number of terms at the time of calling, and lambda is also almost strict.
    -Proc and block are tolerant.
-The above difference is`Proc.new` 、 `Method#to_proc` 、 `&Qualification`And so on
You can convert from one callable object to another.
```

## Domain-specific language

 - Example 1

```rb
def event(description)
  puts "ALERT: #{description}" if yield
end
loda 'events.rb'

# events.rb
event 'Events that always occur' do
  true
end

event 'Events that never occur' do
  false
end

# => ALERT:Events that always occur
```

 --Example 2

```rb
def setup(&block)
  @setups << block
end

def event(description, &block)
  @events << {:description => description, :condition => block }
end

@setups = []
@events = []
load 'events.rb'

@events.each do |event|
  @setups.each do |setup|
    setup.call
  end
  puts "ALERT: #{event[:description}" if event[:condition].call
end
```

### Delete global variables

 --Example 2-Revised

```rb
lambda {
  #setups and events are lambad local variables and are not visible to the outside world
  setups = []
  events = []

  Kernel.send :define_method, :setup do |&block|
    setups << block
  end

  Kernel.send :define_method, :event do |description, &block|
    events << {:description => description, :condition => block}
  end

  Kernel.send :define_method, :each_setup do |&block|
    setups.each do |setup|
      block.call event
    end
  end

  Kernel.send :define_method, :each_event do |&block|
    events.each do |event|
      block.call event
    end
  end
}.call

each_event do |event|
  each_setup do |setup|
    setup.call
  end
  puts "ALERT: #{event[:description]}" if event[:condition].call
end

#Alternatively, you can use a clean room in the context of the Object to prevent events from sharing instance variables.

each_event do |event|
  env = Object.new
  each_setup do |setup|
    env.instance_eval &setup
  end
  puts "ALERT: #{event[:description]}" if env.instance_eval &(event[:condition])
end
```

## Chapter 4 Summary

```
-Scopegate and how does Ruby manage scopes?
-How to add flat and shared scopes to make bindings visible across scopes.
-In the scope of the object (instance_eval and instance_How to execute code (using exec) or how to execute code in a clean room.
-How to convert blocks and objects (Proc) to each other.
-How to convert methods and objects (Method and UnboundMethod) to each other.
-Types of callable objects (blocks, Proc, Lambda) and their differences. Difference from normal method.
-How to write your own small DSL.
```



Recommended Posts

Read Metaprogramming Ruby (Part I, until Wednesday)
I started Ruby
Ruby: Read CouchDB data (Read)