Ruby Language
metaprogrammering
Sök…
Introduktion
Metaprogrammering kan beskrivas på två sätt:
”Datorprogram som skriver eller manipulerar andra program (eller sig själva) som deras data, eller som gör en del av arbetet vid sammanställningstid som annars skulle göras vid körning”.
Mer enkelt uttryckt: Metaprogrammering är att skriva kod som skriver kod under körning för att göra ditt liv enklare .
Implementera "med" med hjälp av instansutvärdering
Många språk har ett with
uttalande som gör det möjligt för programmerare att utelämna mottagaren av metodsamtal.
with
kan enkelt emuleras i Ruby med instance_eval
:
def with(object, &block)
object.instance_eval &block
end
Den with
metoden kan användas för att smidigt köra metoder på objekt:
hash = Hash.new
with hash do
store :key, :value
has_key? :key # => true
values # => [:value]
end
Definiera metoder dynamiskt
Med Ruby kan du ändra programmets struktur under körningstiden. Ett sätt att göra det är genom att definiera metoder dynamiskt med hjälp av metoden method_missing
.
Låt oss säga att vi vill kunna testa om ett nummer är större än annat nummer med syntaxen 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
En viktig sak att komma ihåg när man använder method_missing
att man också bör åsidosätta respond_to?
metod:
class Numeric
def respond_to?(method_name, include_all = false)
method_name.to_s.match(/^is_greater_than_(\d+)\?$/) || super
end
end
Att glömma att göra det leder till en inkonsekvent situation, när du framgångsrikt kan ringa 600.is_greater_than_123
, men 600.respond_to(:is_greater_than_123)
returnerar falskt.
Definiera metoder för instanser
I rubin kan du lägga till metoder i befintliga instanser av valfri klass. Detta tillåter dig att lägga till beteende till och instans för en klass utan att ändra beteendet hos resten av instanserna i klassen.
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}
skicka () metod
send()
används för att skicka meddelande till object
. send()
är en instansmetod för Object
. Det första argumentet som send()
är meddelandet som du skickar till objektet - det vill säga namnet på en metod. Det kan vara string
eller symbol
men symboler föredras. Sedan kommer de argument som måste skickas in i metoden, de kommer att vara de återstående argumenten som 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.
Här är det mer beskrivande exemplet
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
Obs: send()
själv rekommenderas inte längre. Använd __send__()
som har makten att kalla privata metoder, eller (rekommenderas) public_send()