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



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow