Search code examples
rubymetaprogrammingdslproc-object

Using procs with Ruby's DSLs


For user convenience and more clean code I would like to write a class that can be used like this:

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

The challenge now is, to have that parameters available in my encode method.

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

This approach does not work. When the Proc is called, the variables are not evaluated in the context of the Theora class. Usually I would like to use method_missing to put every parameter into a class variable of class Theora, but I do not find the right way for an entry.

Can anyone point me into the right direction?


Solution

  • It can't be done the way you've written it, AFAIK. The body of the proc has its own scope, and variables that are created within that scope are not visible outside it.

    The idiomatic approach is to create a configuration object and pass it into the block, which describes the work to be done using methods or attributes of that object. Then those settings are read when doing the work. This is the approach taken by create_table in ActiveRecord migrations, for example.

    So you can do something like this:

    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