luban-cli 0.3.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad584f0174c5361200ed88f252387ac294ff8768
4
- data.tar.gz: 9af48be33bc18fa6d049780354d0a6dc3abb34fd
3
+ metadata.gz: 2cef4bc2d5883cf60fc715d577ce581b4b99a37d
4
+ data.tar.gz: f9621b3ad7ef039ff84595cc6dc5950059519019
5
5
  SHA512:
6
- metadata.gz: 932c919d74edc1e23cc4a45fe10240913603864aa25cf71a24b76e0a542da0a54296d20348fb5f7dc5c645b1b143e3557d897a6fbaf36df2ae88b5a0ba1bd400
7
- data.tar.gz: 889e415204354c2c6db2440e1297fb52a85e4f59a0d662707c3bc404066eeb835c592b156fc3594bb35562c9db64f463504b2ab3bb1ebd97934de4a0e0b465b1
6
+ metadata.gz: 1e7ccbaef417438934e5d2a49d587c138d7c230122d0b96241935e3cc211b38e3a755f9c7302268229ccc110ae1d9712b5d08ec7b4e271eb616947020488808a
7
+ data.tar.gz: cebee1212f04f353fefb7457e0fcd65abd89cf8d4c008139684e497862ba26309fdfbea473c060773ce1865451d6767b33f71c09cf083565efbe4ce0409b70d5
data/CHANGELOG.md CHANGED
@@ -39,3 +39,13 @@ Bug fixes:
39
39
  * Apply the correct method creator when defining action method
40
40
  * Dispatch command under the right class context
41
41
  * Show correct command chain between program name and synopsis when composing parser banner
42
+
43
+ ## Version 0.3.1 (Apr 15, 2015)
44
+
45
+ Minor enhancements:
46
+ * Simplify keyword arguments for dispatch_command and action handler
47
+ * Include command chain as part of action method name for commands
48
+ * Enrich README with more documentation
49
+
50
+ Bug fixes:
51
+ * Handle validation for multiple values correctly
data/README.md CHANGED
@@ -6,21 +6,454 @@ Luban::CLI requires Ruby 2.1 or later.
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
9
+ Add this line to your application Gemfile:
10
10
 
11
- gem 'luban-cli'
11
+ ```ruby
12
+ gem "luban-cli"
13
+ ```
12
14
 
13
15
  And then execute:
14
16
 
15
- $ bundle
17
+ ```
18
+ $ bundle
19
+ ```
16
20
 
17
21
  Or install it yourself as:
18
22
 
19
- $ gem install luban-cli
23
+ ```
24
+ $ gem install luban-cli
25
+ ```
20
26
 
21
27
  ## Usage
22
28
 
23
- TODO: Write usage instructions here
29
+ ### Simple Example
30
+
31
+ ```ruby
32
+ require 'luban/cli'
33
+
34
+ class MyApp < Luban::CLI::Application
35
+ configure do
36
+ # program "my_app"
37
+ version "1.0.0"
38
+ long_desc "Demo app for Luban::CLI"
39
+ option :prefix, "Prefix to a name, e.g., Mr, Ms, etc.", short: :p
40
+ option :suffix, "Suffix to a name, e.g., Jr, Sr, etc.", short: :s
41
+ switch :verbose, "Run in verbose mode", short: :V
42
+ argument :name, "Name to say hi"
43
+ action :say_hi
44
+ end
45
+
46
+ def say_hi(cmd:, argv:, args:, opts:)
47
+ name = compose_name(opts[:prefix], opts[:suffix], args[:name])
48
+ if opts[:verbose]
49
+ say_hi_verbosely(name, opts, args)
50
+ else
51
+ say_hi_concisely(name)
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def say_hi_verbosely(name, opts, args)
58
+ puts "Options: #{opts.inspect}"
59
+ puts "Arguments: #{args.inspect}"
60
+ say_hi_concisely(name)
61
+ end
62
+
63
+ def say_hi_concisely(name)
64
+ puts "Hi, #{name}!"
65
+ end
66
+
67
+ def compose_name(prefix, suffix, name)
68
+ name = name.capitalize
69
+ name = "#{prefix.capitalize}. #{name}" unless prefix.nil?
70
+ name = "#{name} #{suffix.capitalize}." unless suffix.nil?
71
+ name
72
+ end
73
+ end
74
+
75
+ MyApp.new.run
76
+ ```
77
+
78
+ ```
79
+ $ ruby my_app.rb -h
80
+ Usage: my_app [options] NAME
81
+
82
+ Options:
83
+ -v, --version Show hi version.
84
+ -p, --prefix PREFIX Prefix to a name, e.g., Mr, Ms, etc.
85
+ -s, --suffix SUFFIX Suffix to a name, e.g., Jr, Sr, etc.
86
+ -V, --verbose Run in verbose mode
87
+ -h, --help Show this help message.
88
+
89
+ Arguments:
90
+ NAME Name to say hi
91
+
92
+ Description:
93
+ Demo app for Luban::CLI
94
+
95
+ $ ruby my_app.rb -v
96
+ my_app 1.0.0
97
+
98
+ $ ruby my_app.rb john -p mr -s jr
99
+ Hi, Mr. John Jr.!
100
+
101
+ ruby examples/hi.rb john -p mr -s jr -V
102
+ Options: {:version=>false, :prefix=>"mr", :suffix=>"jr", :verbose=>true, :help=>false}
103
+ Arguments: {:name=>"chi"}
104
+ Hi, Mr. John Jr.!
105
+ ```
106
+
107
+ Please refer to [examples](examples) for more sample usage.
108
+
109
+ ## DSL
110
+
111
+ The following is an overview of the Luban::CLI DSL.
112
+
113
+ ### program
114
+
115
+ The name of the application. Default: $0.
116
+
117
+ ### desc
118
+
119
+ A short description of what the application does.
120
+
121
+ ### long_desc
122
+
123
+ A long description of what the application does.
124
+
125
+ ### argument
126
+
127
+ An arguement is a positioned parameter passed from command-line. To declare an argument:
128
+
129
+ ```ruby
130
+ argument :name, 'description', **modifiers, &blk
131
+ ```
132
+
133
+ The modifiers below can be used:
134
+
135
+ * :default - Default value for the argument.
136
+ * :required - Flag to indicate if the argument is mandatory or not. Default: true.
137
+ * :multiple - Flag to indicate argument is a list of values or a single value. Default: false.
138
+ * :type - Value type for the argument. Default: :string.
139
+ * :match - A regex for argument value matching.
140
+ * :within - A range or a list of values the argument value to be within.
141
+ * :assure - A code block for argument value validation.
142
+
143
+ If required argument is not provided, Luban::CLI::Base::MissingRequiredArguments will be raised.
144
+
145
+ If validation failed, Luban::CLI::Argument::InvalidArgumentValue will be raised.
146
+
147
+ Note: An argument with multiple values needs to be positioned at the last argument. Furthermore, you cannot specify more than one arguements with multiple values.
148
+
149
+ Here is an example how to use argument:
150
+
151
+ ```ruby
152
+ class MyApp < Luban::CLI::Application
153
+ configure do
154
+ argument :name, 'Name for an employee'
155
+ argument :gender, 'Gender for an employee',
156
+ type: :symbol, within: [:male, :femal]
157
+ argument :age, 'Age for an employee',
158
+ type: :integer, assure: ->(age) { age < 60 }
159
+ argument :level, 'Level for an employee',
160
+ type: :integer, within: 1..4
161
+ argument :email, 'Email for an employee',
162
+ match: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i,
163
+ multiple: true, required: false
164
+ action do |**params|
165
+ puts params.inspect
166
+ end
167
+ end
168
+ end
169
+
170
+ MyApp.new.run
171
+ ```
172
+
173
+ ```
174
+ $ ruby my_app.rb
175
+ Missing required argument(s): NAME, GENDER, LEVEL (Luban::CLI::Base::MissingRequiredArguments)
176
+ ... ...
177
+
178
+ $ ruby my_app.rb john male 90
179
+ Invalid value of argument AGE: 90 (Luban::CLI::Argument::InvalidArgumentValue)
180
+ ... ...
181
+
182
+ $ ruby my_app.rb john male 30 2
183
+ {:cmd=>nil, :argv=>[], :args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>nil}, :opts=>{:help=>false}}
184
+
185
+ $ ruby my_app.rb john male 30 2 john@company.com
186
+ {:cmd=>nil, :argv=>[], :args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>["john@company.com"]}, :opts=>{:help=>false}}
187
+
188
+ $ ruby my_app.rb john male 30 2 john@company.com john@personal.com
189
+ {:cmd=>nil, :argv=>[], :args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>["john@company.com", "john@personal.com"]}, :opts=>{:help=>false}}
190
+ ```
191
+
192
+ ### option
193
+
194
+ An option usually takes an argument, e.g. --require LIBRARIES. To declare an option:
195
+
196
+ ```ruby
197
+ option :name, 'description', **modifiers, &blk
198
+ ```
199
+
200
+ The extra modifiers below can be used along with all modifiers applicable to arguments:
201
+
202
+ * :long - Long style argument name.
203
+ * :short - Short style argument alias.
204
+
205
+ Note: modifier :required is turned off by default. Therefore, all options are not mandatory unless they are declared explicitly.
206
+
207
+ Here is an example how to use option:
208
+
209
+ ```ruby
210
+ class MyApp < Luban::CLI::Application
211
+ configure do
212
+ option :libraries, 'Require the LIBRARIES before executing your script',
213
+ long: :require, short: :r, multiple: true
214
+ action do |**params|
215
+ puts params.inspect
216
+ end
217
+ end
218
+ end
219
+
220
+ MyApp.new.run
221
+ ```
222
+
223
+ ```
224
+ $ ruby my_app.rb --require bundler
225
+ {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:libraries=>["bundler"], :help=>false}}
226
+
227
+ $ ruby my_app.rb -r bundler,rails
228
+ {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:libraries=>["bundler", "rails"], :help=>false}}
229
+ ```
230
+
231
+ Occassionally an option might take an optional argument, e.g. --inplace [EXTENSION]. This kind of option is called nullable option. The nullable option is set to true if the optional argument is not provided; otherwise, the value of the option is set to the value of the argument. To declare a nullable option, you can explicitly turn off nullable modifier which is off by default.
232
+
233
+ Here is an example how to use nullable option:
234
+
235
+ ```ruby
236
+ class MyApp < Luban::CLI::Application
237
+ configure do
238
+ option :inplace, 'Edit in place (make backup if EXTENSION supplied)',
239
+ nullable: true # Turn the option into nullable
240
+ action do |**params|
241
+ puts params.inspect
242
+ end
243
+ end
244
+ end
245
+
246
+ MyApp.new.run
247
+ ```
248
+
249
+ ```
250
+ $ ruby my_app.rb
251
+ {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>nil, :help=>false}}
252
+
253
+ $ ruby my_app.rb --inplace
254
+ {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>true, :help=>false}}
255
+
256
+ $ ruby my_app.rb --inplace .bak
257
+ {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>".bak", :help=>false}}
258
+ ```
259
+
260
+ ### switch
261
+
262
+ A switch is a special option that doesn't take any arguments, e.g., --verbose, --help, etc. To declare a switch:
263
+
264
+ ```ruby
265
+ switch :name, 'description', **modifiers, &blk
266
+ ```
267
+
268
+ All modifiers applied to an option can be used for a switch except the following:
269
+
270
+ * :type - Type for switch is set to :bool (true/false) and it cannot be changed.
271
+ * :multiple - Set to false to ensure to handle a single value and it cannot be changed.
272
+
273
+ Negatable switch is also supported, e.g., --local, --no-local. To declare a negatable switch, you can explicitly turn on negatable modifier which is off by default.
274
+
275
+ Here is an example how to use negatable option:
276
+
277
+ ```ruby
278
+ class MyApp < Luban::CLI::Application
279
+ configure do
280
+ switch :local, 'Check repository locally',
281
+ negatable: true # Turn the switch into negatable: --local or --no-local
282
+ action do |**params|
283
+ puts params.inspect
284
+ end
285
+ end
286
+
287
+ MyApp.new.run
288
+ ```
289
+
290
+ ### help
291
+
292
+ Add a switch for help message display. The modifiers below can be used:
293
+
294
+ * :short - Short style switch for help display. Default: :h
295
+ * :desc - Set a switch description for help display. Default: "Show this help message."
296
+
297
+ DSL alias #auto_help is provided which applies default values for the above options.
298
+
299
+ The switch of help display is turned on by default unless explicitly turning off when creating an application or a command.
300
+
301
+ ```ruby
302
+ class MyApp < Luban::CLI::Application
303
+ ... ...
304
+ end
305
+
306
+ MyApp.new(auto_help: false)
307
+ ```
308
+
309
+ ### version
310
+
311
+ Specify or retrieve the version of the application.
312
+
313
+ If calling without any parameters, version previously set is returned.
314
+
315
+ If calling with a version, a switch for version display is added to the application. The modifiers below can be used:
316
+
317
+ * :short - Set a short style switch for version display. Default: :v
318
+ * :desc - Set a switch description for version disiplay. Default: "Show #{program_name} version."
319
+
320
+ ### action
321
+
322
+ Specify handler for the CLI application or command.
323
+
324
+ It accepts a code block or an instance method name from the application as the action handler. If a code block is given, the code block is executed in the binding of the application. If both a code block and an instance method name are provided, the instance method name is used and the code block is ignored.
325
+
326
+ The handler accepts the following keyword arguments:
327
+
328
+ * :args - arguments parsed from the command-line
329
+ * :opts - options parsed from the command-line
330
+
331
+
332
+ ### action!
333
+
334
+ Same as action, but removes command-line arguments destructively.
335
+
336
+ ### configure
337
+
338
+ This is used to configure CLI application during definition.
339
+
340
+ Here is an example for how to configure CLI application:
341
+
342
+ ```ruby
343
+
344
+ class MyApp < Luban::CLI::Application
345
+ configure do
346
+ program "my_app"
347
+ version "1.0.0"
348
+ long_desc "Demo app for Luban::CLI"
349
+ option :opt1, "Description for opt1", short: :o
350
+ switch :swt1, "Description for swt1", short: :s
351
+ argument :arg1, "Description to arg1"
352
+ action :do_something
353
+ end
354
+
355
+ def do_something(args:, opts:)
356
+ ... ...
357
+ end
358
+ end
359
+
360
+ MyApp.new.run
361
+
362
+ ```
363
+
364
+ However, it can be overriden if a configuration block is provided during application instance creation:
365
+
366
+ ```ruby
367
+ MyApp.new.run do
368
+ program "my_app"
369
+ version "1.0.0"
370
+ long_desc "Demo app for Luban::CLI"
371
+ option :opt2, "Description for opt2", short: :p
372
+ switch :swt2, "Description for swt2", short: :w
373
+ action :do_something_else
374
+ end
375
+
376
+ ```
377
+
378
+ ## Commands
379
+
380
+ Luban::CLI supports commands/subcommands. Commands can also be nested. However, all action handlers should be defined under the the application class, otherwise NoMethodError will be raised.
381
+
382
+ ```ruby
383
+ class MyApp < Luban::CLI::Application
384
+ configure do
385
+ version '1.0.0'
386
+ desc 'Short description for the application'
387
+ long_desc 'Long description for the application'
388
+ end
389
+
390
+ # Define a help command to list all commands or help for one command.
391
+ auto_help_command
392
+
393
+ command :cmd1 do
394
+ desc 'Description for command 1'
395
+
396
+ command :task1 do
397
+ desc 'Description for task 1'
398
+ argument :arg1, 'Description for arg1', type: :string
399
+ action :exec_command1_task1
400
+ end
401
+
402
+ command :task2 do
403
+ desc 'Description for task 2'
404
+ option :opt1, 'Description for opt1', type: :integer
405
+ action :exec_command1_task2
406
+ end
407
+ end
408
+
409
+ command :cmd2 do
410
+ desc 'Description for command 1'
411
+ switch :swt1, 'Description for swt1'
412
+ action :exec_command2
413
+ end
414
+
415
+ def exec_command1_task1(args:, opts:); puts 'In command 1/task 1'; end
416
+ def exec_command1_task2(args:, opts:); puts 'In command 1/task 2'; end
417
+ def exec_command2(args:, opts:); puts 'In command 2'; end
418
+ end
419
+
420
+ MyApp.new.run
421
+ ```
422
+
423
+ ### Command method/handler
424
+
425
+ By default, a new method will be defined for each command under the application class. Usually you don't need to call this method directly. Luban::CLI dispatches the specified command to the corresponding command method/handler properly.
426
+
427
+ The command method name is composed of the following, concatenating with an underscore:
428
+
429
+ * prefix - Default prefix is "__command_".
430
+ * command chain
431
+ * For regular command, it is the command name itself
432
+ * For nested commands, it is the commands from the top to the bottom one
433
+
434
+ In the example above, there are following command methods defined in MyApp:
435
+
436
+ * __command_cmd1_task1
437
+ * __command_cmd1_task2
438
+ * __command_cmd2
439
+
440
+ You can also change the prefix to your preferred one by setting the modifier :prefix when defining a command:
441
+
442
+ ```ruby
443
+ command :cmd1, prefix: '__my_prefix_' do
444
+ ... ...
445
+ end
446
+ ```
447
+
448
+ ### auto_help_command / help_command
449
+
450
+ DSL method #auto_help_command is used to define a command to list all commands or help for one command. Under rare circumstances that you need to customize the help command (i.e., use a different command name like :manual), you can use DSL method #help_command which accepts the same parameters that for #command.
451
+
452
+ ## Applications
453
+
454
+ Luban::CLI provides a base class for cli application, Luban::CLI::Application. You can define your own cli application by inheriting it as examples shown in the previous sections.
455
+
456
+ In addition to command-line argument parsing capabilities, Luban::CLI::Application also supports a rc file. For example, if an application called "my_app.rb", it looks up rc file ".my_apprc" under user home when the application starts up. The rc file uses YML format. If rc file is found, the content will be loaded into the instance variable :rc; if rc file is not found, the instance variable :rc will be initialized as an empty hash.
24
457
 
25
458
  ## Contributing
26
459
 
@@ -49,7 +49,9 @@ module Luban
49
49
  end
50
50
 
51
51
  def valid?(value = @value)
52
- !missing?(value) and match?(value) and within?(value) and assured?(value)
52
+ (multiple? ? value : [value]).all? do |v|
53
+ !missing?(v) and match?(v) and within?(v) and assured?(v)
54
+ end
53
55
  end
54
56
 
55
57
  def missing?(value = @value)
@@ -113,7 +115,7 @@ module Luban
113
115
  unless err_msg.nil?
114
116
  raise ArgumentError, "Default value for #{kind} #{display_name} #{err_msg}"
115
117
  end
116
- unless (multiple? ? default : [default]).all? { |v| valid?(v) }
118
+ unless valid?(default)
117
119
  raise ArgumentError, "Invalid default value for #{kind} #{display_name}: #{default.inspect}"
118
120
  end
119
121
  end
@@ -21,7 +21,6 @@ module Luban
21
21
 
22
22
  attr_reader :app
23
23
  attr_reader :prefix
24
- attr_reader :action_method
25
24
  attr_reader :program_name
26
25
  attr_reader :options
27
26
  attr_reader :arguments
@@ -39,7 +38,6 @@ module Luban
39
38
  @app = app
40
39
  @action_name = action_name
41
40
  @prefix = prefix
42
- @action_method = "#{@prefix}#{@action_name}"
43
41
  @action_defined = false
44
42
 
45
43
  @program_name = default_program_name
@@ -62,6 +60,10 @@ module Luban
62
60
 
63
61
  def default_prefix; ''; end
64
62
 
63
+ def action_method
64
+ @action_method ||= "#{@prefix}#{@action_name}"
65
+ end
66
+
65
67
  def parser
66
68
  @parser ||= create_parser
67
69
  end
@@ -90,7 +92,7 @@ module Luban
90
92
  end
91
93
  end
92
94
 
93
- def dispatch_command(context, cmd:, argv:, **params)
95
+ def dispatch_command(context, cmd:, argv:)
94
96
  validate_command(cmd)
95
97
  context.send(commands[cmd].action_method, argv)
96
98
  end
@@ -103,8 +103,8 @@ module Luban
103
103
  _base = self
104
104
  parse_method = preserve_argv ? :parse : :parse!
105
105
  define_action_method do |argv = _base.default_argv|
106
- _base.send(:process, self, parse_method, argv) do |result|
107
- instance_exec(**result, &handler)
106
+ _base.send(:process, self, parse_method, argv) do |params|
107
+ instance_exec(**params, &handler)
108
108
  end
109
109
  end
110
110
  @action_defined = true
@@ -126,11 +126,11 @@ module Luban
126
126
  show_version
127
127
  else
128
128
  if has_commands?
129
- dispatch_command(context, **result)
129
+ dispatch_command(context, cmd: result[:cmd], argv: result[:argv])
130
130
  else
131
131
  validate_required_options
132
132
  validate_required_arguments
133
- yield result
133
+ yield args: result[:args], opts: result[:opts]
134
134
  end
135
135
  end
136
136
  rescue OptionParser::ParseError, Error => e
@@ -12,6 +12,10 @@ module Luban
12
12
 
13
13
  def default_prefix; '__command_'; end
14
14
 
15
+ def action_method
16
+ @action_method ||= "#{@prefix}#{command_chain.map(&:to_s).join('_')}"
17
+ end
18
+
15
19
  protected
16
20
 
17
21
  def compose_banner
@@ -1,5 +1,5 @@
1
1
  module Luban
2
2
  module CLI
3
- VERSION = "0.3.0"
3
+ VERSION = "0.3.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: luban-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rubyist Chi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-11 00:00:00.000000000 Z
11
+ date: 2015-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler