rbcli 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c26d92003e72338b249ee20c7948f8cd25d871c980e16309bcf3281742747e1
4
- data.tar.gz: 4cd46e54fccc1312a9cfa4e8b95996c4dca68cb363e9ea0fbd35b27a6b80ef00
3
+ metadata.gz: 6123cc2792a91f94903c85d80bb940c0e759dd12c2d54f670ba29c660f522d5c
4
+ data.tar.gz: 68093975766eb32942a94479bb1048feaa154b21b28781335c9215025bb7f5e6
5
5
  SHA512:
6
- metadata.gz: 93d7b08b996c5fd70b47e10e819ef23ff2cbde309fa1ff3873acd35477ecc84562137dddcef724a0c2dae3e31792fe8b203e0c8b80156086b3ed8b86a28c1f55
7
- data.tar.gz: 02fd81d6777ea7e18e5d4682ddc6feff963781c2a5c4197165ea68dd421dabd7cbeb451b75cfa3b93051a2752db9475c4836921b70d8e60347a29a88282f5395
6
+ metadata.gz: 124b7849fe7ac522b201bb18f1f9c57ce8776e64300c3c72460f882c77b7fde45e7b673b3d9aeb886dc8f0bab7dee40ab1c134e27d6d799baba27d16c94d9f1d
7
+ data.tar.gz: b60378421c9a11951be12290562fb02a97901c36f9abe78393178a1f2b1d225655654abf411310b2cd322a1d11b934dcb39c59cd7700aa009d7f2ad5218a21c5
data/Gemfile.lock CHANGED
@@ -1,10 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbcli (0.1.1)
4
+ rbcli (0.1.2)
5
5
  colorize (~> 0.8)
6
6
  deep_merge (~> 1.2)
7
- trollop (~> 2.1)
8
7
 
9
8
  GEM
10
9
  remote: https://rubygems.org/
@@ -13,7 +12,6 @@ GEM
13
12
  deep_merge (1.2.1)
14
13
  minitest (5.11.3)
15
14
  rake (10.5.0)
16
- trollop (2.1.2)
17
15
 
18
16
  PLATFORMS
19
17
  ruby
data/README.md CHANGED
@@ -70,7 +70,7 @@ Rbcli::configurate do
70
70
  config_defaults 'defaults.yml' # (Optional, Multiple) Load a YAML file as part of the default config. This can be called multiple times, and the YAML files will be merged. User config is generated from these
71
71
  config_default :myopt, description: 'Testing this', value: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
72
72
 
73
- option :name, 'Give me your name', type: :string, default: 'Stranger' # (Optional, Multiple) Add a global CLI parameter. Supported types are :string, :boolean, :integer, :float, :date, and :io. Can be called multiple times.
73
+ option :name, 'Give me your name', type: :string, default: 'Foo', required: false, permitted: ['Jack', 'Jill'] # (Optional, Multiple) Add a global CLI parameter. Supported types are :string, :boolean, :integer, :float, :date, and :io. Can be called multiple times.
74
74
 
75
75
  default_action do |opts| # (Optional) The default code to execute when no subcommand is given. If not present, the help is shown (same as -h)
76
76
  puts "Hello, #{opts[:name]}."
@@ -85,9 +85,24 @@ Rbcli::configurate do
85
85
  puts 'This is a post-command hook. It executes after the command.'
86
86
  end
87
87
  end
88
-
89
88
  ```
90
89
 
90
+ #### CLI Option Declarations
91
+
92
+ For the `option` parameters that you want to create, the following types are supported:
93
+
94
+ * :string
95
+ * :boolean or :flag
96
+ * :integer
97
+ * :float
98
+
99
+ If a default value is not set, it will default to `nil`.
100
+
101
+ If you want to declare more than one option, you can call it multiple times. The same goes for other items tagged with _Multiple_ in the description above.
102
+
103
+ Once parsed, options will be placed in a hash where they can be accessed via their names as shown above. You can see this demonstrated in the `default_action`, `pre_hook`, and `post_hook` blocks.
104
+
105
+
91
106
  ### Command Declaration
92
107
 
93
108
  Commands are added by subclassing `Rbcli::Command`. There are a few parameters to set for it, which get used by the different components of Rbcli.
data/examples/mytool CHANGED
@@ -20,7 +20,7 @@ Rbcli::configurate do
20
20
  config_defaults 'defaults.yml' # (Optional, Multiple) Load a YAML file as part of the default config. This can be called multiple times, and the YAML files will be merged. User config is generated from these
21
21
  config_default :myopt, description: 'Testing this', value: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
22
22
 
23
- option :name, 'Give me your name', type: :string, default: 'Stranger' # (Optional, Multiple) Add a global CLI parameter. Supported types are :string, :boolean, :integer, :float, :date, and :io. Can be called multiple times.
23
+ option :name, 'Give me your name', type: :string, default: 'Foo', required: false, permitted: ['Jack', 'Jill'] # (Optional, Multiple) Add a global CLI parameter. Supported types are :string, :boolean, :integer, :float, :date, and :io. Can be called multiple times.
24
24
 
25
25
  default_action do |opts| # (Optional) The default code to execute when no subcommand is given. If not present, the help is shown (same as -h)
26
26
  puts "Hello, #{opts[:name]}."
@@ -65,6 +65,6 @@ end
65
65
  ## Parse Statement ##
66
66
  #####################
67
67
  # When this statement is called, the CLI will be
68
- # parsed and code executed as
68
+ # parsed and code executed.
69
69
  #####################
70
70
  Rbcli.parse # Parse CLI and execute
@@ -61,11 +61,14 @@ module Rbcli
61
61
  @data[:allow_json] = allow_json
62
62
  end
63
63
 
64
- def self.option name, description, type: :boolean, default: false
64
+ def self.option name, description, type: :boolean, default: nil, required: false, permitted: nil
65
+ default ||= false if (type == :boolean || type == :bool || type == :flag)
65
66
  @data[:options][name.to_sym] = {
66
67
  description: description,
67
68
  type: type,
68
- default: default
69
+ default: default,
70
+ required: required,
71
+ permitted: permitted
69
72
  }
70
73
  end
71
74
 
@@ -29,13 +29,15 @@ class Rbcli::Command
29
29
  def self.action █ @action = block end
30
30
  def action; self.class.instance_variable_get :@action end
31
31
 
32
- def self.parameter name, description, type: :boolean, default: nil, required: false
32
+ def self.parameter name, description, type: :boolean, default: nil, required: false, permitted: nil
33
+ default ||= false if (type == :boolean || type == :bool || type == :flag)
33
34
  @paramlist ||= {}
34
35
  @paramlist[name.to_sym] = {
35
36
  description: description,
36
37
  type: type,
37
38
  default: default,
38
- required: required
39
+ required: required,
40
+ permitted: permitted
39
41
  }
40
42
  end
41
43
  def paramlist; self.class.instance_variable_get :@paramlist end
@@ -100,7 +102,7 @@ Usage:
100
102
  Command-specific Parameters:
101
103
  EOS
102
104
  params.each do |name, opts|
103
- opt name, opts[:description], type: opts[:type], default: opts[:default], required: opts[:required]
105
+ opt name, opts[:description], type: opts[:type], default: opts[:default], required: opts[:required], permitted: opts[:permitted]
104
106
  end if params.is_a? Hash
105
107
  end
106
108
  optx[:args] = ARGV
@@ -1,4 +1,5 @@
1
- require 'trollop'
1
+ #TODO: Change this once the changes have been merged into trollop gem proper
2
+ require "rbcli/util/trollop"
2
3
 
3
4
  module Rbcli::Parser
4
5
 
@@ -21,7 +22,10 @@ Commands:
21
22
  [options]:
22
23
  EOS
23
24
  data[:options].each do |name, opts|
24
- opt name.to_sym, opts[:description], type: opts[:type], default: opts[:default]
25
+ opts[:default] = nil unless opts.key? :default
26
+ opts[:required] = false unless opts.key? :required
27
+ opts[:permitted] = nil unless opts.key? :permitted
28
+ opt name.to_sym, opts[:description], type: opts[:type], default: opts[:default], required: opts[:required], permitted: opts[:permitted]
25
29
  end
26
30
  opt :json_output, 'Output result in machine-friendly JSON format', :type => :boolean, :default => false if data[:allow_json]
27
31
  opt :config_file, 'Specify a config file manually', :type => :string, :default => data[:config_userfile]
@@ -46,7 +46,7 @@ module Rbcli::Config
46
46
  @loaded = false
47
47
 
48
48
  def self.set_userfile filename, merge_defaults: false, required: false
49
- @config_file = filename
49
+ @config_file = File.expand_path filename
50
50
  @merge_defaults = merge_defaults
51
51
  @userfile_required = required
52
52
  @loaded = false
@@ -76,6 +76,7 @@ module Rbcli::Config
76
76
  end
77
77
 
78
78
  def self.add_defaults filename=nil, text: nil
79
+ filename = File.expand_path filename
79
80
  return unless filename and File.exists? filename
80
81
  @config_text ||= ''
81
82
  @config_text += "\n" unless @config_text.empty?
@@ -0,0 +1,1050 @@
1
+ #TODO: Delete this file once changes have been merged into trollop proper
2
+
3
+ # lib/trollop.rb -- trollop command-line processing library
4
+ # Copyright (c) 2008-2014 William Morgan.
5
+ # Copyright (c) 2014 Red Hat, Inc.
6
+ # trollop is licensed under the MIT license.
7
+
8
+ require 'date'
9
+
10
+ module Trollop
11
+ # note: this is duplicated in gemspec
12
+ # please change over there too
13
+ VERSION = "2.1.2"
14
+
15
+ ## Thrown by Parser in the event of a commandline error. Not needed if
16
+ ## you're using the Trollop::options entry.
17
+ class CommandlineError < StandardError
18
+ attr_reader :error_code
19
+
20
+ def initialize(msg, error_code = nil)
21
+ super(msg)
22
+ @error_code = error_code
23
+ end
24
+ end
25
+
26
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
27
+ ## automatically by Trollop#options.
28
+ class HelpNeeded < StandardError
29
+ end
30
+
31
+ ## Thrown by Parser if the user passes in '-v' or '--version'. Handled
32
+ ## automatically by Trollop#options.
33
+ class VersionNeeded < StandardError
34
+ end
35
+
36
+ ## Regex for floating point numbers
37
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?[\d]+)?$/
38
+
39
+ ## Regex for parameters
40
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
41
+
42
+ ## The commandline parser. In typical usage, the methods in this class
43
+ ## will be handled internally by Trollop::options. In this case, only the
44
+ ## #opt, #banner and #version, #depends, and #conflicts methods will
45
+ ## typically be called.
46
+ ##
47
+ ## If you want to instantiate this class yourself (for more complicated
48
+ ## argument-parsing logic), call #parse to actually produce the output hash,
49
+ ## and consider calling it from within
50
+ ## Trollop::with_standard_exception_handling.
51
+ class Parser
52
+
53
+ ## The registry is a class-instance-variable map of option aliases to their subclassed Option class.
54
+ @registry = {}
55
+
56
+ ## The Option subclasses are responsible for registering themselves using this function.
57
+ def self.register(lookup, klass)
58
+ @registry[lookup.to_sym] = klass
59
+ end
60
+
61
+ ## Gets the class from the registry.
62
+ ## Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer
63
+ def self.registry_getopttype(type)
64
+ return nil unless type
65
+ if type.respond_to?(:name)
66
+ type = type.name
67
+ lookup = type.downcase.to_sym
68
+ else
69
+ lookup = type.to_sym
70
+ end
71
+ raise ArgumentError, "Unsupported argument type '#{type}', registry lookup '#{lookup}'" unless @registry.has_key?(lookup)
72
+ return @registry[lookup].new
73
+ end
74
+
75
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
76
+
77
+ ## The values from the commandline that were not interpreted by #parse.
78
+ attr_reader :leftovers
79
+
80
+ ## The complete configuration hashes for each option. (Mainly useful
81
+ ## for testing.)
82
+ attr_reader :specs
83
+
84
+ ## A flag that determines whether or not to raise an error if the parser is passed one or more
85
+ ## options that were not registered ahead of time. If 'true', then the parser will simply
86
+ ## ignore options that it does not recognize.
87
+ attr_accessor :ignore_invalid_options
88
+
89
+ ## Initializes the parser, and instance-evaluates any block given.
90
+ def initialize(*a, &b)
91
+ @version = nil
92
+ @leftovers = []
93
+ @specs = {}
94
+ @long = {}
95
+ @short = {}
96
+ @order = []
97
+ @constraints = []
98
+ @stop_words = []
99
+ @stop_on_unknown = false
100
+ @educate_on_error = false
101
+ @synopsis = nil
102
+ @usage = nil
103
+
104
+ # instance_eval(&b) if b # can't take arguments
105
+ cloaker(&b).bind(self).call(*a) if b
106
+ end
107
+
108
+ ## Define an option. +name+ is the option name, a unique identifier
109
+ ## for the option that you will use internally, which should be a
110
+ ## symbol or a string. +desc+ is a string description which will be
111
+ ## displayed in help messages.
112
+ ##
113
+ ## Takes the following optional arguments:
114
+ ##
115
+ ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
116
+ ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value.
117
+ ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
118
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
119
+ ## [+:required+] If set to +true+, the argument must be provided on the commandline.
120
+ ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
121
+ ##
122
+ ## Note that there are two types of argument multiplicity: an argument
123
+ ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
124
+ ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
125
+ ##
126
+ ## Arguments that take multiple values should have a +:type+ parameter
127
+ ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
128
+ ## value of an array of the correct type (e.g. [String]). The
129
+ ## value of this argument will be an array of the parameters on the
130
+ ## commandline.
131
+ ##
132
+ ## Arguments that can occur multiple times should be marked with
133
+ ## +:multi+ => +true+. The value of this argument will also be an array.
134
+ ## In contrast with regular non-multi options, if not specified on
135
+ ## the commandline, the default value will be [], not nil.
136
+ ##
137
+ ## These two attributes can be combined (e.g. +:type+ => +:strings+,
138
+ ## +:multi+ => +true+), in which case the value of the argument will be
139
+ ## an array of arrays.
140
+ ##
141
+ ## There's one ambiguous case to be aware of: when +:multi+: is true and a
142
+ ## +:default+ is set to an array (of something), it's ambiguous whether this
143
+ ## is a multi-value argument as well as a multi-occurrence argument.
144
+ ## In thise case, Trollop assumes that it's not a multi-value argument.
145
+ ## If you want a multi-value, multi-occurrence argument with a default
146
+ ## value, you must specify +:type+ as well.
147
+
148
+ def opt(name, desc = "", opts = {}, &b)
149
+ opts[:callback] ||= b if block_given?
150
+ opts[:desc] ||= desc
151
+
152
+ o = Option.create(name, desc, opts)
153
+
154
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? o.name
155
+ raise ArgumentError, "long option name #{o.long.inspect} is already taken; please specify a (different) :long" if @long[o.long]
156
+ raise ArgumentError, "short option name #{o.short.inspect} is already taken; please specify a (different) :short" if @short[o.short]
157
+ raise ArgumentError, "permitted values for option #{o.long.inspect} must be either nil or an array;" unless o.permitted.nil? or o.permitted.is_a? Array
158
+
159
+ @long[o.long] = o.name
160
+ @short[o.short] = o.name if o.short?
161
+ @specs[o.name] = o
162
+ @order << [:opt, o.name]
163
+ end
164
+
165
+ ## Sets the version string. If set, the user can request the version
166
+ ## on the commandline. Should probably be of the form "<program name>
167
+ ## <version number>".
168
+ def version(s = nil)
169
+ s ? @version = s : @version
170
+ end
171
+
172
+ ## Sets the usage string. If set the message will be printed as the
173
+ ## first line in the help (educate) output and ending in two new
174
+ ## lines.
175
+ def usage(s = nil)
176
+ s ? @usage = s : @usage
177
+ end
178
+
179
+ ## Adds a synopsis (command summary description) right below the
180
+ ## usage line, or as the first line if usage isn't specified.
181
+ def synopsis(s = nil)
182
+ s ? @synopsis = s : @synopsis
183
+ end
184
+
185
+ ## Adds text to the help display. Can be interspersed with calls to
186
+ ## #opt to build a multi-section help page.
187
+ def banner(s)
188
+ @order << [:text, s]
189
+ end
190
+ alias_method :text, :banner
191
+
192
+ ## Marks two (or more!) options as requiring each other. Only handles
193
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
194
+ ## better modeled with Trollop::die.
195
+ def depends(*syms)
196
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
197
+ @constraints << [:depends, syms]
198
+ end
199
+
200
+ ## Marks two (or more!) options as conflicting.
201
+ def conflicts(*syms)
202
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
203
+ @constraints << [:conflicts, syms]
204
+ end
205
+
206
+ ## Defines a set of words which cause parsing to terminate when
207
+ ## encountered, such that any options to the left of the word are
208
+ ## parsed as usual, and options to the right of the word are left
209
+ ## intact.
210
+ ##
211
+ ## A typical use case would be for subcommand support, where these
212
+ ## would be set to the list of subcommands. A subsequent Trollop
213
+ ## invocation would then be used to parse subcommand options, after
214
+ ## shifting the subcommand off of ARGV.
215
+ def stop_on(*words)
216
+ @stop_words = [*words].flatten
217
+ end
218
+
219
+ ## Similar to #stop_on, but stops on any unknown word when encountered
220
+ ## (unless it is a parameter for an argument). This is useful for
221
+ ## cases where you don't know the set of subcommands ahead of time,
222
+ ## i.e., without first parsing the global options.
223
+ def stop_on_unknown
224
+ @stop_on_unknown = true
225
+ end
226
+
227
+ ## Instead of displaying "Try --help for help." on an error
228
+ ## display the usage (via educate)
229
+ def educate_on_error
230
+ @educate_on_error = true
231
+ end
232
+
233
+ ## Parses the commandline. Typically called by Trollop::options,
234
+ ## but you can call it directly if you need more control.
235
+ ##
236
+ ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
237
+ def parse(cmdline = ARGV)
238
+ vals = {}
239
+ required = {}
240
+
241
+ opt :version, "Print version and exit" if @version && ! (@specs[:version] || @long["version"])
242
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
243
+
244
+ @specs.each do |sym, opts|
245
+ required[sym] = true if opts.required?
246
+ vals[sym] = opts.default
247
+ vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil
248
+ end
249
+
250
+ resolve_default_short_options!
251
+
252
+ ## resolve symbols
253
+ given_args = {}
254
+ @leftovers = each_arg cmdline do |arg, params|
255
+ ## handle --no- forms
256
+ arg, negative_given = if arg =~ /^--no-([^-]\S*)$/
257
+ ["--#{$1}", true]
258
+ else
259
+ [arg, false]
260
+ end
261
+
262
+ sym = case arg
263
+ when /^-([^-])$/ then @short[$1]
264
+ when /^--([^-]\S*)$/ then @long[$1] || @long["no-#{$1}"]
265
+ else raise CommandlineError, "invalid argument syntax: '#{arg}'"
266
+ end
267
+
268
+ sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments
269
+
270
+ next nil if ignore_invalid_options && !sym
271
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
272
+
273
+ if given_args.include?(sym) && !@specs[sym].multi?
274
+ raise CommandlineError, "option '#{arg}' specified multiple times"
275
+ end
276
+
277
+ given_args[sym] ||= {}
278
+ given_args[sym][:arg] = arg
279
+ given_args[sym][:negative_given] = negative_given
280
+ given_args[sym][:params] ||= []
281
+
282
+ # The block returns the number of parameters taken.
283
+ num_params_taken = 0
284
+
285
+ unless params.empty?
286
+ if @specs[sym].single_arg?
287
+ given_args[sym][:params] << params[0, 1] # take the first parameter
288
+ num_params_taken = 1
289
+ elsif @specs[sym].multi_arg?
290
+ given_args[sym][:params] << params # take all the parameters
291
+ num_params_taken = params.size
292
+ end
293
+ end
294
+
295
+ num_params_taken
296
+ end
297
+
298
+ ## check for version and help args
299
+ raise VersionNeeded if given_args.include? :version
300
+ raise HelpNeeded if given_args.include? :help
301
+
302
+ ## check constraint satisfaction
303
+ @constraints.each do |type, syms|
304
+ constraint_sym = syms.find { |sym| given_args[sym] }
305
+ next unless constraint_sym
306
+
307
+ case type
308
+ when :depends
309
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}" unless given_args.include? sym }
310
+ when :conflicts
311
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}" if given_args.include?(sym) && (sym != constraint_sym) }
312
+ end
313
+ end
314
+
315
+ required.each do |sym, val|
316
+ raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym
317
+ end
318
+
319
+ ## parse parameters
320
+ given_args.each do |sym, given_data|
321
+ arg, params, negative_given = given_data.values_at :arg, :params, :negative_given
322
+
323
+ opts = @specs[sym]
324
+ if params.empty? && !opts.flag?
325
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
326
+ params << (opts.array_default? ? opts.default.clone : [opts.default])
327
+ end
328
+
329
+ params[0].each do |p|
330
+ raise CommandlineError, "option '#{arg}' only accepts one of: #{opts.permitted.join(', ')}" unless opts.permitted.include? p
331
+ end unless opts.permitted.nil?
332
+
333
+ vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
334
+
335
+ vals[sym] = opts.parse(params, negative_given)
336
+
337
+ if opts.single_arg?
338
+ if opts.multi? # multiple options, each with a single parameter
339
+ vals[sym] = vals[sym].map { |p| p[0] }
340
+ else # single parameter
341
+ vals[sym] = vals[sym][0][0]
342
+ end
343
+ elsif opts.multi_arg? && !opts.multi?
344
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
345
+ end
346
+ # else: multiple options, with multiple parameters
347
+
348
+ opts.callback.call(vals[sym]) if opts.callback
349
+ end
350
+
351
+ ## modify input in place with only those
352
+ ## arguments we didn't process
353
+ cmdline.clear
354
+ @leftovers.each { |l| cmdline << l }
355
+
356
+ ## allow openstruct-style accessors
357
+ class << vals
358
+ def method_missing(m, *_args)
359
+ self[m] || self[m.to_s]
360
+ end
361
+ end
362
+ vals
363
+ end
364
+
365
+ ## Print the help message to +stream+.
366
+ def educate(stream = $stdout)
367
+ width # hack: calculate it now; otherwise we have to be careful not to
368
+ # call this unless the cursor's at the beginning of a line.
369
+
370
+ left = {}
371
+ @specs.each { |name, spec| left[name] = spec.educate }
372
+
373
+ leftcol_width = left.values.map(&:length).max || 0
374
+ rightcol_start = leftcol_width + 6 # spaces
375
+
376
+ unless @order.size > 0 && @order.first.first == :text
377
+ command_name = File.basename($0).gsub(/\.[^.]+$/, '')
378
+ stream.puts "Usage: #{command_name} #{@usage}\n" if @usage
379
+ stream.puts "#{@synopsis}\n" if @synopsis
380
+ stream.puts if @usage || @synopsis
381
+ stream.puts "#{@version}\n" if @version
382
+ stream.puts "Options:"
383
+ end
384
+
385
+ @order.each do |what, opt|
386
+ if what == :text
387
+ stream.puts wrap(opt)
388
+ next
389
+ end
390
+
391
+ spec = @specs[opt]
392
+ stream.printf " %-#{leftcol_width}s ", left[opt]
393
+ desc = spec.full_description
394
+
395
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
396
+ end
397
+ end
398
+
399
+ def width #:nodoc:
400
+ @width ||= if $stdout.tty?
401
+ begin
402
+ require 'io/console'
403
+ w = IO.console.winsize.last
404
+ w.to_i > 0 ? w : 80
405
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL
406
+ legacy_width
407
+ end
408
+ else
409
+ 80
410
+ end
411
+ end
412
+
413
+ def legacy_width
414
+ # Support for older Rubies where io/console is not available
415
+ `tput cols`.to_i
416
+ rescue Errno::ENOENT
417
+ 80
418
+ end
419
+ private :legacy_width
420
+
421
+ def wrap(str, opts = {}) # :nodoc:
422
+ if str == ""
423
+ [""]
424
+ else
425
+ inner = false
426
+ str.split("\n").map do |s|
427
+ line = wrap_line s, opts.merge(:inner => inner)
428
+ inner = true
429
+ line
430
+ end.flatten
431
+ end
432
+ end
433
+
434
+ ## The per-parser version of Trollop::die (see that for documentation).
435
+ def die(arg, msg = nil, error_code = nil)
436
+ msg, error_code = nil, msg if msg.kind_of?(Integer)
437
+ if msg
438
+ $stderr.puts "Error: argument --#{@specs[arg].long} #{msg}."
439
+ else
440
+ $stderr.puts "Error: #{arg}."
441
+ end
442
+ if @educate_on_error
443
+ $stderr.puts
444
+ educate $stderr
445
+ else
446
+ $stderr.puts "Try --help for help."
447
+ end
448
+ exit(error_code || -1)
449
+ end
450
+
451
+ private
452
+
453
+ ## yield successive arg, parameter pairs
454
+ def each_arg(args)
455
+ remains = []
456
+ i = 0
457
+
458
+ until i >= args.length
459
+ return remains += args[i..-1] if @stop_words.member? args[i]
460
+ case args[i]
461
+ when /^--$/ # arg terminator
462
+ return remains += args[(i + 1)..-1]
463
+ when /^--(\S+?)=(.*)$/ # long argument with equals
464
+ num_params_taken = yield "--#{$1}", [$2]
465
+ if num_params_taken.nil?
466
+ remains << args[i]
467
+ if @stop_on_unknown
468
+ return remains += args[i + 1..-1]
469
+ end
470
+ end
471
+ i += 1
472
+ when /^--(\S+)$/ # long argument
473
+ params = collect_argument_parameters(args, i + 1)
474
+ num_params_taken = yield args[i], params
475
+
476
+ if num_params_taken.nil?
477
+ remains << args[i]
478
+ if @stop_on_unknown
479
+ return remains += args[i + 1..-1]
480
+ end
481
+ else
482
+ i += num_params_taken
483
+ end
484
+ i += 1
485
+ when /^-(\S+)$/ # one or more short arguments
486
+ short_remaining = ""
487
+ shortargs = $1.split(//)
488
+ shortargs.each_with_index do |a, j|
489
+ if j == (shortargs.length - 1)
490
+ params = collect_argument_parameters(args, i + 1)
491
+
492
+ num_params_taken = yield "-#{a}", params
493
+ unless num_params_taken
494
+ short_remaining << a
495
+ if @stop_on_unknown
496
+ remains << "-#{short_remaining}"
497
+ return remains += args[i + 1..-1]
498
+ end
499
+ else
500
+ i += num_params_taken
501
+ end
502
+ else
503
+ unless yield "-#{a}", []
504
+ short_remaining << a
505
+ if @stop_on_unknown
506
+ short_remaining += shortargs[j + 1..-1].join
507
+ remains << "-#{short_remaining}"
508
+ return remains += args[i + 1..-1]
509
+ end
510
+ end
511
+ end
512
+ end
513
+
514
+ unless short_remaining.empty?
515
+ remains << "-#{short_remaining}"
516
+ end
517
+ i += 1
518
+ else
519
+ if @stop_on_unknown
520
+ return remains += args[i..-1]
521
+ else
522
+ remains << args[i]
523
+ i += 1
524
+ end
525
+ end
526
+ end
527
+
528
+ remains
529
+ end
530
+
531
+ def collect_argument_parameters(args, start_at)
532
+ params = []
533
+ pos = start_at
534
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
535
+ params << args[pos]
536
+ pos += 1
537
+ end
538
+ params
539
+ end
540
+
541
+ def resolve_default_short_options!
542
+ @order.each do |type, name|
543
+ opts = @specs[name]
544
+ next if type != :opt || opts.short
545
+
546
+ c = opts.long.split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
547
+ if c # found a character to use
548
+ opts.short = c
549
+ @short[c] = name
550
+ end
551
+ end
552
+ end
553
+
554
+ def wrap_line(str, opts = {})
555
+ prefix = opts[:prefix] || 0
556
+ width = opts[:width] || (self.width - 1)
557
+ start = 0
558
+ ret = []
559
+ until start > str.length
560
+ nextt =
561
+ if start + width >= str.length
562
+ str.length
563
+ else
564
+ x = str.rindex(/\s/, start + width)
565
+ x = str.index(/\s/, start) if x && x < start
566
+ x || str.length
567
+ end
568
+ ret << ((ret.empty? && !opts[:inner]) ? "" : " " * prefix) + str[start...nextt]
569
+ start = nextt + 1
570
+ end
571
+ ret
572
+ end
573
+
574
+ ## instance_eval but with ability to handle block arguments
575
+ ## thanks to _why: http://redhanded.hobix.com/inspect/aBlockCostume.html
576
+ def cloaker(&b)
577
+ (class << self; self; end).class_eval do
578
+ define_method :cloaker_, &b
579
+ meth = instance_method :cloaker_
580
+ remove_method :cloaker_
581
+ meth
582
+ end
583
+ end
584
+ end
585
+
586
+ class Option
587
+
588
+ attr_accessor :name, :short, :long, :default, :permitted
589
+ attr_writer :multi_given
590
+
591
+ def initialize
592
+ @long = nil
593
+ @short = nil
594
+ @name = nil
595
+ @multi_given = false
596
+ @hidden = false
597
+ @default = nil
598
+ @permitted = nil
599
+ @optshash = Hash.new()
600
+ end
601
+
602
+ def opts (key)
603
+ @optshash[key]
604
+ end
605
+
606
+ def opts= (o)
607
+ @optshash = o
608
+ end
609
+
610
+ ## Indicates a flag option, which is an option without an argument
611
+ def flag? ; false ; end
612
+ def single_arg?
613
+ !self.multi_arg? && !self.flag?
614
+ end
615
+
616
+ def multi ; @multi_given ; end
617
+ alias multi? multi
618
+
619
+ ## Indicates that this is a multivalued (Array type) argument
620
+ def multi_arg? ; false ; end
621
+ ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options.
622
+
623
+ def array_default? ; self.default.kind_of?(Array) ; end
624
+
625
+ def short? ; short && short != :none ; end
626
+
627
+ def callback ; opts(:callback) ; end
628
+ def desc ; opts(:desc) ; end
629
+
630
+ def required? ; opts(:required) ; end
631
+
632
+ def parse (_paramlist, _neg_given)
633
+ raise NotImplementedError, "parse must be overridden for newly registered type"
634
+ end
635
+
636
+ # provide type-format string. default to empty, but user should probably override it
637
+ def type_format ; "" ; end
638
+
639
+ def educate
640
+ (short? ? "-#{short}, " : "") + "--#{long}" + type_format + (flag? && default ? ", --no-#{long}" : "")
641
+ end
642
+
643
+ ## Format the educate-line description including the default and permitted value(s)
644
+ def full_description
645
+ desc_str = desc
646
+ desc_str = description_with_default desc_str if default
647
+ desc_str = description_with_permitted desc_str if permitted
648
+ desc_str
649
+ end
650
+
651
+ ## Format the educate-line description including the default-value(s)
652
+ def description_with_default str
653
+ default_s = case default
654
+ when $stdout then '<stdout>'
655
+ when $stdin then '<stdin>'
656
+ when $stderr then '<stderr>'
657
+ when Array
658
+ default.join(', ')
659
+ else
660
+ default.to_s
661
+ end
662
+ defword = str.end_with?('.') ? 'Default' : 'default'
663
+ return "#{str} (#{defword}: #{default_s})"
664
+ end
665
+
666
+ ## Format the educate-line description including the permitted-value(s)
667
+ def description_with_permitted str
668
+ permitted_s = permitted.map do |p|
669
+ case p
670
+ when $stdout then '<stdout>'
671
+ when $stdin then '<stdin>'
672
+ when $stderr then '<stderr>'
673
+ else
674
+ p.to_s
675
+ end
676
+ end.join(', ')
677
+
678
+ permword = str.end_with?('.') ? 'Permitted' : 'permitted'
679
+ return "#{str} (#{permword}: #{permitted_s})"
680
+ end
681
+
682
+ ## Provide a way to register symbol aliases to the Parser
683
+ def self.register_alias(*alias_keys)
684
+ alias_keys.each do |alias_key|
685
+ # pass in the alias-key and the class
686
+ Parser.register(alias_key, self)
687
+ end
688
+ end
689
+
690
+ ## Factory class methods ...
691
+
692
+ # Determines which type of object to create based on arguments passed
693
+ # to +Trollop::opt+. This is trickier in Trollop, than other cmdline
694
+ # parsers (e.g. Slop) because we allow the +default:+ to be able to
695
+ # set the option's type.
696
+ def self.create(name, desc="", opts={}, settings={})
697
+
698
+ opttype = Trollop::Parser.registry_getopttype(opts[:type])
699
+ opttype_from_default = get_klass_from_default(opts, opttype)
700
+
701
+ raise ArgumentError, ":type specification and default type don't match (default type is #{opttype_from_default.class})" if opttype && opttype_from_default && (opttype.class != opttype_from_default.class)
702
+
703
+ opt_inst = (opttype || opttype_from_default || Trollop::BooleanOption.new)
704
+
705
+ ## fill in :long
706
+ opt_inst.long = handle_long_opt(opts[:long], name)
707
+
708
+ ## fill in :short
709
+ opt_inst.short = handle_short_opt(opts[:short])
710
+
711
+ ## fill in :multi
712
+ multi_given = opts[:multi] || false
713
+ opt_inst.multi_given = multi_given
714
+
715
+ ## fill in :default for flags
716
+ defvalue = opts[:default] || opt_inst.default
717
+
718
+ ## fill in permitted values
719
+ permitted = opts[:permitted] || nil
720
+
721
+ ## autobox :default for :multi (multi-occurrence) arguments
722
+ defvalue = [defvalue] if defvalue && multi_given && !defvalue.kind_of?(Array)
723
+ opt_inst.permitted = permitted
724
+ opt_inst.default = defvalue
725
+ opt_inst.name = name
726
+ opt_inst.opts = opts
727
+ opt_inst
728
+ end
729
+
730
+ private
731
+
732
+ def self.get_type_from_disdef(optdef, opttype, disambiguated_default)
733
+ if disambiguated_default.is_a? Array
734
+ return(optdef.first.class.name.downcase + "s") if !optdef.empty?
735
+ if opttype
736
+ raise ArgumentError, "multiple argument type must be plural" unless opttype.multi_arg?
737
+ return nil
738
+ else
739
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array"
740
+ end
741
+ end
742
+ return disambiguated_default.class.name.downcase
743
+ end
744
+
745
+ def self.get_klass_from_default(opts, opttype)
746
+ ## for options with :multi => true, an array default doesn't imply
747
+ ## a multi-valued argument. for that you have to specify a :type
748
+ ## as well. (this is how we disambiguate an ambiguous situation;
749
+ ## see the docs for Parser#opt for details.)
750
+
751
+ disambiguated_default = if opts[:multi] && opts[:default].is_a?(Array) && opttype.nil?
752
+ opts[:default].first
753
+ else
754
+ opts[:default]
755
+ end
756
+
757
+ return nil if disambiguated_default.nil?
758
+ type_from_default = get_type_from_disdef(opts[:default], opttype, disambiguated_default)
759
+ return Trollop::Parser.registry_getopttype(type_from_default)
760
+ end
761
+
762
+ def self.handle_long_opt(lopt, name)
763
+ lopt = lopt ? lopt.to_s : name.to_s.gsub("_", "-")
764
+ lopt = case lopt
765
+ when /^--([^-].*)$/ then $1
766
+ when /^[^-]/ then lopt
767
+ else raise ArgumentError, "invalid long option name #{lopt.inspect}"
768
+ end
769
+ end
770
+
771
+ def self.handle_short_opt(sopt)
772
+ sopt = sopt.to_s if sopt && sopt != :none
773
+ sopt = case sopt
774
+ when /^-(.)$/ then $1
775
+ when nil, :none, /^.$/ then sopt
776
+ else raise ArgumentError, "invalid short option name '#{sopt.inspect}'"
777
+ end
778
+
779
+ if sopt
780
+ raise ArgumentError, "a short option name can't be a number or a dash" if sopt =~ ::Trollop::Parser::INVALID_SHORT_ARG_REGEX
781
+ end
782
+ return sopt
783
+ end
784
+
785
+ end
786
+
787
+ # Flag option. Has no arguments. Can be negated with "no-".
788
+ class BooleanOption < Option
789
+ register_alias :flag, :bool, :boolean, :trueclass, :falseclass
790
+ def initialize
791
+ super()
792
+ @default = false
793
+ end
794
+ def flag? ; true ; end
795
+ def parse(_paramlist, neg_given)
796
+ return(self.name.to_s =~ /^no_/ ? neg_given : !neg_given)
797
+ end
798
+ end
799
+
800
+ # Floating point number option class.
801
+ class FloatOption < Option
802
+ register_alias :float, :double
803
+ def type_format ; "=<f>" ; end
804
+ def parse(paramlist, _neg_given)
805
+ paramlist.map do |pg|
806
+ pg.map do |param|
807
+ raise CommandlineError, "option '#{self.name}' needs a floating-point number" unless param.is_a?(Numeric) || param =~ FLOAT_RE
808
+ param.to_f
809
+ end
810
+ end
811
+ end
812
+ end
813
+
814
+ # Integer number option class.
815
+ class IntegerOption < Option
816
+ register_alias :int, :integer, :fixnum
817
+ def type_format ; "=<i>" ; end
818
+ def parse(paramlist, _neg_given)
819
+ paramlist.map do |pg|
820
+ pg.map do |param|
821
+ raise CommandlineError, "option '#{self.name}' needs an integer" unless param.is_a?(Numeric) || param =~ /^-?[\d_]+$/
822
+ param.to_i
823
+ end
824
+ end
825
+ end
826
+ end
827
+
828
+ # Option class for handling IO objects and URLs.
829
+ # Note that this will return the file-handle, not the file-name
830
+ # in the case of file-paths given to it.
831
+ class IOOption < Option
832
+ register_alias :io
833
+ def type_format ; "=<filename/uri>" ; end
834
+ def parse(paramlist, _neg_given)
835
+ paramlist.map do |pg|
836
+ pg.map do |param|
837
+ if param =~ /^(stdin|-)$/i
838
+ $stdin
839
+ else
840
+ require 'open-uri'
841
+ begin
842
+ open param
843
+ rescue SystemCallError => e
844
+ raise CommandlineError, "file or url for option '#{self.name}' cannot be opened: #{e.message}"
845
+ end
846
+ end
847
+ end
848
+ end
849
+ end
850
+ end
851
+
852
+ # Option class for handling Strings.
853
+ class StringOption < Option
854
+ register_alias :string
855
+ def type_format ; "=<s>" ; end
856
+ def parse(paramlist, _neg_given)
857
+ paramlist.map { |pg| pg.map(&:to_s) }
858
+ end
859
+ end
860
+
861
+ # Option for dates. Uses Chronic if it exists.
862
+ class DateOption < Option
863
+ register_alias :date
864
+ def type_format ; "=<date>" ; end
865
+ def parse(paramlist, _neg_given)
866
+ paramlist.map do |pg|
867
+ pg.map do |param|
868
+ next param if param.is_a?(Date)
869
+ begin
870
+ begin
871
+ require 'chronic'
872
+ time = Chronic.parse(param)
873
+ rescue LoadError
874
+ # chronic is not available
875
+ end
876
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
877
+ rescue ArgumentError
878
+ raise CommandlineError, "option '#{self.name}' needs a date"
879
+ end
880
+ end
881
+ end
882
+ end
883
+ end
884
+
885
+ ### MULTI_OPT_TYPES :
886
+ ## The set of values that indicate a multiple-parameter option (i.e., that
887
+ ## takes multiple space-separated values on the commandline) when passed as
888
+ ## the +:type+ parameter of #opt.
889
+
890
+ # Option class for handling multiple Integers
891
+ class IntegerArrayOption < IntegerOption
892
+ register_alias :fixnums, :ints, :integers
893
+ def type_format ; "=<i+>" ; end
894
+ def multi_arg? ; true ; end
895
+ end
896
+
897
+ # Option class for handling multiple Floats
898
+ class FloatArrayOption < FloatOption
899
+ register_alias :doubles, :floats
900
+ def type_format ; "=<f+>" ; end
901
+ def multi_arg? ; true ; end
902
+ end
903
+
904
+ # Option class for handling multiple Strings
905
+ class StringArrayOption < StringOption
906
+ register_alias :strings
907
+ def type_format ; "=<s+>" ; end
908
+ def multi_arg? ; true ; end
909
+ end
910
+
911
+ # Option class for handling multiple dates
912
+ class DateArrayOption < DateOption
913
+ register_alias :dates
914
+ def type_format ; "=<date+>" ; end
915
+ def multi_arg? ; true ; end
916
+ end
917
+
918
+ # Option class for handling Files/URLs via 'open'
919
+ class IOArrayOption < IOOption
920
+ register_alias :ios
921
+ def type_format ; "=<filename/uri+>" ; end
922
+ def multi_arg? ; true ; end
923
+ end
924
+
925
+ ## The easy, syntactic-sugary entry method into Trollop. Creates a Parser,
926
+ ## passes the block to it, then parses +args+ with it, handling any errors or
927
+ ## requests for help or version information appropriately (and then exiting).
928
+ ## Modifies +args+ in place. Returns a hash of option values.
929
+ ##
930
+ ## The block passed in should contain zero or more calls to +opt+
931
+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
932
+ ## probably a call to +version+ (Parser#version).
933
+ ##
934
+ ## The returned block contains a value for every option specified with
935
+ ## +opt+. The value will be the value given on the commandline, or the
936
+ ## default value if the option was not specified on the commandline. For
937
+ ## every option specified on the commandline, a key "<option
938
+ ## name>_given" will also be set in the hash.
939
+ ##
940
+ ## Example:
941
+ ##
942
+ ## require 'trollop'
943
+ ## opts = Trollop::options do
944
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
945
+ ## opt :name, "Monkey name", :type => :string # a string --name <s>, defaulting to nil
946
+ ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
947
+ ## end
948
+ ##
949
+ ## ## if called with no arguments
950
+ ## p opts # => {:monkey=>false, :name=>nil, :num_limbs=>4, :help=>false}
951
+ ##
952
+ ## ## if called with --monkey
953
+ ## p opts # => {:monkey=>true, :name=>nil, :num_limbs=>4, :help=>false, :monkey_given=>true}
954
+ ##
955
+ ## See more examples at http://trollop.rubyforge.org.
956
+ def options(args = ARGV, *a, &b)
957
+ @last_parser = Parser.new(*a, &b)
958
+ with_standard_exception_handling(@last_parser) { @last_parser.parse args }
959
+ end
960
+
961
+ ## If Trollop::options doesn't do quite what you want, you can create a Parser
962
+ ## object and call Parser#parse on it. That method will throw CommandlineError,
963
+ ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to
964
+ ## have these handled for you in the standard manner (e.g. show the help
965
+ ## and then exit upon an HelpNeeded exception), call your code from within
966
+ ## a block passed to this method.
967
+ ##
968
+ ## Note that this method will call System#exit after handling an exception!
969
+ ##
970
+ ## Usage example:
971
+ ##
972
+ ## require 'trollop'
973
+ ## p = Trollop::Parser.new do
974
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
975
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
976
+ ## end
977
+ ##
978
+ ## opts = Trollop::with_standard_exception_handling p do
979
+ ## o = p.parse ARGV
980
+ ## raise Trollop::HelpNeeded if ARGV.empty? # show help screen
981
+ ## o
982
+ ## end
983
+ ##
984
+ ## Requires passing in the parser object.
985
+
986
+ def with_standard_exception_handling(parser)
987
+ yield
988
+ rescue CommandlineError => e
989
+ parser.die(e.message, nil, e.error_code)
990
+ rescue HelpNeeded
991
+ parser.educate
992
+ exit
993
+ rescue VersionNeeded
994
+ puts parser.version
995
+ exit
996
+ end
997
+
998
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
999
+ ## 'msg', and dies. Example:
1000
+ ##
1001
+ ## options do
1002
+ ## opt :volume, :default => 0.0
1003
+ ## end
1004
+ ##
1005
+ ## die :volume, "too loud" if opts[:volume] > 10.0
1006
+ ## die :volume, "too soft" if opts[:volume] < 0.1
1007
+ ##
1008
+ ## In the one-argument case, simply print that message, a notice
1009
+ ## about -h, and die. Example:
1010
+ ##
1011
+ ## options do
1012
+ ## opt :whatever # ...
1013
+ ## end
1014
+ ##
1015
+ ## Trollop::die "need at least one filename" if ARGV.empty?
1016
+ ##
1017
+ ## An exit code can be provide if needed
1018
+ ##
1019
+ ## Trollop::die "need at least one filename", -2 if ARGV.empty?
1020
+ def die(arg, msg = nil, error_code = nil)
1021
+ if @last_parser
1022
+ @last_parser.die arg, msg, error_code
1023
+ else
1024
+ raise ArgumentError, "Trollop::die can only be called after Trollop::options"
1025
+ end
1026
+ end
1027
+
1028
+ ## Displays the help message and dies. Example:
1029
+ ##
1030
+ ## options do
1031
+ ## opt :volume, :default => 0.0
1032
+ ## banner <<-EOS
1033
+ ## Usage:
1034
+ ## #$0 [options] <name>
1035
+ ## where [options] are:
1036
+ ## EOS
1037
+ ## end
1038
+ ##
1039
+ ## Trollop::educate if ARGV.empty?
1040
+ def educate
1041
+ if @last_parser
1042
+ @last_parser.educate
1043
+ exit
1044
+ else
1045
+ raise ArgumentError, "Trollop::educate can only be called after Trollop::options"
1046
+ end
1047
+ end
1048
+
1049
+ module_function :options, :die, :educate, :with_standard_exception_handling
1050
+ end # module
data/lib/rbcli/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rbcli
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
data/rbcli.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = 'https://github.com/akhoury6/rbcli'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.required_ruby_version = '~> 2.3'
17
+ spec.required_ruby_version = '>= 2.3'
18
18
 
19
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
20
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -40,5 +40,5 @@ Gem::Specification.new do |spec|
40
40
 
41
41
  spec.add_dependency 'colorize', '~> 0.8'
42
42
  spec.add_dependency 'deep_merge', '~> 1.2'
43
- spec.add_dependency 'trollop', '~> 2.1'
43
+ #spec.add_dependency 'trollop', '~> 2.1'
44
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbcli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Khoury
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-23 00:00:00.000000000 Z
11
+ date: 2018-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.2'
83
- - !ruby/object:Gem::Dependency
84
- name: trollop
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '2.1'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '2.1'
97
83
  description: RBCli is a framework to quickly develop command-line tools.
98
84
  email:
99
85
  - akhoury@live.com
@@ -124,6 +110,7 @@ files:
124
110
  - lib/rbcli/util/hash_deep_symbolize.rb
125
111
  - lib/rbcli/util/logging.rb
126
112
  - lib/rbcli/util/string_colorize.rb
113
+ - lib/rbcli/util/trollop.rb
127
114
  - lib/rbcli/version.rb
128
115
  - rbcli.gemspec
129
116
  homepage: https://github.com/akhoury6/rbcli
@@ -136,7 +123,7 @@ require_paths:
136
123
  - lib
137
124
  required_ruby_version: !ruby/object:Gem::Requirement
138
125
  requirements:
139
- - - "~>"
126
+ - - ">="
140
127
  - !ruby/object:Gem::Version
141
128
  version: '2.3'
142
129
  required_rubygems_version: !ruby/object:Gem::Requirement