Ruby Language
metaprogramming
Zoeken…
Invoering
Metaprogrammering kan op twee manieren worden beschreven:
“Computerprogramma's die andere programma's (of zichzelf) als hun gegevens schrijven of manipuleren, of die tijdens het compileren een deel van het werk doen dat anders tijdens runtime zou worden gedaan”.
Simpel gezegd: metaprogrammering is code schrijven die code schrijft tijdens runtime om uw leven gemakkelijker te maken .
Implementeren van "met" met behulp van instantie-evaluatie
Veel talen hebben een with
statement waarmee programmeurs de ontvanger van methodeaanroepen kunnen weglaten.
with
kan eenvoudig worden geëmuleerd in Ruby met instance_eval
:
def with(object, &block)
object.instance_eval &block
end
De with
werkwijze kan worden toegepast op werkwijzen naadloos voeren op objecten:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Methoden dynamisch definiëren
Met Ruby kunt u de structuur van het programma in uitvoeringstijd wijzigen. Een manier om dit te doen, is door methoden dynamisch te definiëren met de methode method_missing
.
Laten we zeggen dat we willen kunnen testen of een getal groter is dan een ander getal met de syntaxis 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
Een belangrijk ding om te onthouden bij het gebruik van method_missing
dat men ook respond_to?
moet overschrijven 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
Als u dit vergeet, leidt dit tot een inconsistente situatie waarin u met succes 600.is_greater_than_123
kunt bellen, maar 600.respond_to(:is_greater_than_123)
resultaat false.
Methoden definiëren voor instanties
In ruby kunt u methoden toevoegen aan bestaande instanties van elke klasse. Hiermee kunt u gedrag toevoegen aan en een instantie van een klasse zonder het gedrag van de rest van de instanties van die klasse te wijzigen.
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()
wordt gebruikt om bericht door te geven aan object
. send()
is een instantiemethode van de klasse Object
. Het eerste argument in send()
is het bericht dat u naar het object verzendt, dat wil zeggen de naam van een methode. Het kan een string
of symbol
maar symbolen hebben de voorkeur. Vervolgens argumenten die moeten worden doorgegeven, dat zijn de resterende argumenten 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 is het meer beschrijvende voorbeeld
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
Opmerking: send()
zelf wordt niet meer aanbevolen. Gebruik __send__()
die de mogelijkheid heeft om private methoden aan te roepen, of (aanbevolen) public_send()