Wie funktioniert die Rails-Delegate-Methode?
Nachdem ich die Antwort von jvans unten gelesen und mir den Quellcode noch ein paar Mal angesehen habe, verstehe ich ihn jetzt :). Und falls sich noch jemand fragt, wie genau die Rails-Delegierten funktionieren. Alle Rails erstellen eine neue Methode mit (module_eval) in der Datei / Klasse, aus der Sie die Delegate-Methode ausgeführt haben.
Also zum Beispiel:
class A
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end
An dem Punkt, an dem der Delegat Rails heißt, wird eine Hello-Methode mit (* args, & block) in Klasse A erstellt (technisch in der Datei, in der Klasse A geschrieben ist). In dieser Methode verwenden alle Rails den Wert ": to" (Dies sollte ein Objekt oder eine Klasse sein, die bereits in der Klasse A definiert ist), und weisen Sie es einer lokalen Variablen _ zu. Rufen Sie dann einfach die Methode für dieses Objekt oder diese Klasse auf, und übergeben Sie die Parameter.
Damit der Delegierte arbeiten kann, ohne eine Ausnahme auszulösen ... mit unserem vorherigen Beispiel. Eine Instanz von A muss bereits eine Instanzvariable haben, die auf eine Instanz der Klasse B verweist.
class A
attr_accessor :b
def b
@b ||= B.new
end
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end
Dies ist keine Frage zur "Verwendung der Delegate-Methode in Schienen", die ich bereits kenne. Ich frage mich, wie genau "delegieren" Methoden delegiert: D. In Rails 4 ist der Quellcode delegate in der Ruby Module-Kernklasse definiert, wodurch er als Klassenmethode in allen Rails-Apps verfügbar ist.
Eigentlich wäre meine erste Frage, wie Rubys Modulklasse enthalten ist. Ich meine, jede Ruby-Klasse hat Vorfahren von> Object> Kernel> BasicObject und jedes Modul in Ruby hat dieselben Vorfahren. Wie genau fügt Ruby allen Ruby-Klassen / Modulen Methoden hinzu, wenn jemand die Modul-Klasse erneut öffnet?
Meine zweite Frage ist .. Ich verstehe, dass die Delegate-Methode in Rails module_eval verwendet, um die eigentliche Delegation durchzuführen, aber ich verstehe nicht wirklich, wie module_eval funktioniert.
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
if prefix == true && to =~ /^[^a-z_]/
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end
method_prefix = \
if prefix
"#{prefix == true ? to : prefix}_"
else
''
end
file, line = caller.first.split(':', 2)
line = line.to_i
to = to.to_s
to = 'self.class' if to == 'class'
methods.each do |method|
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
# The following generated methods call the target exactly once, storing
# the returned value in a dummy variable.
#
# Reason is twofold: On one hand doing less calls is in general better.
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
if allow_nil
module_eval(<<-EOS, file, line - 3)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
_ = #{to} # _ = client
if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
_.#{method}(#{definition}) # _.name(*args, &block)
end # end
end # end
EOS
else
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
module_eval(<<-EOS, file, line - 2)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
_ = #{to} # _ = client
_.#{method}(#{definition}) # _.name(*args, &block)
rescue NoMethodError => e # rescue NoMethodError => e
if _.nil? && e.name == :#{method} # if _.nil? && e.name == :name
#{exception} # # add helpful message to the exception
else # else
raise # raise
end # end
end # end
EOS
end
end
Ende