Ruby Language
metaprogrammazione
Ricerca…
introduzione
La metaprogrammazione può essere descritta in due modi:
"Programmi per computer che scrivono o manipolano altri programmi (o se stessi) come dati, o che fanno parte del lavoro in fase di compilazione che altrimenti verrebbero eseguiti in fase di esecuzione".
Più semplicemente: Metaprogramming sta scrivendo un codice che scrive codice durante il runtime per semplificarti la vita .
Implementare "con" usando la valutazione dell'istanza
Molte lingue sono dotate di with
dichiarazione che consente ai programmatori di omettere il ricevitore di chiamate di metodo.
with
può essere facilmente emulato in Ruby usando instance_eval
:
def with(object, &block)
object.instance_eval &block
end
Il metodo with
può essere utilizzato per eseguire senza problemi metodi sugli oggetti:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Definizione dei metodi in modo dinamico
Con Ruby puoi modificare la struttura del programma in tempo di esecuzione. Un modo per farlo è definire i metodi in modo dinamico usando il metodo method_missing
.
Diciamo che vogliamo essere in grado di verificare se un numero è maggiore di un altro numero con la sintassi 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
Una cosa importante da ricordare quando si usa method_missing
anche quello di sovrascrivere respond_to?
metodo:
class Numeric
def respond_to?(method_name, include_all = false)
method_name.to_s.match(/^is_greater_than_(\d+)\?$/) || super
end
end
Dimenticare di farlo porta a una situazione incoerente, quando è possibile chiamare correttamente 600.is_greater_than_123
, ma 600.respond_to(:is_greater_than_123)
restituisce false.
Definizione dei metodi su istanze
In ruby puoi aggiungere metodi a istanze esistenti di qualsiasi classe. Ciò consente di aggiungere un comportamento e un'istanza di una classe senza modificare il comportamento del resto delle istanze di quella 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}
metodo send ()
send()
è usato per passare il messaggio object
. send()
è un metodo di istanza della classe Object
. Il primo argomento di send()
è il messaggio che stai inviando all'oggetto, cioè il nome di un metodo. Potrebbe essere una string
o un symbol
ma i simboli sono preferiti. Quindi gli argomenti che devono passare nel metodo, quelli saranno gli argomenti rimanenti 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.
Ecco l'esempio più descrittivo
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
Nota: send()
stesso non è più raccomandato. Usa __send__()
che ha il potere di chiamare metodi privati o (consigliato) public_send()