Domanda

Per una maggiore comodità d'uso e codice più pulito Vorrei scrivere una classe che può essere utilizzato in questo modo:

Encoder::Theora.encode do
  infile = "path/to/infile"
  outfile = "path/to/outfile"
  passes = 2
  # ... more params
end

La sfida è ora, per avere quel parametri disponibili nel mio metodo di codifica.

module Encoder
  class Theora
    def self.encode(&proc)
      proc.call
      # do some fancy encoding stuff here
      # using the parameters from the proc
    end
  end
end

Questo approccio non funziona. Quando il Proc si chiama, le variabili non sono valutati nel contesto della classe Theora. Di solito Vorrei utilizzare method_missing per mettere tutti i parametri in una variabile di classe di classe Theora, ma io non trovare la strada giusta per una voce.

Qualcuno mi può punto nella giusta direzione?

È stato utile?

Soluzione

Non può essere fatto nel modo che hai scritto, per quanto ne so. Il corpo del proc ha il suo campo di applicazione, e le variabili che si creano all'interno di tale portata non sono al di fuori visibile esso.

L'approccio idiomatica consiste nel creare un oggetto di configurazione e passarlo nel blocco, che descrive il lavoro da svolgere con metodi o attributi di quell'oggetto. Poi quelle impostazioni vengono lette quando si fa il lavoro. Questo è l'approccio adottato dal create_table nelle migrazioni ActiveRecord, per esempio.

Così si può fare qualcosa di simile:

module Encoder
  class Theora
    Config = Struct.new(:infile, :outfile, :passes)

    def self.encode(&proc)
      config = Config.new
      proc.call(config)
      # use the config settings here
      fp = File.open(config.infile)       # for example
      # ...
    end
  end
end

# then use the method like this:
Encoder::Theora.encode do |config|
  config.infile = "path/to/infile"
  config.outfile = "path/to/outfile"
  config.passes = 2
  # ...
end

Altri suggerimenti

Non sono sicuro che sia possibile ottenere il DSL per l'assegnazione uso, penso che l'interprete Ruby sempre per scontato che infile in infile = 'path/to/something' è una variabile locale in quel contesto (ma self.infile = 'path/to/something' può essere fatto per il lavoro). Tuttavia, se si può vivere senza quel particolare dettaglio, è possibile implementare il DSL in questo modo:

module Encoder
  class Theora
    def self.encode(&block)
      instance = new
      instance.instance_eval(&block)
      instance
    end

    def infile(path=nil)
      @infile = path if path
      @infile
    end
  end
end

e usarlo in questo modo:

Encoder::Theora.encode do
  infile 'path/somewhere'
end

(attuare le altre proprietà similmente).

In giocando con questo sono arrivato alla seguente, che io non consiglio necessariamente, e che non del tutto adatta la sintassi richiesta, ma che non consentono di utilizzare l'assegnazione (o quasi). Così peruse nello spirito di completezza:

module Encoder
  class Theora
    def self.encode(&proc)
      infile = nil
      outfile = nil
      yield binding
    end
  end
end

Encoder::Theora.encode do |b|
  b.eval <<-ruby
    infile = "path/to/infile"
    outfile = "path/to/outfile"
  ruby
end

Credo Binding.eval funziona solo in Ruby 1.9. Inoltre, sembra che le variabili locali devono essere dichiarate prima di cedere o non funzionerà -? Qualcuno sa perché

OK, in primo luogo devo dire che la risposta del pmdboi è molto elegante e quasi certamente quella giusta.

Ancora, nel caso in cui si vuole un super DSL cut-down come

Encoder::Theora.encode do
  infile "path/to/infile"
  outfile "path/to/outfile"
  passes 2
end

Si può fare qualcosa di brutto in questo modo:

require 'blockenspiel'
module Encoder
  class Theora
    # this replaces pmdboi's elegant Struct
    class Config
      include Blockenspiel::DSL
      def method_missing(method_id, *args, &blk)
        if args.length == 1
          instance_variable_set :"@#{method_id}", args[0]
        else
          instance_variable_get :"@#{method_id}"
        end
      end
    end

    def self.encode(&blk)
      config = Config.new
      Blockenspiel.invoke blk, config
      # now you can do things like
      puts config.infile
      puts config.outfile
      puts config.passes
    end
  end
end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top