pyer-options 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/pyer/options.rb +385 -0
  3. metadata +73 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1156ad00624c00bc48c7c0cac0d09095f64a13fc
4
+ data.tar.gz: 6b1a39e51bbad51ac75ea03d49e5bfd514fea61a
5
+ SHA512:
6
+ metadata.gz: ade9e22ff0a6967a66b32e1b48a4a636fd20a96748537bfdfcb56799643c883976995c6308fc51526c20a4965c2e0bf4d4cd1a4dfd4ba57647daac1e7b0697ba
7
+ data.tar.gz: 985d1edffaf989e8c25033d8fe3a107acc78f440eff8c006659ac3a23b73b64c6045f18c9dba0d11fcb3fcdebdb3752a410484645500e7ea82d5594c6cd03920
@@ -0,0 +1,385 @@
1
+ module Pyer
2
+ class Options
3
+ include Enumerable
4
+
5
+ # Raised when the command starts whith '-', or is not given
6
+ class InvalidCommandError < StandardError
7
+ end
8
+
9
+ # Raised when the command is not defined
10
+ class UnknownCommandError < StandardError
11
+ end
12
+
13
+ # Raised when an invalid option is found.
14
+ class InvalidOptionError < StandardError
15
+ end
16
+
17
+ # Raised when an unknown option is found.
18
+ class UnknownOptionError < StandardError
19
+ end
20
+
21
+ # Raised when an option argument is expected but none are given.
22
+ class MissingArgumentError < StandardError
23
+ end
24
+
25
+ # Raised when an option argument starts whith '-'
26
+ class InvalidArgumentError < StandardError
27
+ end
28
+
29
+ # items - The Array of items to extract options from (default: ARGV).
30
+ # block - An optional block used to add options.
31
+ #
32
+ # Examples:
33
+ #
34
+ # Options.parse(ARGV) do
35
+ # value 'name', 'Your username'
36
+ # flag 'verbose', 'Enable verbose mode'
37
+ # end
38
+ #
39
+ # short option is the first letter of long option
40
+ # Returns a new instance of Options.
41
+ def self.parse(items = ARGV, &block)
42
+ new( &block ).parse items
43
+ end
44
+
45
+ # The Array of Options::Command objects tied to this Options instance.
46
+ attr_reader :commands
47
+
48
+ # The Array of Options::Option objects tied to this Options instance.
49
+ attr_reader :options
50
+
51
+ # Create a new instance of Options and optionally build options via a block.
52
+ #
53
+ # block - An optional block used to specify options.
54
+ def initialize(&block)
55
+ @banner = ""
56
+ @runner = nil
57
+ @commands = []
58
+ @command_name = nil
59
+ @command_callback = nil
60
+ @options = []
61
+ @triggered_options = []
62
+ @longest_cmd = 0
63
+ @longest_flag = 0
64
+
65
+ # if block_given?
66
+ # block.arity == 1 ? yield(self) : instance_eval(&block)
67
+ # end
68
+ instance_eval(&block) if block_given?
69
+ end
70
+
71
+ # Parse a list of items, executing and gathering options along the way.
72
+ #
73
+ # items - The Array of items to extract options from (default: ARGV).
74
+ # block - An optional block which when used will yield non options.
75
+ #
76
+ # Returns an Array of original items with options removed.
77
+ def parse(items = ARGV, &block)
78
+ item=items.shift
79
+ # need some help ?
80
+ if item == '?' || item == '-h' || item == '--help' || item == 'help' || item.nil?
81
+ puts self.help
82
+ exit
83
+ end
84
+ # parsing command
85
+ if !@commands.empty?
86
+ cmd = commands.find { |cmd| cmd.name == item }
87
+ raise UnknownCommandError if cmd.nil?
88
+ @command_name = cmd.name
89
+ @command_call = cmd.callback
90
+ item=items.shift
91
+ end
92
+ # parsing options
93
+ until item.nil?
94
+ #break if item == '--'
95
+ if item.match(/^--[^-]+$/).nil? && item.match(/^-[^-]$/).nil?
96
+ raise InvalidOptionError, "invalid #{item} option"
97
+ end
98
+ key = item.sub(/\A--?/, '')
99
+ option = options.find { |opt| opt.name == key || opt.short == key }
100
+ if option
101
+ @triggered_options << option
102
+ if option.expects_argument?
103
+ option.value = items.shift
104
+ raise MissingArgumentError, "missing #{item} argument" if option.value.nil?
105
+ raise InvalidArgumentError, "(#{item}=#{option.value}) argument can't start with '-'" if option.value.start_with?('-')
106
+ else
107
+ option.value = true
108
+ end
109
+ else
110
+ raise UnknownOptionError, "unknown #{item} option"
111
+ end
112
+ item=items.shift
113
+ end
114
+ if @runner.respond_to?(:call)
115
+ @runner.call(self, items)
116
+ end
117
+ # return the Options instance
118
+ self
119
+ end
120
+
121
+ # Print a handy Options help string.
122
+ #
123
+ # Returns the banner followed by available option help strings.
124
+ def help
125
+ if @commands.empty?
126
+ helpstr = "Usage: #{File.basename($0)} [options]\n"
127
+ else
128
+ helpstr = "Usage: #{File.basename($0)} command [options]\n"
129
+ end
130
+ helpstr << @banner if !@banner.empty?
131
+ if !@commands.empty?
132
+ helpstr << "Commands:\n"
133
+ commands.each { |cmd|
134
+ tab = ' ' * ( @longest_cmd + 1 - cmd.name.size )
135
+ helpstr << ' ' + cmd.name + tab + ': ' + cmd.description + "\n"
136
+ }
137
+ end
138
+ helpstr << "Options:\n"
139
+ options.each { |opt|
140
+ tab = ' ' * ( @longest_flag + 1 - opt.name.size )
141
+ if opt.expects_argument?
142
+ arg = ' <arg>'
143
+ else
144
+ arg = ' '
145
+ end
146
+ helpstr << ' -' + opt.short + '|--' + opt.name + arg + tab + ': ' + opt.description + "\n"
147
+ }
148
+ helpstr
149
+ end
150
+
151
+ # Banner
152
+ #
153
+ # Example:
154
+ # banner 'This is the banner'
155
+ #
156
+ def banner( desc = nil )
157
+ if desc.nil?
158
+ @banner
159
+ else
160
+ @banner += desc +"\n"
161
+ end
162
+ end
163
+
164
+ # Command
165
+ #
166
+ # Examples:
167
+ # command 'run', 'Running'
168
+ # command :test, 'Testing'
169
+ #
170
+ # Returns the created instance of Options::Command.
171
+ # or returns the command given in argument
172
+ #
173
+ def command(name = nil, desc = nil, &block)
174
+ if !name.nil?
175
+ @longest_cmd = name.size if name.size > @longest_cmd
176
+ cmd = Command.new(name, desc, &block)
177
+ @commands << cmd
178
+ end
179
+ @command_name
180
+ end
181
+ alias cmd command
182
+
183
+ # Call the command callback of the command given in ARGV
184
+ #
185
+ # Example:
186
+ # # show messahe when command is executed (not during parsing)
187
+ # command 'run', 'Running' do
188
+ # puts "run in progress"
189
+ # end
190
+ #
191
+ def callback
192
+ @command_call.call if @command_call.respond_to?(:call)
193
+ end
194
+
195
+ # Add a value to options
196
+ #
197
+ # Examples:
198
+ # value 'user', 'Your username'
199
+ # value :pass, 'Your password'
200
+ #
201
+ # Returns the created instance of Options::Value.
202
+ #
203
+ def value(name, desc, &block)
204
+ @longest_flag = name.size if name.size > @longest_flag
205
+ option = Value.new(name, desc, &block)
206
+ @options << option
207
+ option
208
+ end
209
+
210
+ # Add an flag to options
211
+ #
212
+ # Examples:
213
+ # flag :verbose, 'Enable verbose mode'
214
+ # flag 'debug', 'Enable debug mode'
215
+ #
216
+ # Returns the created instance of Options::Flag.
217
+ #
218
+ def flag(name, desc, &block)
219
+ @longest_flag = name.size if name.size > @longest_flag
220
+ option = Flag.new(name, desc, &block)
221
+ @options << option
222
+ option
223
+ end
224
+
225
+ # Specify code to be executed when these options are parsed.
226
+ #
227
+ # Example:
228
+ #
229
+ # opts = Options.parse do
230
+ # flag :v, :verbose
231
+ #
232
+ # run do |opts, args|
233
+ # puts "Arguments: #{args.inspect}" if opts.verbose?
234
+ # end
235
+ # end
236
+ #def run(callable = nil, &block)
237
+ def run(&block)
238
+ @runner = block if block_given?
239
+ end
240
+
241
+ # Fetch an options argument value.
242
+ #
243
+ # key - The Symbol or String option short or long flag.
244
+ #
245
+ # Returns the Object value for this option, or nil.
246
+ def [](key)
247
+ key = key.to_s
248
+ option = options.find { |opt| opt.name == key || opt.short == key }
249
+ option.value if option
250
+ end
251
+
252
+ # Enumerable interface. Yields each Options::Option.
253
+ def each(&block)
254
+ options.each(&block)
255
+ end
256
+
257
+ # Returns a new Hash with option flags as keys and option values as values.
258
+ #
259
+ # include_commands - If true, merge options from all sub-commands.
260
+ def to_hash
261
+ Hash[options.map { |opt| [opt.name.to_sym, opt.value] }]
262
+ end
263
+ alias to_h to_hash
264
+
265
+ # Fetch a list of options which were missing from the parsed list.
266
+ #
267
+ # Examples:
268
+ #
269
+ # opts = Options.new do
270
+ # value :n, :name
271
+ # value :p, :password
272
+ # end
273
+ #
274
+ # opts.parse %w[ --name Lee ]
275
+ # opts.missing #=> ['password']
276
+ #
277
+ # Returns an Array of Strings representing missing options.
278
+ def missing
279
+ (options - @triggered_options).map(&:name)
280
+ end
281
+
282
+ private
283
+ # Returns true if this option is present.
284
+ # If this method does not end with a ? character it will instead
285
+ # return the value of the option or nil
286
+ #
287
+ # Examples:
288
+ # opts.parse %( --verbose )
289
+ # opts.verbose? #=> true
290
+ # opts.other? #=> false
291
+ #
292
+ def method_missing(method)
293
+ meth = method.to_s
294
+ if meth.end_with?('?')
295
+ meth.chop!
296
+ !(@triggered_options.find { |opt| opt.name == meth }).nil?
297
+ else
298
+ o = @triggered_options.find { |opt| opt.name == meth }
299
+ # o.nil? ? super : o.value
300
+ if o.nil?
301
+ nil
302
+ else
303
+ o.callback.call if o.callback.respond_to?(:call)
304
+ o.nil? ? nil : o.value
305
+ end
306
+ end
307
+ end
308
+
309
+ class Command
310
+ attr_reader :name, :description, :callback
311
+
312
+ # Incapsulate internal command.
313
+ #
314
+ # name - The String or Symbol command name.
315
+ # description - The String description text.
316
+ # block - An optional block.
317
+ def initialize(name, description, &block)
318
+ @name = name.to_s
319
+ raise InvalidCommandError, "Command #{@name} is invalid" if @name.start_with?('-')
320
+ @description = description
321
+ @callback = (block_given? ? block : nil)
322
+ end
323
+ end
324
+
325
+ class Flag
326
+ attr_reader :short, :name, :description, :callback
327
+ attr_accessor :value
328
+
329
+ # Incapsulate internal option information, mainly used to store
330
+ # option specific configuration data, most of the meat of this
331
+ # class is found in the #value method.
332
+ #
333
+ # name - The String or Symbol option name.
334
+ # description - The String description text.
335
+ # block - An optional block.
336
+ def initialize(name, description, &block)
337
+ # Remove leading '-' from name if any
338
+ @name = name.to_s.gsub(/^--?/, '')
339
+ raise InvalidOptionError, "Option #{@name} is invalid" if @name.size < 2
340
+ @expects_argument = false
341
+ @value = false
342
+ @short = @name[0]
343
+ @description = description
344
+ @callback = (block_given? ? block : nil)
345
+ end
346
+
347
+ # Returns true if this option expects an argument.
348
+ def expects_argument?
349
+ @expects_argument
350
+ end
351
+ end
352
+
353
+ class Value
354
+ attr_reader :short, :name, :description, :callback
355
+ attr_accessor :value
356
+
357
+ # Incapsulate internal option information, mainly used to store
358
+ # option specific configuration data, most of the meat of this
359
+ # class is found in the #value method.
360
+ #
361
+ # name - The String or Symbol option name.
362
+ # description - The String description text.
363
+ # block - An optional block.
364
+ def initialize(name, description, &block)
365
+ # Remove leading '-' from name if any
366
+ @name = name.to_s.gsub(/^--?/, '')
367
+ raise InvalidOptionError, "Option #{@name} is invalid" if @name.size < 2
368
+ @expects_argument = true
369
+ @value = nil
370
+ @short = @name[0]
371
+ @description = description
372
+ @callback = (block_given? ? block : nil)
373
+ end
374
+
375
+ # Returns true if this option expects an argument.
376
+ def expects_argument?
377
+ @expects_argument
378
+ end
379
+ end
380
+
381
+ end
382
+ end
383
+
384
+ # Backward-compatible alias
385
+ Options = Pyer::Options
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pyer-options
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pierre BAZONNARD
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 5.4.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 5.4.2
41
+ description: Simple options parser inspired by slop
42
+ email:
43
+ - pierre.bazonnard@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/pyer/options.rb
49
+ homepage: https://github.com/pyer/ruby/tree/master/options
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.1.0
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.4.5
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Simple options parser
73
+ test_files: []