Ruby Language
Métaprogrammation
Recherche…
Introduction
La métaprogrammation peut être décrite de deux manières:
"Les programmes informatiques qui écrivent ou manipulent d'autres programmes (ou eux-mêmes) en tant que leurs données, ou qui effectuent une partie du travail au moment de la compilation qui serait effectuée au moment de l'exécution".
Plus simplement: la métaprogrammation consiste à écrire du code qui écrit du code pendant l'exécution pour vous simplifier la vie .
Implémenter "avec" en utilisant l'évaluation d'instance
De nombreux langages disposent d'une instruction with
qui permet aux programmeurs d'omettre le récepteur d'appels de méthode.
with
peut être facilement émulé dans Ruby en utilisant instance_eval
:
def with(object, &block)
object.instance_eval &block
end
La méthode with
peut être utilisée pour exécuter de manière transparente des méthodes sur des objets:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Définition dynamique de méthodes
Avec Ruby, vous pouvez modifier la structure du programme en temps d'exécution. Une façon de le faire est de définir des méthodes dynamiquement en utilisant la méthode method_missing
.
Disons que nous voulons pouvoir tester si un nombre est supérieur à un autre nombre avec la syntaxe 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
Une chose importante à retenir lors de l’utilisation de method_missing
que vous method_missing
également remplacer respond_to?
méthode:
class Numeric
def respond_to?(method_name, include_all = false)
method_name.to_s.match(/^is_greater_than_(\d+)\?$/) || super
end
end
Oublier de le faire conduit à une situation incohérente lorsque vous pouvez appeler 600.is_greater_than_123
avec succès, mais que 600.respond_to(:is_greater_than_123)
renvoie false.
Définir des méthodes sur des instances
Dans Ruby, vous pouvez ajouter des méthodes aux instances existantes de n'importe quelle classe. Cela vous permet d'ajouter un comportement et une instance à une classe sans modifier le comportement des autres instances de cette classe.
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}
méthode send ()
send()
est utilisé pour transmettre un message à l' object
. send()
est une méthode d'instance de la classe Object
. Le premier argument dans send()
est le message que vous envoyez à l'objet, c'est-à-dire le nom d'une méthode. Il peut s'agir d'une string
ou d'un symbol
mais les symboles sont préférés. Ensuite, les arguments doivent passer en méthode, ce seront les arguments restants dans 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.
Voici l'exemple plus descriptif
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
Remarque: send()
n'est plus recommandé. Utilisez __send__()
qui a le pouvoir d'appeler des méthodes privées, ou (recommandé) public_send()