luban-cli 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +61 -45
- data/lib/luban/cli/application.rb +10 -4
- data/lib/luban/cli/base/argument.rb +1 -1
- data/lib/luban/cli/base/commands.rb +72 -0
- data/lib/luban/cli/base/core.rb +68 -18
- data/lib/luban/cli/base/dsl.rb +34 -33
- data/lib/luban/cli/base.rb +1 -0
- data/lib/luban/cli/command.rb +7 -2
- data/lib/luban/cli/version.rb +1 -1
- data/lib/luban/cli.rb +0 -1
- metadata +3 -3
- data/lib/luban/cli/commands.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3717edf51bdf2888b4d7d7d378b8ba777e7f2df
|
4
|
+
data.tar.gz: 23c48793a99823629c9b339a7b65f64f3be9dc27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5dd0736e211101a179af588fab6892445fc3e6b77d4203237f383cb14267da9ce61dbdc565c6ffa8cf4a46fbf7feaba43c1347afbfc8cb27afa0b00e42222ab
|
7
|
+
data.tar.gz: abe9f13478171fd6c9bb4b5163f73919666b2f830c5a43896812da9e09e938da9ed16c23699486657b94752ef68849f902ff5384ffee6dcb6bce5e1026f636fa
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## Version 0.4.0 (Oct 14, 2015)
|
4
|
+
|
5
|
+
New features:
|
6
|
+
* Provide convenient method :start to create and run new instance of CLI application
|
7
|
+
* Added method #alter to allow change cli configuration during runtime
|
8
|
+
|
9
|
+
Minor enhancements:
|
10
|
+
* Merge Commands module into Luban::CLI::Base
|
11
|
+
* Handled commands in instance level instead of class level
|
12
|
+
* Handled command definition and action method on its eigenclass
|
13
|
+
* Enhanced action method invocation to look up thru its parent
|
14
|
+
* Update examples accordingly
|
15
|
+
* Some other minor refactoring
|
16
|
+
|
3
17
|
## Version 0.3.2 (Apr 28, 2015)
|
4
18
|
|
5
19
|
Minor enhancements:
|
data/README.md
CHANGED
@@ -43,7 +43,7 @@ class MyApp < Luban::CLI::Application
|
|
43
43
|
action :say_hi
|
44
44
|
end
|
45
45
|
|
46
|
-
def say_hi(
|
46
|
+
def say_hi(args:, opts:)
|
47
47
|
name = compose_name(opts[:prefix], opts[:suffix], args[:name])
|
48
48
|
if opts[:verbose]
|
49
49
|
say_hi_verbosely(name, opts, args)
|
@@ -72,7 +72,7 @@ class MyApp < Luban::CLI::Application
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
MyApp.
|
75
|
+
MyApp.start
|
76
76
|
```
|
77
77
|
|
78
78
|
```
|
@@ -149,6 +149,8 @@ Note: An argument with multiple values needs to be positioned at the last argume
|
|
149
149
|
Here is an example how to use argument:
|
150
150
|
|
151
151
|
```ruby
|
152
|
+
require 'luban/cli'
|
153
|
+
|
152
154
|
class MyApp < Luban::CLI::Application
|
153
155
|
configure do
|
154
156
|
argument :name, 'Name for an employee'
|
@@ -167,7 +169,7 @@ class MyApp < Luban::CLI::Application
|
|
167
169
|
end
|
168
170
|
end
|
169
171
|
|
170
|
-
MyApp.
|
172
|
+
MyApp.start
|
171
173
|
```
|
172
174
|
|
173
175
|
```
|
@@ -207,6 +209,8 @@ Note: modifier :required is turned off by default. Therefore, all options are no
|
|
207
209
|
Here is an example how to use option:
|
208
210
|
|
209
211
|
```ruby
|
212
|
+
require 'luban/cli'
|
213
|
+
|
210
214
|
class MyApp < Luban::CLI::Application
|
211
215
|
configure do
|
212
216
|
option :libraries, 'Require the LIBRARIES before executing your script',
|
@@ -217,7 +221,7 @@ class MyApp < Luban::CLI::Application
|
|
217
221
|
end
|
218
222
|
end
|
219
223
|
|
220
|
-
MyApp.
|
224
|
+
MyApp.start
|
221
225
|
```
|
222
226
|
|
223
227
|
```
|
@@ -233,6 +237,8 @@ Occassionally an option might take an optional argument, e.g. --inplace [EXTENSI
|
|
233
237
|
Here is an example how to use nullable option:
|
234
238
|
|
235
239
|
```ruby
|
240
|
+
require 'luban/cli'
|
241
|
+
|
236
242
|
class MyApp < Luban::CLI::Application
|
237
243
|
configure do
|
238
244
|
option :inplace, 'Edit in place (make backup if EXTENSION supplied)',
|
@@ -243,7 +249,7 @@ class MyApp < Luban::CLI::Application
|
|
243
249
|
end
|
244
250
|
end
|
245
251
|
|
246
|
-
MyApp.
|
252
|
+
MyApp.start
|
247
253
|
```
|
248
254
|
|
249
255
|
```
|
@@ -275,16 +281,19 @@ Negatable switch is also supported, e.g., --local, --no-local. To declare a nega
|
|
275
281
|
Here is an example how to use negatable option:
|
276
282
|
|
277
283
|
```ruby
|
284
|
+
require 'luban/cli'
|
285
|
+
|
278
286
|
class MyApp < Luban::CLI::Application
|
279
287
|
configure do
|
280
288
|
switch :local, 'Check repository locally',
|
281
289
|
negatable: true # Turn the switch into negatable: --local or --no-local
|
282
|
-
|
283
|
-
|
290
|
+
action do |**params|
|
291
|
+
puts params.inspect
|
292
|
+
end
|
284
293
|
end
|
285
294
|
end
|
286
295
|
|
287
|
-
MyApp.
|
296
|
+
MyApp.start
|
288
297
|
```
|
289
298
|
|
290
299
|
### help
|
@@ -299,11 +308,13 @@ DSL alias #auto_help is provided which applies default values for the above opti
|
|
299
308
|
The switch of help display is turned on by default unless explicitly turning off when creating an application or a command.
|
300
309
|
|
301
310
|
```ruby
|
311
|
+
require 'luban/cli'
|
312
|
+
|
302
313
|
class MyApp < Luban::CLI::Application
|
303
314
|
... ...
|
304
315
|
end
|
305
316
|
|
306
|
-
MyApp.
|
317
|
+
MyApp.start(auto_help: false)
|
307
318
|
```
|
308
319
|
|
309
320
|
### version
|
@@ -340,6 +351,7 @@ This is used to configure CLI application during definition.
|
|
340
351
|
Here is an example for how to configure CLI application:
|
341
352
|
|
342
353
|
```ruby
|
354
|
+
require 'luban/cli'
|
343
355
|
|
344
356
|
class MyApp < Luban::CLI::Application
|
345
357
|
configure do
|
@@ -357,59 +369,61 @@ class MyApp < Luban::CLI::Application
|
|
357
369
|
end
|
358
370
|
end
|
359
371
|
|
360
|
-
MyApp.
|
372
|
+
MyApp.start
|
361
373
|
|
362
374
|
```
|
363
375
|
|
364
376
|
However, it can be overriden if a configuration block is provided during application instance creation:
|
365
377
|
|
366
378
|
```ruby
|
367
|
-
MyApp.
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
379
|
+
MyApp.start do
|
380
|
+
program "my_app"
|
381
|
+
version "1.0.0"
|
382
|
+
long_desc "Demo app for Luban::CLI"
|
383
|
+
option :opt2, "Description for opt2", short: :p
|
384
|
+
switch :swt2, "Description for swt2", short: :w
|
385
|
+
action :do_something_else
|
374
386
|
end
|
375
387
|
|
376
388
|
```
|
377
389
|
|
378
390
|
## Commands
|
379
391
|
|
380
|
-
Luban::CLI supports commands/subcommands. Commands can also be nested. However, all action handlers
|
392
|
+
Luban::CLI supports commands/subcommands. Commands can also be nested. However, all action handlers are preferred to be defined under the the application class; otherwise RuntimeError will be raised.
|
381
393
|
|
382
394
|
```ruby
|
395
|
+
require 'luban/cli'
|
396
|
+
|
383
397
|
class MyApp < Luban::CLI::Application
|
384
398
|
configure do
|
385
399
|
version '1.0.0'
|
386
400
|
desc 'Short description for the application'
|
387
401
|
long_desc 'Long description for the application'
|
388
|
-
end
|
389
402
|
|
390
|
-
|
391
|
-
|
403
|
+
command :cmd1 do
|
404
|
+
desc 'Description for command 1'
|
392
405
|
|
393
|
-
|
394
|
-
|
406
|
+
command :task1 do
|
407
|
+
desc 'Description for task 1'
|
408
|
+
argument :arg1, 'Description for arg1', type: :string
|
409
|
+
action :exec_command1_task1
|
410
|
+
end
|
395
411
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
412
|
+
command :task2 do
|
413
|
+
desc 'Description for task 2'
|
414
|
+
option :opt1, 'Description for opt1', type: :integer
|
415
|
+
action :exec_command1_task2
|
416
|
+
end
|
400
417
|
end
|
401
418
|
|
402
|
-
command :
|
403
|
-
desc 'Description for
|
404
|
-
|
405
|
-
action :
|
419
|
+
command :cmd2 do
|
420
|
+
desc 'Description for command 1'
|
421
|
+
switch :swt1, 'Description for swt1'
|
422
|
+
action :exec_command2
|
406
423
|
end
|
407
|
-
end
|
408
424
|
|
409
|
-
|
410
|
-
|
411
|
-
switch :swt1, 'Description for swt1'
|
412
|
-
action :exec_command2
|
425
|
+
# Define a help command to list all commands or help for one command.
|
426
|
+
auto_help_command
|
413
427
|
end
|
414
428
|
|
415
429
|
def exec_command1_task1(args:, opts:); puts 'In command 1/task 1'; end
|
@@ -417,12 +431,12 @@ class MyApp < Luban::CLI::Application
|
|
417
431
|
def exec_command2(args:, opts:); puts 'In command 2'; end
|
418
432
|
end
|
419
433
|
|
420
|
-
MyApp.
|
434
|
+
MyApp.start
|
421
435
|
```
|
422
436
|
|
423
437
|
### Command method/handler
|
424
438
|
|
425
|
-
By default, a new method will be defined for each command under the application
|
439
|
+
By default, a new method will be defined for each command under the application/command instance's eigenclass. Usually you don't need to call this method directly. Luban::CLI dispatches the specified command to the corresponding command method/handler properly.
|
426
440
|
|
427
441
|
The command method name is composed of the following, concatenating with an underscore:
|
428
442
|
|
@@ -456,15 +470,17 @@ Commands can be defined directly within the Luban app class like examples shown
|
|
456
470
|
Below is an example demonstrating how command injection is supposed to work.
|
457
471
|
|
458
472
|
```ruby
|
473
|
+
require 'luban/cli'
|
474
|
+
|
459
475
|
module App
|
460
476
|
module ControlTasks
|
461
|
-
class
|
477
|
+
class Task1 < Luban::CLI::Command
|
462
478
|
configure do
|
463
479
|
... ...
|
464
480
|
end
|
465
481
|
end
|
466
482
|
|
467
|
-
class
|
483
|
+
class Task2 < Luban::CLI::Command
|
468
484
|
configure do
|
469
485
|
... ...
|
470
486
|
end
|
@@ -485,6 +501,8 @@ class MyApp < Luban::CLI::Application
|
|
485
501
|
# command 'app:control_tasks:task1'
|
486
502
|
# command 'app:control_tasks:task2'
|
487
503
|
end
|
504
|
+
|
505
|
+
MyApp.start
|
488
506
|
```
|
489
507
|
|
490
508
|
```
|
@@ -495,16 +513,14 @@ $ ruby my_app.rb app:control_tasks:task2
|
|
495
513
|
|
496
514
|
As shown above, there are a few naming conventions about the command class naming and injection:
|
497
515
|
|
498
|
-
* Command class should has suffix "Command"
|
499
|
-
* For instances, Task1Command, Task2Command
|
500
516
|
* Module name to be injected should be fully qualified
|
501
517
|
* namespaces/modules should be in snake case and
|
502
518
|
* Separated by colon as the delimiter
|
503
|
-
* For
|
519
|
+
* For instance, 'app:control_tasks'
|
504
520
|
* Alternatively, commands can be injected individually
|
505
521
|
* Command name should be fully qualified
|
506
|
-
*
|
507
|
-
|
522
|
+
* For instance, 'app:control_tasks:task2'
|
523
|
+
|
508
524
|
## Applications
|
509
525
|
|
510
526
|
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.
|
@@ -3,12 +3,16 @@ require 'pathname'
|
|
3
3
|
module Luban
|
4
4
|
module CLI
|
5
5
|
class Application < Base
|
6
|
+
class << self
|
7
|
+
def start(action_name = :run, **opts, &config_blk)
|
8
|
+
new(action_name, **opts, &config_blk).send(action_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
6
12
|
attr_reader :rc
|
7
13
|
|
8
14
|
def initialize(action_name = :run, **opts, &config_blk)
|
9
15
|
super(self, action_name, **opts, &config_blk)
|
10
|
-
@rc = init_rc
|
11
|
-
validate
|
12
16
|
end
|
13
17
|
|
14
18
|
def rc_file
|
@@ -29,7 +33,9 @@ module Luban
|
|
29
33
|
|
30
34
|
protected
|
31
35
|
|
32
|
-
def
|
36
|
+
def on_configure
|
37
|
+
@rc = init_rc
|
38
|
+
end
|
33
39
|
|
34
40
|
def init_rc
|
35
41
|
if rc_file_exists?
|
@@ -45,4 +51,4 @@ module Luban
|
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|
48
|
-
end
|
54
|
+
end
|
@@ -134,7 +134,7 @@ module Luban
|
|
134
134
|
|
135
135
|
def verify_config_assurance
|
136
136
|
unless @config[:assure].respond_to?(:call)
|
137
|
-
raise ArgumentError, "Assurance of #{kind} #{display_name} must
|
137
|
+
raise ArgumentError, "Assurance of #{kind} #{display_name} must respond to #call."
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Luban
|
2
|
+
module CLI
|
3
|
+
class Base
|
4
|
+
def command(cmd, base: Command, **opts, &blk)
|
5
|
+
cmd = cmd.to_sym
|
6
|
+
@commands[cmd] = command_class(cmd, base).new(self, cmd, **opts, &blk)
|
7
|
+
end
|
8
|
+
|
9
|
+
def undef_command(cmd)
|
10
|
+
undef_singleton_method(@commands.delete(cmd.to_sym).action_method)
|
11
|
+
end
|
12
|
+
|
13
|
+
def use_commands(module_name, **opts, &blk)
|
14
|
+
module_class = Object.const_get(camelcase(module_name.to_s), false)
|
15
|
+
module_class.constants(false).map { |c| module_class.const_get(c, false) }.each do |c|
|
16
|
+
command(snakecase(c.name), base: c, **opts, &blk) if c < Command
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def list_commands
|
21
|
+
@commands.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_command?(cmd)
|
25
|
+
@commands.has_key?(cmd.to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_commands?
|
29
|
+
!@commands.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def command_class(cmd, base)
|
35
|
+
class_name = camelcase(cmd)
|
36
|
+
if command_class_defined?(class_name)
|
37
|
+
get_command_class(class_name)
|
38
|
+
else
|
39
|
+
define_command_class(class_name, base)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def command_class_defined?(class_name)
|
44
|
+
self.class.const_defined?(class_name, false)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_command_class(class_name)
|
48
|
+
self.class.const_get(class_name, false)
|
49
|
+
end
|
50
|
+
|
51
|
+
def define_command_class(class_name, base)
|
52
|
+
self.class.send(:define_class, class_name, base: base, namespace: self.class)
|
53
|
+
end
|
54
|
+
|
55
|
+
def camelcase(str)
|
56
|
+
str = str.to_s.dup
|
57
|
+
str.gsub!(/(\:|\/)(.?)/){ "::#{$2.upcase}" }
|
58
|
+
str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
|
59
|
+
str.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase }
|
60
|
+
str
|
61
|
+
end
|
62
|
+
|
63
|
+
def snakecase(str)
|
64
|
+
str.gsub(/::/, ':').
|
65
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
66
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
67
|
+
tr("-", "_").
|
68
|
+
downcase
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/luban/cli/base/core.rb
CHANGED
@@ -8,8 +8,6 @@ OptionParser::Officious.delete('version')
|
|
8
8
|
module Luban
|
9
9
|
module CLI
|
10
10
|
class Base
|
11
|
-
include Commands
|
12
|
-
|
13
11
|
class MissingCommand < Error; end
|
14
12
|
class InvalidCommand < Error; end
|
15
13
|
class MissingRequiredOptions < Error; end
|
@@ -19,7 +17,7 @@ module Luban
|
|
19
17
|
DefaultSummaryIndent = 4
|
20
18
|
DefaultTitleIndent = 2
|
21
19
|
|
22
|
-
attr_reader :
|
20
|
+
attr_reader :parent
|
23
21
|
attr_reader :prefix
|
24
22
|
attr_reader :program_name
|
25
23
|
attr_reader :options
|
@@ -29,13 +27,14 @@ module Luban
|
|
29
27
|
attr_reader :version
|
30
28
|
attr_reader :result
|
31
29
|
attr_reader :default_argv
|
30
|
+
attr_reader :commands
|
32
31
|
|
33
32
|
attr_accessor :title_indent
|
34
33
|
attr_accessor :summary_width
|
35
34
|
attr_accessor :summary_indent
|
36
35
|
|
37
|
-
def initialize(
|
38
|
-
@
|
36
|
+
def initialize(parent, action_name, prefix: default_prefix, auto_help: true, &config_blk)
|
37
|
+
@parent = parent
|
39
38
|
@action_name = action_name
|
40
39
|
@prefix = prefix
|
41
40
|
@action_defined = false
|
@@ -48,6 +47,7 @@ module Luban
|
|
48
47
|
@version = ''
|
49
48
|
@default_argv = ARGV
|
50
49
|
@result = { cmd: nil, argv: @default_argv, args: {}, opts: {} }
|
50
|
+
@commands = {}
|
51
51
|
|
52
52
|
@title_indent = DefaultTitleIndent
|
53
53
|
@summary_width = DefaultSummaryWidth
|
@@ -78,36 +78,86 @@ module Luban
|
|
78
78
|
@result = { cmd: nil, argv: @default_argv, args: {}, opts: {} }
|
79
79
|
end
|
80
80
|
|
81
|
+
def alter(&blk)
|
82
|
+
instance_eval(&blk)
|
83
|
+
on_alter
|
84
|
+
end
|
85
|
+
|
81
86
|
protected
|
82
87
|
|
88
|
+
def self.define_class(class_name, base:, namespace:)
|
89
|
+
mods = class_name.split('::')
|
90
|
+
cmd_class = mods.pop
|
91
|
+
mods.inject(namespace) do |ns, mod|
|
92
|
+
ns.const_set(mod, Module.new) unless ns.const_defined?(mod, false)
|
93
|
+
ns.const_get(mod, false)
|
94
|
+
end.const_set(cmd_class, Class.new(base))
|
95
|
+
end
|
96
|
+
|
97
|
+
def undef_singleton_method(method_name)
|
98
|
+
# Undefine methods that are defined in the eigenclass
|
99
|
+
(class << self; self; end).send(:undef_method, method_name)
|
100
|
+
end
|
101
|
+
|
83
102
|
def configure(&blk)
|
84
|
-
config_blk
|
85
|
-
|
103
|
+
[self.class.config_blk, blk].each do |callback|
|
104
|
+
instance_eval(&callback) unless callback.nil?
|
105
|
+
end
|
106
|
+
on_configure
|
86
107
|
end
|
87
108
|
|
109
|
+
def on_configure; end
|
110
|
+
def on_alter; end
|
111
|
+
|
88
112
|
def setup_default_action
|
89
|
-
|
90
|
-
|
91
|
-
|
113
|
+
if has_commands?
|
114
|
+
action_noops
|
115
|
+
else
|
116
|
+
action_abort
|
92
117
|
end
|
93
118
|
end
|
94
119
|
|
95
|
-
def
|
120
|
+
def action_noops
|
121
|
+
action { } # NOOPS
|
122
|
+
end
|
123
|
+
|
124
|
+
def action_abort
|
125
|
+
name = @action_name
|
126
|
+
action do
|
127
|
+
abort "Aborted! Action is NOT defined for #{name} in #{self.class.name}."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def dispatch_command(cmd:, argv:)
|
96
132
|
validate_command(cmd)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
133
|
+
send(commands[cmd].action_method, argv)
|
134
|
+
end
|
135
|
+
|
136
|
+
def invoke_action(action_method, **opts)
|
137
|
+
handler = find_action_handler(action_method)
|
138
|
+
if handler.nil?
|
139
|
+
raise RuntimeError, "Action handler #{action_method} is MISSING."
|
140
|
+
else
|
141
|
+
handler.send(action_method, **opts)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def find_action_handler(action_method)
|
146
|
+
if respond_to?(action_method, true)
|
147
|
+
self
|
148
|
+
elsif @parent != self and @parent.respond_to?(:find_action_handler, true)
|
149
|
+
@parent.send(:find_action_handler, action_method)
|
150
|
+
else
|
151
|
+
nil
|
102
152
|
end
|
103
153
|
end
|
104
154
|
|
105
155
|
def validate_command(cmd)
|
106
156
|
if cmd.nil?
|
107
|
-
raise MissingCommand, "
|
157
|
+
raise MissingCommand, "Please specify a command to execute."
|
108
158
|
end
|
109
159
|
unless has_command?(cmd)
|
110
|
-
raise InvalidCommand, "Invalid command
|
160
|
+
raise InvalidCommand, "Invalid command: #{cmd}"
|
111
161
|
end
|
112
162
|
end
|
113
163
|
|
data/lib/luban/cli/base/dsl.rb
CHANGED
@@ -1,28 +1,18 @@
|
|
1
1
|
module Luban
|
2
2
|
module CLI
|
3
3
|
class Base
|
4
|
-
include Commands
|
5
|
-
|
6
4
|
class << self
|
5
|
+
def inherited(subclass)
|
6
|
+
super
|
7
|
+
# Ensure configuration block from base class
|
8
|
+
# got inherited to its subclasses
|
9
|
+
blk = instance_variable_get('@config_blk')
|
10
|
+
subclass.instance_variable_set('@config_blk', blk.clone) unless blk.nil?
|
11
|
+
end
|
12
|
+
|
7
13
|
attr_reader :config_blk
|
8
|
-
|
9
|
-
def configure(&blk); @config_blk = blk; end
|
10
14
|
|
11
|
-
def
|
12
|
-
if block_given?
|
13
|
-
command(**opts, &blk)
|
14
|
-
else
|
15
|
-
validator = method(:has_command?)
|
16
|
-
command(:help, **opts) do
|
17
|
-
desc "List all commands or help for one command"
|
18
|
-
argument :command, "Command to help for",
|
19
|
-
type: :symbol, required: false,
|
20
|
-
assure: validator
|
21
|
-
action :show_help_for_command
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
alias_method :auto_help_command, :help_command
|
15
|
+
def configure(&blk); @config_blk = blk; end
|
26
16
|
end
|
27
17
|
|
28
18
|
def program(name)
|
@@ -72,6 +62,22 @@ module Luban
|
|
72
62
|
end
|
73
63
|
end
|
74
64
|
|
65
|
+
def help_command(**opts, &blk)
|
66
|
+
if block_given?
|
67
|
+
command(**opts, &blk)
|
68
|
+
else
|
69
|
+
validator = method(:has_command?)
|
70
|
+
command(:help, **opts) do
|
71
|
+
desc "List all commands or help for one command"
|
72
|
+
argument :command, "Command to help for",
|
73
|
+
type: :symbol, required: false,
|
74
|
+
assure: validator
|
75
|
+
action :show_help_for_command
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
alias_method :auto_help_command, :help_command
|
80
|
+
|
75
81
|
def version(ver = nil, short: :v, desc: "Show #{program_name} version.", &blk)
|
76
82
|
if ver.nil?
|
77
83
|
@version
|
@@ -95,7 +101,7 @@ module Luban
|
|
95
101
|
|
96
102
|
def create_action(method_name = nil, preserve_argv: true, &blk)
|
97
103
|
handler = if method_name
|
98
|
-
lambda { |**opts|
|
104
|
+
lambda { |**opts| invoke_action(method_name, **opts) }
|
99
105
|
elsif block_given?
|
100
106
|
blk
|
101
107
|
end
|
@@ -105,7 +111,7 @@ module Luban
|
|
105
111
|
_base = self
|
106
112
|
parse_method = preserve_argv ? :parse : :parse!
|
107
113
|
define_action_method do |argv = _base.default_argv|
|
108
|
-
_base.send(:process,
|
114
|
+
_base.send(:process, parse_method, argv) do |params|
|
109
115
|
instance_exec(**params, &handler)
|
110
116
|
end
|
111
117
|
end
|
@@ -113,26 +119,21 @@ module Luban
|
|
113
119
|
end
|
114
120
|
|
115
121
|
def define_action_method(&action_blk)
|
116
|
-
@
|
117
|
-
end
|
118
|
-
|
119
|
-
def method_creator
|
120
|
-
@method_creator ||= @app.is_a?(Class) ? :define_method : :define_singleton_method
|
122
|
+
@parent.send(:define_singleton_method, action_method, &action_blk)
|
121
123
|
end
|
122
124
|
|
123
|
-
def process(
|
125
|
+
def process(parse_method, argv)
|
124
126
|
send(parse_method, argv)
|
125
127
|
if result[:opts][:help]
|
126
128
|
show_help
|
127
|
-
elsif result[:opts][:version]
|
129
|
+
elsif !@version.empty? and result[:opts][:version]
|
128
130
|
show_version
|
129
131
|
else
|
132
|
+
validate_required_options
|
133
|
+
validate_required_arguments
|
134
|
+
yield args: result[:args], opts: result[:opts]
|
130
135
|
if has_commands?
|
131
|
-
dispatch_command(
|
132
|
-
else
|
133
|
-
validate_required_options
|
134
|
-
validate_required_arguments
|
135
|
-
yield args: result[:args], opts: result[:opts]
|
136
|
+
dispatch_command(cmd: result[:cmd], argv: result[:argv])
|
136
137
|
end
|
137
138
|
end
|
138
139
|
rescue OptionParser::ParseError, Error => e
|
data/lib/luban/cli/base.rb
CHANGED
data/lib/luban/cli/command.rb
CHANGED
@@ -4,10 +4,10 @@ module Luban
|
|
4
4
|
attr_reader :name
|
5
5
|
attr_reader :command_chain
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(parent, name, command_chain: [name], **opts, &config_blk)
|
8
8
|
@name = name
|
9
9
|
@command_chain = command_chain
|
10
|
-
super(
|
10
|
+
super(parent, name, **opts, &config_blk)
|
11
11
|
end
|
12
12
|
|
13
13
|
def default_prefix; '__command_'; end
|
@@ -16,6 +16,11 @@ module Luban
|
|
16
16
|
@action_method ||= "#{@prefix}#{command_chain.map(&:to_s).join('_').gsub(':', '_')}"
|
17
17
|
end
|
18
18
|
|
19
|
+
def command(cmd, **opts, &blk)
|
20
|
+
opts[:command_chain] = self.command_chain.clone.push(cmd)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
19
24
|
protected
|
20
25
|
|
21
26
|
def compose_banner
|
data/lib/luban/cli/version.rb
CHANGED
data/lib/luban/cli.rb
CHANGED
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.
|
4
|
+
version: 0.4.0
|
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-
|
11
|
+
date: 2015-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,13 +56,13 @@ files:
|
|
56
56
|
- lib/luban/cli/application.rb
|
57
57
|
- lib/luban/cli/base.rb
|
58
58
|
- lib/luban/cli/base/argument.rb
|
59
|
+
- lib/luban/cli/base/commands.rb
|
59
60
|
- lib/luban/cli/base/core.rb
|
60
61
|
- lib/luban/cli/base/dsl.rb
|
61
62
|
- lib/luban/cli/base/option.rb
|
62
63
|
- lib/luban/cli/base/parse.rb
|
63
64
|
- lib/luban/cli/base/switch.rb
|
64
65
|
- lib/luban/cli/command.rb
|
65
|
-
- lib/luban/cli/commands.rb
|
66
66
|
- lib/luban/cli/error.rb
|
67
67
|
- lib/luban/cli/version.rb
|
68
68
|
- luban-cli.gemspec
|
data/lib/luban/cli/commands.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
module Luban
|
2
|
-
module CLI
|
3
|
-
module Commands
|
4
|
-
def self.included(base)
|
5
|
-
base.extend(ClassMethods)
|
6
|
-
base.send(:include, InstanceMethods)
|
7
|
-
end
|
8
|
-
|
9
|
-
module CommonMethods
|
10
|
-
def list_commands
|
11
|
-
commands.keys
|
12
|
-
end
|
13
|
-
|
14
|
-
def has_command?(cmd)
|
15
|
-
commands.has_key?(cmd)
|
16
|
-
end
|
17
|
-
|
18
|
-
def has_commands?
|
19
|
-
!commands.empty?
|
20
|
-
end
|
21
|
-
|
22
|
-
def use_commands(mod_name)
|
23
|
-
mod_class = Kernel.const_get(camelcase(mod_name.to_s))
|
24
|
-
mod_class.constants.map { |c| mod_class.const_get(c) }.each do |c|
|
25
|
-
command(snakecase(c.name.sub(/Command$/, ''))) if c < Luban::CLI::Command
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
protected
|
30
|
-
|
31
|
-
def camelcase(str)
|
32
|
-
str = str.to_s.dup
|
33
|
-
str.gsub!(/\:(.?)/){ "::#{$1.upcase}" }
|
34
|
-
str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
|
35
|
-
str.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase }
|
36
|
-
str
|
37
|
-
end
|
38
|
-
|
39
|
-
def snakecase(str)
|
40
|
-
str.gsub(/::/, ':').
|
41
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
42
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
43
|
-
tr("-", "_").
|
44
|
-
downcase
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
module ClassMethods
|
49
|
-
include CommonMethods
|
50
|
-
|
51
|
-
def commands
|
52
|
-
@commands ||= {}
|
53
|
-
end
|
54
|
-
|
55
|
-
def command_class(cmd)
|
56
|
-
"#{camelcase(cmd)}Command"
|
57
|
-
end
|
58
|
-
|
59
|
-
def command(app = self, cmd, **opts, &blk)
|
60
|
-
cmd = cmd.to_sym
|
61
|
-
cmd_class = command_class(cmd)
|
62
|
-
klass = if self.const_defined?(command_class(cmd))
|
63
|
-
self.const_get(command_class(cmd))
|
64
|
-
else
|
65
|
-
self.const_set(command_class(cmd), Class.new(Command))
|
66
|
-
end
|
67
|
-
commands[cmd] = klass.new(app, cmd, **opts, &blk)
|
68
|
-
end
|
69
|
-
|
70
|
-
def undef_command(cmd)
|
71
|
-
undef_method(commands.delete(cmd).action_method)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
module InstanceMethods
|
76
|
-
include CommonMethods
|
77
|
-
|
78
|
-
def commands
|
79
|
-
self.class.commands
|
80
|
-
end
|
81
|
-
|
82
|
-
def command(cmd, **opts, &blk)
|
83
|
-
cmd = cmd.to_sym
|
84
|
-
if self.is_a?(Command)
|
85
|
-
opts[:command_chain] = self.command_chain.clone.push(cmd)
|
86
|
-
self.class.command(self.app, cmd, **opts, &blk)
|
87
|
-
else
|
88
|
-
self.class.command(cmd, **opts, &blk)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def undef_command(cmd)
|
93
|
-
self.class.undef_command(cmd)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|