[RAILS] Lesen Sie Metaprogramming Ruby (Teil I, bis Mittwoch)

Kapitel 1 Initial M.

--Metaprogramming schreibt Code, der Code schreibt.

Was ist Selbstbeobachtung?

In der Programmierung und in Ruby ist Introspektion im Allgemeinen die Fähigkeit, Objekte, Klassen usw. zur Laufzeit zu sehen, um sie zu kennen.

vgl. Ruby Introspektion

Beispiel:

#Unten ist die Klassendefinition

class A
   def a; end
end

module B
   def b; end
end

class C < A
   include B
   def c; end
end

##Selbstbeobachtung
# Q.Was ist eine C-Instanzmethode?
# A. C.instance_methods # [:c, :b, :a, :to_json, :instance_of?...]

# Q.Was ist eine Instanzmethode, die nur C deklariert?
# A. C.instance_methods(false) # [:c]

# Q.Was sind die Vorfahren der Klasse C?
# A. C.ancestors # [C, B, A, Object,...]

# Q.C Superklasse?
# A. C.superclass # A

Metaprogrammierung

# 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

Auch wenn Sie die obige Klasse nicht schreiben Sie können Code schreiben, der zur Laufzeit Zugriffsmethoden definiert, indem Sie einfach ActiveRecord :: Base erben.

Kapitel 2: Montag: Objektmodell

Offene Klasse (Affenpflaster)

Instanzvariable

Methode

Klasse

--Klasse ist ein Objekt

#Vom Argument"false"Bedeutet "geerbte Methoden ignorieren"
Class.instance_methods(false) # => [:allocate, :new, :superclass]

Modul

Class.superclass # => Module

Konstante

Ständiger Weg

--Instanzmethode Modul # Konstanten

Zusammenfassung der Objekte und Klassen

Was ist ein Objekt?

Namensraum

laden und benötigen

load

require

Ruby-Objektmodell

Methodensuche

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

Empfänger

Vererbungskette

Modul- und Methodensuche

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]

Methode voranstellen

Ausführung der Methode

Selbst Schlüsselwort

--Ruby-Code wird innerhalb von ** aktuelles Objekt selbst ** ausgeführt.

Kontext der obersten Ebene

--State, wenn die Methode nicht aufgerufen wird oder wenn alle aufgerufenen Methoden zurückgegeben werden

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

Klassendefinition und Selbst

class MyClass
  self # => MyClass
end

privates Schlüsselwort

Refinements

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"

--Refinierungen sind nur an zwei Stellen aktiviert.

  1. Der Verfeinerungsblock selbst und
  2. Von wo aus Sie mit bis zum Ende des Moduls (wenn Sie sich in der Moduldefinition befinden) oder bis zum Ende der Datei (wenn Sie sich auf der obersten Ebene befinden) angerufen haben.

Kapitel 2 Zusammenfassung

-Ein Objekt besteht aus mehreren Instanzvariablen und Verknüpfungen zu Klassen.
-Die Methoden des Objekts befinden sich in der Klasse des Objekts (aus Sicht der Klasse wird sie als Instanzmethode bezeichnet).
-Eine Klasse ist ein Objekt der Klassenklasse. Klassenkameraden sind nur Konstanten.
-Klasse ist eine Unterklasse von Module. Ein Modul ist im Grunde eine Sammlung von Methoden. Darüber hinaus können Klassen mit neu instanziiert oder hierarchisch mit Superklasse erstellt werden.
-Die Konstanten sind wie ein Dateisystem in einem Baum angeordnet. Module und Klassen werden als Verzeichnisse bezeichnet, und reguläre Konstanten sind wie Dateien.
-Jede Klasse hat eine Vererbungskette, die sich auf BasicObject erstreckt.
-Wenn Sie die Methode aufrufen, geht Ruby einen Schritt nach rechts in Richtung der Klasse des Empfängers und dann die Vererbungskette hinauf. Es wird fortgesetzt, bis Sie eine Methode entdecken oder die Vererbungskette beenden.
-Wenn Sie ein Modul in eine Klasse aufnehmen, wird das Modul direkt über der Vererbungskette für diese Klasse eingefügt. Durch das Vorabschreiben eines Moduls wird es direkt unter der Vererbungskette für diese Klasse eingefügt.
-Beim Aufruf der Methode wird der Empfänger selbst.
-Wenn Sie ein Modul (oder eine Klasse) definieren, wird dieses Modul selbst.
-Instanzvariablen werden immer als Selbstinstanzvariablen betrachtet.
-Wenn Sie eine Methode aufrufen, ohne einen Empfänger explizit anzugeben, wird sie als Selbstmethode betrachtet.
-Verfeinerungen sind wie das Patchen von Klassencode und das Überschreiben der normalen Methodensuche. Verfeinerungen sind jedoch nur in einem begrenzten Bereich von dem Punkt gültig, an dem die Verwendung aufgerufen wird, bis zu dem Punkt, an dem die Definition der Datei oder des Moduls endet.

Kapitel 3: Dienstag: Methode

Beseitigen Sie doppelten Code

  1. Dynamische Methode
  2. method_missing

1. Dynamische Methode

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

Dynamischer Versand

--Mit Verwendung von "Senden" wird der Name der Methode, die Sie aufrufen möchten, zu einem normalen Argument, und Sie können entscheiden, welche Methode aufgerufen werden soll, wenn der Code ausgeführt wird.

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=Der Accessor des Attributs wie wird aufgerufen.
    send("#{key}=", value) if respond_to?("#{key}=")
  end

  true
end
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

Methodenname und Symbol

Dynamische Methode

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-Beispiel mit definierter define_method

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
class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
    # Array#Wenn Sie einen Block an grep übergeben, wird der Block für alle Elemente ausgewertet, die dem regulären Ausdruck entsprechen.
    #Die Zeichenfolge, die den Klammern des regulären Ausdrucks entspricht, ist eine globale Variable$Gespeichert in 1.
    #Beispiel: data_Quelle ist bekommen_cpu_info und bekommen_mouse_Wenn Sie zwei Informationsmethoden haben
    # Computer.define_Sie rufen die Komponente zweimal auf (übergeben Sie die Zeichenfolgen "CPU" bzw. "Maus").
    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

nick.send :method_missing, :my_method
# => NoMethodError: undefined method `my_method` for #<Lawyer:0x007f801b0f4978>
class Lawyer
  def method_missing(method, *args)
    puts "Namens:#{method}(#{args.join(', ')})"
    puts "(Ich habe auch den Block passiert)" if block_given? 
  end
end

bob = Lawyer.new
bob.talk_simple('a', 'b') do
  #Block
end
# =>Genannt: reden_simple(a, b)
#(Ich habe auch den Block passiert)

Methode_missing überschreiben

Geistermethode

--Diese Nachricht wird von method_missing verarbeitet und sieht aus wie ein normaler Aufruf an den Aufrufer, aber die entsprechende Methode wird auf der Empfängerseite nicht gefunden.

module Hashie
  class Mash < Hashie::Hash
    def method_missing(method_name, *args, &blk)
      #Wenn der Name der aufgerufenen Methode ein Hash-Schlüssel ist[]Rufen Sie die Methode auf und geben Sie den entsprechenden Wert zurück.
      return self.[](method_name,&blk)ifkey?(method_name)
      #Das Ende des Methodennamens lautet "="Wenn,"=Entfernen Sie das "", um den Namen des Attributs abzurufen, und behalten Sie dann seinen Wert bei.
      #Wenn beides nicht übereinstimmt, Methode_fehlt gibt den Standardwert zurück.
      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
Dynamischer Proxy
class Ghee
  class ResourceProxy
    # ...
 
    def method_missing(message, *args, &block)
      # my_gist.Hashie, ein Methodenaufruf wie url::Mash#method_Übergabe an Vermisste.
      subject.send(message, *args, &block)
    end

    def subject
      #Empfangen Sie das GitHub-Objekt in JSON und Hasie::In Mash konvertieren.
      @subject ||= connection.get(path_prefix){|req| req.params.merge!params }.body
    end
  end
end
  1. Halten Sie das GitHub-Objekt in einem dynamischen Hash. Auf Hash-Attribute kann durch Aufrufen von Ghost-Methoden wie "url" und "description" zugegriffen werden.
  2. Wickeln Sie diese Hashes in ein Proxy-Objekt. Das Proxy-Objekt bietet zusätzliche Methoden. Der Proxy macht zwei Dinge.
  3. Implementierung einer Methode, die eine Verarbeitung erfordert, wie z. B. "Stern".
  4. Übertragen Sie eine Methode, die nur Daten wie "url" liest, in den umschlossenen Hash.

--Gee hält den Code mit diesem zweistufigen Design einfach.

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

  # BasicObject#method_Überschreiben fehlt
  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

  #Antworten Sie, um festzustellen, ob es eine Geistermethode gibt_to_missing?Wird richtig überschrieben.
  def respond_to_missing?(method, include_private = false)
    @data_source.respond_to?("get_#{method}_info") || super
  end
end

Module#const_missing

--Beispiel, wenn Task aufgrund eines Upgrades in Rake :: Task umbenannt wird

class Module
  def const_missing(const_name)
    case const_name
    when :Task
      #Warnen Sie vor der Verwendung veralteter Klassennamen.
      Rake.application.const_warning(const_name)
      Rake::Task
    when :FileTask
      Rake.application.const_warning(const_name)
      Rake::FileTask
    when :FileCreationTask
      # ...
    end
  end
end

Bugy method_missing

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

Unbeschriebenes Blatt

--BasicObject, das die Wurzel der Klassenhierarchie von Ruby ist, verfügt nur über die minimal erforderlichen Methoden. Um schnell eine leere Tafel in Ruby zu definieren, sollte BasicObject vererbt werden.

Kapitel 3 Zusammenfassung

-Die Ghost-Methode ist ein grundlegender Ratschlag (immer super anrufen, immer antworten_to_missing?Neudefinitionen), wodurch die meisten Probleme vermieden werden, aber dennoch komplexe Fehler verursacht werden.
-Da die Ghost-Methode keine echte Methode ist, verhält sie sich anders als die eigentliche Methode.
-In einigen Situationen können nur Ghost-Methoden verwendet werden, z. B. wenn eine große Anzahl von Methodenaufrufen vorliegt oder wenn die aufzurufende Methode nur zur Laufzeit bekannt ist.
-"Verwenden Sie nach Möglichkeit dynamische Methoden und Ghost-Methoden, wenn Sie nicht helfen können."

Kapitel 4: Mittwoch: Block

--Block ist Mitglied einer großen Familie von "aufrufbaren Objekten". --Brocks Familie hat eine andere Abstammung als objektorientiert und folgt dem Fluss von "funktionalen Programmiersprachen" wie LISP.

Blockieren Sie die Grundlagen

--Blöcke können nur beim Aufruf einer Methode definiert werden.

Der Block ist ein Verschluss

Blöcke und Bondage

def my_method
  #Die Bindungen in der Methode sind für den Block nicht sichtbar.
  x = "Goodbye"
  yield("cruel")
end

x = "Hello"
#Wickeln Sie eine lokale Bindung wie x ein und übergeben Sie den Block an die Methode.
my_method = {|y| "#{x}, #{y} world"} # => "Hello, cruel world"

Umfang

Globale Variablen und Instanzvariablen der obersten Ebene

#Auf globale Variablen kann von jedem Bereich aus zugegriffen werden.
def a_scope
  $var = "some value"
end

def another_scope
  $var
end

a_scope
another_scope # => "some value"
#Instanzvariablen der obersten Ebene sind Instanzvariablen des Hauptobjekts auf der obersten Ebene.
#Sie können es von jedem Ort aus aufrufen, an dem die Hauptleitung selbst wird.
#Wenn das andere Objekt selbst wird, verlässt die Instanzvariable der obersten Ebene den Gültigkeitsbereich.
@var = "Variablen der obersten Ebene@var"

def my_method
  @var
end

my_method # => "Variablen der obersten Ebene@var"

class MyClass
  def my_method
    @var = "Variablen der obersten Ebene@Nicht var!"
  end
end

Scope Gate

  1. Klassendefinition (Klasse)
  2. Moduldefinition (Modul)
  3. Methode (def)

Umfang reduzieren

my_var = "Erfolg"

MyClass = Class.new do  # class MyClass
  puts "In der Klassendefinition#{my_var}!"

  define_method :my_method do  # def my_method
    "Auch in der Methodendefinition#{my_var}!"
  end
end

Geteilter Bereich

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

Zusammenfassung der Schließung

-Es gibt viele Einschränkungen für den Umfang von Ruby.
    -Bereiche werden durch Bereichsfenster wie Klasse, Modul und Def getrennt.
-Wenn Sie über das Zielfernrohr springen und sich in die Knechtschaft schleichen möchten, können Sie den Block (näher) verwenden.
    -Durch Definieren eines Blocks können Sie die Einschränkungen in Ihrer aktuellen Umgebung umbrechen und übertragen.
        -Ersetzen Sie das Scope-Gate durch einen Methodenaufruf, wickeln Sie die aktuelle Bindung in einen Abschluss ein und übergeben Sie diesen Abschluss an die Methode.
-Klasse ist Klasse.neu, Modul ist Modul.neu und def sind Modul.define_Kann durch Methode ersetzt werden.
    -Dies ist ein flaches Zielfernrohr, die grundlegende Magie des Verschlusses.
-Wenn Sie mehrere Methoden in demselben flachen Bereich definieren und sie mit dem Bereichsfenster schützen, können Sie die Bindung freigeben.
    -Dies wird als gemeinsamer Bereich bezeichnet.

instance_eval

--BasicObject # instance_eval ** wertet den Block im Kontext des Objekts aus **. -

class MyClass
  def initialize
    @v = 1
  end
end

obj = MyClass.new

# instance_Der an eval übergebene Block wird ausgewertet, nachdem der Empfänger sich selbst gemacht hat
#Die private Methode des Empfängers und@Sie können auch auf Instanzvariablen wie v zugreifen.
obj.instance_eval do
  self  # => #<MyClass:0x3340dc @v=1>
  @v    # => 1
end

Unterschied zwischen instance_eval und instance_exec

class C
  def initialize
    @x = 1
  end
end

# instance_eval
class D
  def twisted_method
    @y = 2
    # instance_Wenn eval sich selbst zum Empfänger wechselt
    #Die Instanzvariable caller (D) fällt aus dem 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
    #C-Instanzvariablen und D-Instanzvariablen können sich im selben Bereich befinden.
    C.new.instance_exec(@y) {|y| "#{@x}, @y: #{y}" }
  end
end
D.mew.twisted_method # => "@x: 1, @y: 2"

Über die Zerstörung der Einkapselung

--Padrino-Testbeispiel

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

Sauberes Zimmer

--Erstellen Sie ein Objekt, um den Block auszuwerten. Eine Umgebung zum Auswerten von Blöcken.

Wo Code außerhalb von Blöcken gespeichert werden kann

Proc-Objekt

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

Konvertieren Sie Blöcke in Proc

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

& Änderung

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

# &Mit der Qualifizierung wird der an die Methode übergebene Block in Proc konvertiert!
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!"
def my_method(greeting)
  "#{greeting}, #{yield}"
end

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

-Proc-Umwandlung durch & wird "Proc-Zwang" genannt (Zwang: Zwang, Zwang usw.).

lambda

--Proc mit Lambda gemacht unterscheidet sich von anderen Proc.

  1. Sache mit dem Schlüsselwort return
  2. Argumentprüfung

Proc, Lambda und Rückkehr

#Für Lambda kehrt return einfach von Lambda zurück.
def double(callable_object)
  callable_object.call * 2
end

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

#Kehren Sie für Proc aus dem Bereich zurück, in dem Proc definiert ist.
def another_double
  p = Proc.new { return 10 }
  result = p.call
  return result * 2 #Ich werde nicht so weit kommen!
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 und Anzahl der Begriffe

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

 Proc vs lambda

 - Im Allgemeinen soll Lambda intuitiver sein als Proc, weil es eher einer Methode ähnelt.
 --Auch die Anzahl der Begriffe ist streng, und wenn Sie return aufrufen, endet sie einfach.
 - Viele Rubyisten wählen zuerst Lambda, es sei denn, Sie benötigen Proc-Funktionalität.


### Methodenobjekt

 - Die Methode wird auch zu einem aufrufbaren Objekt wie 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  #Sie können die Methode selbst als Methodenobjekt abrufen
m.call  # => 1  # Object#Das durch die Methode erhaltene Objekt wird als Methode verwendet#Kann per Aufruf ausgeführt werden
```

 --`Method` kann mit` Method # to_proc` in` Proc` konvertiert werden, und Blöcke können mit` define_method` in Methoden konvertiert werden.

 --lambda wird im definierten Bereich (näher) ausgewertet, Methode wird jedoch im Bereich des Objekts ausgewertet, zu dem es gehört.

 UnboundMethod

 - So etwas wie eine Methode, die von der ursprünglichen Klasse oder dem ursprünglichen Modul getrennt ist.

```rb
module MyModule
  def my_method
    42
  end
end

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

### Zusammenfassung der aufrufbaren Objekte

```
-Block (nicht "Objekt", sondern "aufrufbar"): Wird im definierten Bereich ausgewertet
-Proc: Ein Objekt der Proc-Klasse. Wie ein Block wird er in einem definierten Bereich ausgewertet.
-Lambda: Dies ist ebenfalls ein Proc-Klassenobjekt, unterscheidet sich jedoch geringfügig von einem normalen Proc. Wie Blöcke und Prozesse ist es ein Abschluss und wird in einem definierten Bereich bewertet.
-Methode: An das Objekt binden und im Bereich des Objekts auswerten. Sie können es auch aus dem Bereich des Objekts entfernen und an ein anderes Objekt binden.

-Geben Sie für Methoden und Lambdas Rückgaben vom aufrufbaren Objekt zurück.
    -Andererseits kehren Proc und Block aus dem ursprünglichen Kontext des aufrufbaren Objekts zurück.
-Die Methode ist streng in Bezug auf den Unterschied in der Anzahl der Begriffe zum Zeitpunkt des Anrufs, und das Lambda ist auch fast streng.
    -Proc und Block sind tolerant.
-Der obige Unterschied ist`Proc.new` 、 `Method#to_proc` 、 `&Qualifikation`Und so weiter
Sie können von einem aufrufbaren Objekt in ein anderes konvertieren.
```

## Domain-spezifische Sprache

 - Beispiel 1

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

# events.rb
event 'Ereignisse, die immer auftreten' do
  true
end

event 'Ereignisse, die niemals auftreten' do
  false
end

# => ALERT:Ereignisse, die immer auftreten
```

 - Beispiel 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
```

### Löschen Sie globale Variablen

 --Beispiel 2 überarbeitet

```rb
lambda {
  #Setups und Ereignisse sind lokale Variablen von Lambad, daher sind sie von außen nicht sichtbar
  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

#Alternativ können Sie einen Reinraum im Kontext des Objekts verwenden, um zu verhindern, dass Ereignisse Instanzvariablen gemeinsam nutzen.

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
```

## Kapitel 4 Zusammenfassung

```
-Scopegate und wie Ruby Bereiche verwaltet.
-So fügen Sie flache und gemeinsam genutzte Bereiche hinzu, um Bindungen über Bereiche hinweg sichtbar zu machen.
-Im Bereich des Objekts (Instanz_Bewertung und Instanz_Ausführen von Code (mit exec) oder Ausführen von Code in einem Reinraum.
-So konvertieren Sie Blöcke und Objekte (Proc) ineinander.
-So konvertieren Sie Methoden und Objekte (Method und UnboundMethod) ineinander.
-Arten von aufrufbaren Objekten (Blöcke, Procs, Lambda) und ihre Unterschiede. Unterschied zur normalen Methode.
-So schreiben Sie Ihr eigenes kleines DSL.
```



Recommended Posts

Lesen Sie Metaprogramming Ruby (Teil I, bis Mittwoch)
Ich habe Ruby gestartet
Ruby: CouchDB-Daten lesen (Lesen)