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



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow