pyer-options 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []