How do I defer execution of some Ruby code until later and run it on demand in this scenario?

Posted by Kyle Kaitan on Stack Overflow See other posts from Stack Overflow or by Kyle Kaitan
Published on 2010-06-15T04:27:38Z Indexed on 2010/06/15 4:32 UTC
Read the original article Hit count: 139

Filed under:
|

I've got some code that looks like the following. First, there's a simple Parser class for parsing command-line arguments with options.

class Parser
  def initialize(&b); ...; end      # Create new parser.
  def parse(args = ARGV); ...; end  # Consume command-line args.
  def opt(...); ...; end            # Declare supported option.
  def die(...); ...; end            # Validation handler.
end

Then I have my own Parsers module which holds some metadata about parsers that I want to track.

module Parsers
  ParserMap = {}
  def self.make_parser(kind, desc, &b)
    b ||= lambda {}
    module_eval {
      ParserMap[kind] = {:desc => "", :validation => lambda {} }
      ParserMap[kind][:desc] = desc

      # Create new parser identified by `<Kind>Parser`. Making a Parser is very
      #   expensive, so we defer its creation until it's actually needed later
      #   by wrapping it in a lambda and calling it when we actually need it.
      const_set(name_for_parser(kind), lambda { Parser.new(&b) })
    }
  end

  # ...
end

Now when you want to add a new parser, you can call make_parser like so:

make_parser :db, "login to database" do
  # Options that this parser knows how to parse.
  opt :verbose, "be verbose with output messages"
  opt :uid, "user id"
  opt :pwd, "password"
end

Cool. But there's a problem. We want to optionally associate validation with each parser, so that we can write something like:

validation = lambda { |parser, opts|
  parser.die unless opts[:uid] && opts[:pwd]  # Must provide login.
}

The interface contract with Parser says that we can't do any validation until after Parser#parse has been called. So, we want to do the following:

  • Associate an optional block with every Parser we make with make_parser.
  • We also want to be able to run this block, ideally as a new method called Parser#validate. But any on-demand method is equally suitable.

How do we do that?

© Stack Overflow or respective owner

Related posts about ruby

Related posts about metaprogramming