Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Ruby Diary #12: Ruby attr_accessor dinamici

Come creare dinamicamente i metodi setter e getter per l'accesso alle variabili d'istanza di un oggetto.
Come creare dinamicamente i metodi setter e getter per l'accesso alle variabili d'istanza di un oggetto.
Link copiato negli appunti

Ho un problema (il primo che mi risponde "solo uno" lo banno). Ora che ho la mia classe astratta contenente i campi di default dell'oggetto Result, devo permettere a ProductResult di ereditare le variabili d'istanza da Result e fonderle con quelle proprie di ProductResult.

Poiché in Ruby, di default, ogni variabile d'istanza è privata ho necessità  di dichiarare anche i metodi getter e setter per ogni proprietà . In poche parole, immaginando che ProductResult contenga le seguenti proprietà 

  1. id (ereditata da Result)
  2. url (ereditata da Result)
  3. name
  4. price
  5. image

non posso permettermi di dichiarare dinamicamente gli attributi usando attr_accessor all'interno di un metodo. Il seguente codice non è consentito.

class ProductResult < Result
    def initialize()
        @fields = %w(name price image)
        # no!
        @fields.each do |field|
            attr_accessor field.to_sym
        end
    end
end

Come posso allora fare in modo che sia un metodo ad incaricarsi di generare automaticamente questi metodi accessori? Ci sono almeno due soluzioni per risolvere questo quesito di metaprogrammazione.

La prima consiste nello "scrivere" i metodi in una stringa e poi richiamare class_eval per valutare il contenuto della stringa come codice Ruby.

La seconda alternativa, quella che prendo in esame in questo post, è quella di sfruttare il nostro buon vecchio metodo Kernel#method_missing per intercettare la chiamata al metodo e magicamente restituire la proprietà  quando disponibile.

Come fare? Ecco qui, vi regalo un metodo fresco fresco contenuto nella famosa raccolta di cui vi parlavo nel mio post precedente. Lo potete copiare in una classe o inserirlo in un modulo e "mixarlo" in una libreria esistente.

#
# Intercept missing methods.
#
# This is a convenient method to emulate dynamic attribute accessors.
# Kernel#method_missing is a magic mathod automatically invoked whenever
# a missing method is called.
# A missing method is a method that hasn't been defined before.
#
#   class Foo
#     def inititialize()
#       // do something
#     end
#   end
#
#   foo = Foo.new()
#   foo.this_is_a_missing_method()  #=> NoMethodError,
#                                   #   this is a missing method
#
# Using the Kernel#missing_method method you can intercept any missing method
# and create your custom action, including the ability to emulate
# any attr_reader and attr_writer methods dynamically.
#
#
# === Arguments:
# [+String+ _name_] The name of the missing method
# [+Mixed+ _args_]  Missing methd arguments
#
# === Return:
# +void+
#
def method_missing(name, *args)
  attr_name = name.to_s.split(/=/).first.to_sym()
  instance_variable_name = "@#{attr_name}"
  # perform attr_writer if argument given
  unless args.empty?
    instance_variable_set(instance_variable_name, args.first)
  end
  # try to return value or forward to parent class
  instance_variables.include?(instance_variable_name) ?
    instance_variable_get(instance_variable_name) : super
end

A proposito. A questo punto, dovreste già  avere la soluzione al quesito precedente. Se così non fosse, sappiate che la libreria di Rails che si basa proprio su method_missing e su una implementazione simile a quanto sopra descritto è ActiveRecord.

Ti consigliamo anche