Class: Cri::OptionParser

Inherits:
Object
  • Object
show all
Defined in:
lib/cri/option_parser.rb

Overview

Cri::OptionParser is used for parsing commandline options.

Option definitions are hashes with the keys ‘:short`, `:long` and `:argument` (optionally `:description` but this is not used by the option parser, only by the help generator). `:short` is the short, one-character option, without the `-` prefix. `:long` is the long, multi-character option, without the `–` prefix. `:argument` can be :required (if an argument should be provided to the option), :optional (if an argument may be provided) or :forbidden (if an argument should not be provided).

A sample array of definition hashes could look like this:

[
  { :short => 'a', :long => 'all',  :argument => :forbidden },
  { :short => 'p', :long => 'port', :argument => :required  },
]

For example, the following commandline options (which should not be passed as a string, but as an array of strings):

foo -xyz -a hiss -s -m please --level 50 --father=ani -n luke squeak

with the following option definitions:

[
  { :short => 'x', :long => 'xxx',    :argument => :forbidden },
  { :short => 'y', :long => 'yyy',    :argument => :forbidden },
  { :short => 'z', :long => 'zzz',    :argument => :forbidden },
  { :short => 'a', :long => 'all',    :argument => :forbidden },
  { :short => 's', :long => 'stuff',  :argument => :optional  },
  { :short => 'm', :long => 'more',   :argument => :optional  },
  { :short => 'l', :long => 'level',  :argument => :required  },
  { :short => 'f', :long => 'father', :argument => :required  },
  { :short => 'n', :long => 'name',   :argument => :required  }
]

will be translated into:

{
  :arguments => [ 'foo', 'hiss', 'squeak' ],
  :options => {
    :xxx    => true,
    :yyy    => true,
    :zzz    => true,
    :all    => true,
    :stuff  => true,
    :more   => 'please',
    :level  => '50',
    :father => 'ani',
    :name   => 'luke'
  }
}

Defined Under Namespace

Classes: IllegalOptionError, OptionRequiresAnArgumentError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(arguments_and_options, definitions) ⇒ OptionParser

Creates a new parser with the given options/arguments and definitions.

Parameters:

  • arguments_and_options (Array<String>)

    An array containing the commandline arguments (will probably be ‘ARGS` for a root command)

  • definitions (Array<Hash>)

    An array of option definitions



121
122
123
124
125
126
127
128
129
130
# File 'lib/cri/option_parser.rb', line 121

def initialize(arguments_and_options, definitions)
  @unprocessed_arguments_and_options = arguments_and_options.dup
  @definitions = definitions

  @options   = {}
  @arguments = []

  @running = false
  @no_more_options = false
end

Instance Attribute Details

#argumentsArray (readonly)

The arguments that have already been parsed.

If the parser was stopped before it finished, this will not contain all options and ‘unprocessed_arguments_and_options` will contain what is left to be processed.

Returns:

  • (Array)

    The already parsed arguments.



94
95
96
# File 'lib/cri/option_parser.rb', line 94

def arguments
  @arguments
end

#delegate#option_added, #argument_added

The delegate to which events will be sent. The following methods will be send to the delegate:

  • ‘option_added(key, value, cmd)`

  • ‘argument_added(argument, cmd)`

Returns:

  • (#option_added, #argument_added)

    The delegate



76
77
78
# File 'lib/cri/option_parser.rb', line 76

def delegate
  @delegate
end

#optionsHash (readonly)

The options that have already been parsed.

If the parser was stopped before it finished, this will not contain all options and ‘unprocessed_arguments_and_options` will contain what is left to be processed.

Returns:

  • (Hash)

    The already parsed options.



85
86
87
# File 'lib/cri/option_parser.rb', line 85

def options
  @options
end

#unprocessed_arguments_and_optionsArray (readonly)

The options and arguments that have not yet been processed. If the parser wasn’t stopped (using #stop), this list will be empty.

Returns:

  • (Array)

    The not yet parsed options and arguments.



100
101
102
# File 'lib/cri/option_parser.rb', line 100

def unprocessed_arguments_and_options
  @unprocessed_arguments_and_options
end

Class Method Details

.parse(arguments_and_options, definitions) ⇒ Cri::OptionParser

Parses the commandline arguments. See the instance ‘parse` method for details.

Parameters:

  • arguments_and_options (Array<String>)

    An array containing the commandline arguments (will probably be ‘ARGS` for a root command)

  • definitions (Array<Hash>)

    An array of option definitions

Returns:



111
112
113
# File 'lib/cri/option_parser.rb', line 111

def self.parse(arguments_and_options, definitions)
  self.new(arguments_and_options, definitions).run
end

Instance Method Details

#runCri::OptionParser

Parses the commandline arguments into options and arguments.

During parsing, two errors can be raised:

Returns:

Raises:

  • IllegalOptionError if an unrecognised option was encountered, i.e. an option that is not present in the list of option definitions

  • OptionRequiresAnArgumentError if an option was found that did not have a value, even though this value was required.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
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
242
243
# File 'lib/cri/option_parser.rb', line 156

def run
  @running = true

  while running?
    # Get next item
    e = @unprocessed_arguments_and_options.shift
    break if e.nil?

    # Handle end-of-options marker
    if e == '--'
      @no_more_options = true
    # Handle incomplete options
    elsif e =~ /^--./ and !@no_more_options
      # Get option key, and option value if included
      if e =~ /^--([^=]+)=(.+)$/
        option_key   = $1
        option_value = $2
      else
        option_key    = e[2..-1]
        option_value  = nil
      end

      # Find definition
      definition = @definitions.find { |d| d[:long] == option_key }
      raise IllegalOptionError.new(option_key) if definition.nil?

      if [ :required, :optional ].include?(definition[:argument])
        # Get option value if necessary
        if option_value.nil?
          option_value = @unprocessed_arguments_and_options.shift
          if option_value.nil? || option_value =~ /^-/
            if definition[:argument] == :required
              raise OptionRequiresAnArgumentError.new(option_key)
            else
              @unprocessed_arguments_and_options.unshift(option_value)
              option_value = true
            end
          end
        end

        # Store option
        add_option(definition[:long].to_sym, option_value)
      else
        # Store option
        add_option(definition[:long].to_sym, true)
      end
    # Handle -xyz options
    elsif e =~ /^-./ and !@no_more_options
      # Get option keys
      option_keys = e[1..-1].scan(/./)

      # For each key
      option_keys.each do |option_key|
        # Find definition
        definition = @definitions.find { |d| d[:short] == option_key }
        raise IllegalOptionError.new(option_key) if definition.nil?

        if option_keys.length > 1 and definition[:argument] == :required
          # This is a combined option and it requires an argument, so complain
          raise OptionRequiresAnArgumentError.new(option_key)
        elsif [ :required, :optional ].include?(definition[:argument])
          # Get option value
          option_value = @unprocessed_arguments_and_options.shift
          if option_value.nil? || option_value =~ /^-/
            if definition[:argument] == :required
              raise OptionRequiresAnArgumentError.new(option_key)
            else
              @unprocessed_arguments_and_options.unshift(option_value)
              option_value = true
            end
          end

          # Store option
          add_option(definition[:long].to_sym, option_value)
        else
          # Store option
          add_option(definition[:long].to_sym, true)
        end
      end
    # Handle normal arguments
    else
      add_argument(e)
    end
  end
  self
ensure
  @running = false
end

#running?Boolean

Returns true if the parser is running, false otherwise.

Returns:

  • (Boolean)

    true if the parser is running, false otherwise.



133
134
135
# File 'lib/cri/option_parser.rb', line 133

def running?
  @running
end

#stopvoid

This method returns an undefined value.

Stops the parser. The parser will finish its current parse cycle but will not start parsing new options and/or arguments.



141
142
143
# File 'lib/cri/option_parser.rb', line 141

def stop
  @running = false
end