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()



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow