I have an option parsing gem that provides a DSL for creating commands. In practice, it often ends up looking like this:
option :ints, Integer, arity: [1, -1], on_multiple: :append
option :floats, Float, arity: [1, -1], on_multiple: :append
option :complex, Complex, arity: [1, -1], on_multiple: :append
These are class methods. Not very DRY, as you can see. It would be better to write something like this:
scope arity: [1, -1], on_multiple: :append do
option :ints, Integer
option :floats, Float
option :complex, Complex
end
To have the options hash given to scope transparently merged with the one given to option. That’s where I’m stuck. I’m not sure where to store the common options so that I can merge them later.
Any ideas?
option forwards everything to Option#new:
def option(*args, &block)
# self is a class that represents a command
self.options << Option.new(*args, &block)
end
As requested, here’s the code, with usage of support gems removed:
def initialize(key, *args, &block)
# Retrieve the options hash from the argument array.
options = args.last.is_a?(Hash) ? args.pop : {}
# The rest of the implementation...
type = args.find { |arg| arg.is_a? Module }
strings = args.flatten.select do |arg|
arg.is_a? String
end.group_by do |arg|
arg =~ Parser::Regexp::SWITCH ? :switches : :description
end
self.key = key
self.names = strings.fetch(:switches) { [ Option.name_from(key) ] }
self.description = options.fetch :description, strings.fetch(:description, []).first
self.on_multiple = options.fetch :on_multiple, :replace
self.arity = options.fetch :arity, nil
self.default = options.fetch :default, nil
self.required = options.fetch :required, false
self.type = type || String
self.handler = block
end
Take a look at Object#with_options from Rails, and swipe that for your own use.