Ruby Language
Metaprogrammierung
Suche…
Einführung
Metaprogrammierung kann auf zwei Arten beschrieben werden:
"Computerprogramme, die andere Programme (oder sich selbst) als ihre Daten schreiben oder bearbeiten oder zur Kompilierzeit einen Teil der Arbeit erledigen, der sonst zur Laufzeit erledigt würde"
Einfacher ausgedrückt: Metaprogrammierung schreibt Code, der zur Laufzeit Code schreibt, um Ihnen das Leben zu erleichtern .
"Mit" mit Instanzbewertung implementieren
In vielen Sprachen gibt es eine with
Anweisung, mit der Programmierer den Empfänger von Methodenaufrufen auslassen können.
with
kann in Ruby mit instance_eval
leicht emuliert werden:
def with(object, &block)
object.instance_eval &block
end
Die with
Methode kann verwendet werden, um Methoden nahtlos an Objekten auszuführen:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Methoden dynamisch definieren
Mit Ruby können Sie die Programmstruktur während der Ausführung ändern. Eine Möglichkeit besteht darin, Methoden dynamisch mit der Methode method_missing
.
Nehmen wir an, wir wollen mit der Syntax 777.is_greater_than_123?
testen können, ob eine Zahl größer als eine andere Zahl ist 777.is_greater_than_123?
.
# open Numeric class
class Numeric
# override `method_missing`
def method_missing(method_name,*args)
# test if the method_name matches the syntax we want
if method_name.to_s.match /^is_greater_than_(\d+)\?$/
# capture the number in the method_name
the_other_number = $1.to_i
# return whether the number is greater than the other number or not
self > the_other_number
else
# if the method_name doesn't match what we want, let the previous definition of `method_missing` handle it
super
end
end
end
Eine wichtige Sache, die Sie bei der Verwendung von method_missing
beachten sollten, damit auch respond_to?
überschrieben respond_to?
Methode:
class Numeric
def respond_to?(method_name, include_all = false)
method_name.to_s.match(/^is_greater_than_(\d+)\?$/) || super
end
end
Wenn Sie dies 600.is_greater_than_123
, führt dies zu einer inkonsistenten Situation, wenn Sie erfolgreich 600.is_greater_than_123
aufrufen können. 600.is_greater_than_123
, aber 600.respond_to(:is_greater_than_123)
gibt false zurück.
Methoden für Instanzen definieren
In Ruby können Sie vorhandenen Instanzen einer beliebigen Klasse Methoden hinzufügen. Auf diese Weise können Sie einer Klasse Verhalten hinzufügen und eine Instanz hinzufügen, ohne das Verhalten der übrigen Instanzen dieser Klasse zu ändern.
class Example
def method1(foo)
puts foo
end
end
#defines method2 on object exp
exp = Example.new
exp.define_method(:method2) {puts "Method2"}
#with method parameters
exp.define_method(:method3) {|name| puts name}
send () -Methode
send()
wird verwendet, um eine Nachricht an ein object
. send()
ist eine Instanzmethode der Object
Klasse. Das erste Argument in send()
ist die Nachricht, die Sie an das Objekt senden, dh den Namen einer Methode. Es kann sich um einen string
oder ein symbol
aber Symbole werden bevorzugt. Dann Argumente, die in method übergeben werden müssen, das sind die restlichen Argumente in send()
.
class Hello
def hello(*args)
puts 'Hello ' + args.join(' ')
end
end
h = Hello.new
h.send :hello, 'gentle', 'readers' #=> "Hello gentle readers"
# h.send(:hello, 'gentle', 'readers') #=> Here :hello is method and rest are the arguments to method.
Hier ist das anschaulichere Beispiel
class Account
attr_accessor :name, :email, :notes, :address
def assign_values(values)
values.each_key do |k, v|
# How send method would look a like
# self.name = value[k]
self.send("#{k}=", values[k])
end
end
end
user_info = {
name: 'Matt',
email: '[email protected]',
address: '132 random st.',
notes: "annoying customer"
}
account = Account.new
If attributes gets increase then we would messup the code
#--------- Bad way --------------
account.name = user_info[:name]
account.address = user_info[:address]
account.email = user_info[:email]
account.notes = user_info[:notes]
# --------- Meta Programing way --------------
account.assign_values(user_info) # With single line we can assign n number of attributes
puts account.inspect
Hinweis: send()
selbst wird nicht mehr empfohlen. Verwenden Sie __send__()
, mit dem private Methoden aufgerufen werden können, oder (empfohlen) public_send()