class Thor class Option < Argument attr_reader :aliases, :group VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil) super(name, description, required, type, default, banner) @aliases = [*aliases].compact @group = group.to_s.capitalize if group end # This parse quick options given as method_options. It makes several # assumptions, but you can be more specific using the option method. # # parse :foo => "bar" # #=> Option foo with default value bar # # parse [:foo, :baz] => "bar" # #=> Option foo with default value bar and alias :baz # # parse :foo => :required # #=> Required option foo without default value # # parse :foo => 2 # #=> Option foo with default value 2 and type numeric # # parse :foo => :numeric # #=> Option foo without default value and type numeric # # parse :foo => true # #=> Option foo with default value true and type boolean # # The valid types are :boolean, :numeric, :hash, :array and :string. If none # is given a default type is assumed. This default type accepts arguments as # string (--foo=value) or booleans (just --foo). # # By default all options are optional, unless :required is given. # def self.parse(key, value) if key.is_a?(Array) name, *aliases = key else name, aliases = key, [] end name = name.to_s default = value type = case value when Symbol default = nil if VALID_TYPES.include?(value) value elsif required = (value == :required) :string elsif value == :optional # TODO Remove this warning in the future. warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean." :boolean end when TrueClass, FalseClass :boolean when Numeric :numeric when Hash, Array, String value.class.name.downcase.to_sym end self.new(name.to_s, nil, required, type, default, nil, nil, aliases) end def switch_name @switch_name ||= dasherized? ? name : dasherize(name) end def human_name @human_name ||= dasherized? ? undasherize(name) : name end def usage(padding=0) sample = if banner && !banner.to_s.empty? "#{switch_name}=#{banner}" else switch_name end sample = "[#{sample}]" unless required? if aliases.empty? (" " * padding) << sample else "#{aliases.join(', ')}, #{sample}" end end # Allow some type predicates as: boolean?, string? and etc. # def method_missing(method, *args, &block) given = method.to_s.sub(/\?$/, '').to_sym if valid_type?(given) self.type == given else super end end protected def validate! raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? end def valid_type?(type) VALID_TYPES.include?(type.to_sym) end def dasherized? name.index('-') == 0 end def undasherize(str) str.sub(/^-{1,2}/, '') end def dasherize(str) (str.length > 1 ? "--" : "-") + str.gsub('_', '-') end end end