Domanda

Ho una classe che contiene alcuni attributi privati. Quello che vorrei fare è quello di aggiungere dinamicamente alcuni setter per questi solo per l'esecuzione di un blocco specifico.

Esempio di ciò che mi piacerebbe essere in grado di:

class Content
  attr_reader :a, :b

  def initialize
    @a = 1
    @b = "plop"
  end

  def set(&block)
    extend(Setter)
    instance_eval(&block)
    unextend(Setter) ????
  end

  module Setter
    def a(value)
      @a = value
    end
    def b(value)
      @b = value
    end
  end
end

content = Content.new
content.set do
  a 2
  b "yeah!"
end
content.a # should return 2

Modifica : Grazie per le grandi risposte finora. Ho chiarito la domanda perché effettivamente bisogno di definire lettori attributi nella classe stessa che potrebbero contrastare con le incastonatori definite nel modulo. Ho dimenticato di questa parte durante la pubblicazione della domanda. (Era tardi ^^)

Chiarimento : Questa classe è destinato ad una DSL per scrivere un file di configurazione. L'iniziativa è rivolta a non-sviluppatori in modo che i meno operatori, meglio è.

Io attualmente implementare questo utilizzando una classe proxy che instance_eval il blocco, ma devo pasticciare con instance_variable_set al fine di impostare i valori e non mi piace. Sto solo cercando un altro modo di vedere se riesco a fare il mio codice più leggibile.

È stato utile?

Soluzione

  def set(&block)
    extend(Setter)
    instance_eval(&block)
    Setter.instance_methods.each do |m| 
      instance_eval "undef #{m}"
    end
  end

Non so di alcun metodo che farlo per voi, anche se ci potrebbe essere qualcosa del genere .. Questo dovrebbe fare il lavoro, però, trovando tutti i metodi di Setter istanza e indefiniti nei contenuti.

Altri suggerimenti

Non c'è modo nativo a moduli "annullare l'estensione" in Ruby. Il mixology gioiello implementa questo schema come estensione di C (e Java, per JRuby), creando mixin e metodi unmix. Sembra potrebbe essere necessario applicare un se avete bisogno di supporto Ruby 1.9 tuttavia,.

Se si preferisce evitare l'uso di librerie di terze parti, un altro approccio potrebbe essere semplicemente quello di rendere i setter privato:

class Content
  def initialize
    @a = 1
    @b = "plop"
  end

  def set(&block)
    instance_eval(&block)
  end

  private

  def a(val)
    @a = val
  end

  def b(val)
    @b = val
  end 

end

content = Content.new

#This will succeed
content.set do
  a 2
  b "yeah!"
end

# This will raise a NoMethodError, as it attempts to call a private method
content.a 3

Si potrebbe utilizzare biblioteca mixico di _why (disponibile su GitHub )

E 'avrebbe permesso di fare questo:

require 'mixology'
#...
def set(&block)
  Setter.mix_eval(Setter, &block)
end

Il gemma mixology fa più o meno la stessa cosa, solo un po 'diverso.

se ti senti in uno stato d'animo sperimentale anche verificare: dup_eval

E 'simile per certi versi a mixico ma con alcuni extra interessanti ( object2module )

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top