Module: Rubycom::ArgParse

Defined in:
lib/rubycom/arg_parse.rb

Defined Under Namespace

Classes: ArgParser, ArgTransform

Class Method Summary collapse

Class Method Details

.combine_options(command_line) ⇒ Array

Matches a word representing an optional key to the separator and/or value which goes with the key

Parameters:

  • command_line (Array)

    an array of strings representing the arguments taken from the command line

Returns:

  • (Array)

    an Array of Strings with matched items combined into one entry per matched set



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/rubycom/arg_parse.rb', line 63

def self.combine_options(command_line)
  command_line.reduce([]) { |acc, next_word|
    if next_word == '=' || (!next_word.start_with?('-') && acc.last.to_s.start_with?('-') && acc.last.to_s.end_with?('='))
      acc[-1] = acc[-1].dup << next_word
      acc
    elsif next_word == '=' || (!next_word.start_with?('-') && acc.last.to_s.start_with?('-') && !(acc.last.to_s.include?('=') || acc.last.to_s.include?(' ')))
      acc[-1] = acc[-1].dup << ' ' << next_word
      acc
    else
      acc << next_word
    end
  }
end

.load_flag_value(flag) ⇒ Boolean

Resolves the given flag to true or false as appropriate.

Parameters:

  • flag (String)

    string representing a flag without the proceeding dashes

Returns:

  • (Boolean)

    FalseClass if the flag starts with a no- TrueClass otherwise



247
248
249
# File 'lib/rubycom/arg_parse.rb', line 247

def self.load_flag_value(flag)
  (flag.start_with?('no-') || flag.start_with?('NO-')) ? false : true
end

.load_opt_value(value, loader = Rubycom::ArgParse.public_method(:load_string)) ⇒ Object

Calls the given loader to load a single string or array of strings

Parameters:

  • value (Array)

    containing the string(s) to be loaded

  • loader (Method|Proc) (defaults to: Rubycom::ArgParse.public_method(:load_string))

    called to load the value(s)

Returns:

  • (Object)

    the result of a call to #load_string



186
187
188
189
190
191
192
# File 'lib/rubycom/arg_parse.rb', line 186

def self.load_opt_value(value, loader=Rubycom::ArgParse.public_method(:load_string))
  if value.class == Array
    (value.length == 1) ? loader.call(value.first) : value.map { |v| loader.call(v) }
  else
    loader.call(value)
  end
end

.load_string(string, loader = YAML.public_method(:load)) ⇒ Object

Uses the given loader to resolve the ruby type for the given string

Parameters:

  • string (String)

    to be loaded

  • loader (Method|Proc) (defaults to: YAML.public_method(:load))

    called to load the string

Returns:

  • (Object)

    the result of a call to the loader or the given string if it could not be parsed



199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/rubycom/arg_parse.rb', line 199

def self.load_string(string, loader=YAML.public_method(:load))
  if string.start_with?('#') || string.start_with?('!')
    result = string
  else
    begin
      result = loader.call(string)
    rescue Exception
      result = string
    end
  end
  result
end

.parse_command_line(command_line) ⇒ Hash

Runs a parser against the given Array of arguments to match command argument, option, and flag patterns.

Parameters:

  • command_line (Array)

    an array of strings representing the arguments taken from the command line

Returns:

  • (Hash)

    :args => Array of arguments, :opts => Hash mapping each unique option/flag to their values

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/rubycom/arg_parse.rb', line 10

def self.parse_command_line(command_line)
  raise ArgumentError, "command_line should be String or Array but was #{command_line.class}" unless [String, Array].include?(command_line.class)
  command_line = command_line.dup
  command_line = [command_line] if command_line.class == String
  command_line = self.combine_options(command_line)
  begin
    command_line.map { |word|
      ArgTransform.new.apply(ArgParser.new.parse(word))
    }.reduce({}) { |acc, n|
      # the handlers for opt and flag accumulate all unique mentions of an option name
      if n.has_key?(:opt)
        acc[:opts] = {} unless acc.has_key?(:opts)
        acc[:opts] = acc[:opts].update(n[:opt]) { |key, old, new|
          if old.class == Array
            acc[:opts][key] = old << new
          else
            acc[:opts][key] = [old] << new
          end
        }
      elsif n.has_key?(:flag)
        acc[:opts] = {} unless acc.has_key?(:opts)
        acc[:opts] = acc[:opts].update(n[:flag]) { |key, old, new|
          if old.class == Array
            combined = old
          else
            combined = [old]
          end

          if new.class == Array
            new.each { |new_flag|
              combined << new_flag
            }
          else
            combined << new
          end

          acc[:opts][key] = combined
        }
      else
        acc[:args] = [] unless acc.has_key?(:args)
        acc[:args] << n[:arg]
      end
      acc
    }
  rescue Parslet::ParseFailed => failure
    raise ArgParseError, "Arguments could not be parsed.", failure
  end
end

.transform(matched_type, val, loaders = {}, transformers = {}) ⇒ Hash

Calls one of the transform functions according to the matched_type

anything else will extract the value as a string it matches a complex pattern, a Slice will be returned when the matched pattern is not tree like.

Parameters:

  • matched_type (Symbol)

    :arg, :opt, :flag will transform the value according to the corresponding transform function.

  • val (Hash|Parslet::Slice)

    a possibly nested Hash structure or a Slice. Hashes are returned by the parser when

Returns:

  • (Hash)

    :arg => value | :opt|:flag => a Hash mapping keys to values



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/rubycom/arg_parse.rb', line 136

def self.transform(matched_type, val, loaders={}, transformers={})
  loader_methods = {
      arg: Rubycom::ArgParse.public_method(:load_string),
      opt: Rubycom::ArgParse.public_method(:load_opt_value),
      flag: Rubycom::ArgParse.public_method(:load_flag_value)
  }.merge(loaders)
  transforms = {
      arg: Rubycom::ArgParse.public_method(:transform_arg),
      opt: Rubycom::ArgParse.public_method(:transform_opt),
      flag: Rubycom::ArgParse.public_method(:transform_flag)
  }.merge(transformers)

  {
      matched_type => if [:arg,:opt,:flag].include?(matched_type)
                        transforms[matched_type].call(val, loader_methods[matched_type])
                      else
                        val.str.strip
                      end
  }
end

.transform_arg(match_string, arg_loader = Rubycom::ArgParse.public_method(:load_string)) ⇒ Object

Uses the given arg_loader to resolve the ruby type for the given string

Parameters:

  • match_string (String|Parslet::Slice)

    a string identified as an argument

  • arg_loader (Method|Proc) (defaults to: Rubycom::ArgParse.public_method(:load_string))

    called to load the value(s)

Returns:

  • (Object)

    the result of a call to the given arg_loader



162
163
164
165
# File 'lib/rubycom/arg_parse.rb', line 162

def self.transform_arg(match_string, arg_loader=Rubycom::ArgParse.public_method(:load_string))
  match_string = match_string.str.strip if match_string.class == Parslet::Slice
  arg_loader.call(match_string)
end

.transform_flag(match_string, loader = Rubycom::ArgParse.public_method(:load_flag_value)) ⇒ Hash

Resolves the type and values for the given flag string

Parameters:

  • match_string (String|Parslet::Slice)

    a string identified as a flag, should start with a - or – and contain no spaces

Returns:

  • (Hash)

    flag_key(s) => true|false | an array of true|false if there were multiple mentions of the same short flag key



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/rubycom/arg_parse.rb', line 216

def self.transform_flag(match_string, loader=Rubycom::ArgParse.public_method(:load_flag_value))
  match_string = match_string.str.strip if match_string.class == Parslet::Slice
  if match_string.start_with?('--')
    long_flag = match_string.reverse.chomp('-').chomp('-').reverse
    long_flag_key = long_flag.sub(/no-|NO-/, '')
    {
        long_flag_key => loader.call(long_flag)
    }
  else
    short_flag = match_string.reverse.chomp('-').reverse
    short_flag_key = short_flag.sub(/no-|NO-/, '')
    short_flag_key.split(//).map { |k|
      {
          k => loader.call(short_flag)
      }
    }.reduce({}) { |acc, n|
      acc.update(n) { |_, old, new|
        if old.class == Array
          old << new
        else
          [old] << new
        end
      }
    }
  end
end

.transform_opt(subtree, opt_loader = Rubycom::ArgParse.public_method(:load_opt_value)) ⇒ Hash

Uses the given opt_loader to resolve the ruby type for the value in the given Hash

Parameters:

  • subtree (Hash)

    a structure identified as an option, must have keys :key, :sep, :val

  • opt_loader (Method|Proc) (defaults to: Rubycom::ArgParse.public_method(:load_opt_value))

    called to load the option value(s)

Returns:

  • (Hash)

    mapping the option key to it’s loaded value



172
173
174
175
176
177
178
179
# File 'lib/rubycom/arg_parse.rb', line 172

def self.transform_opt(subtree, opt_loader=Rubycom::ArgParse.public_method(:load_opt_value))
  val = subtree[:val].str
  val = val.split(',') unless (val.start_with?('[') && val.end_with?(']'))
  value = opt_loader.call(val)
  {
      subtree[:key].str.reverse.chomp('-').chomp('-').reverse => value
  }
end