luban-cli 0.3.1 → 0.3.2

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: 2cef4bc2d5883cf60fc715d577ce581b4b99a37d
4
- data.tar.gz: f9621b3ad7ef039ff84595cc6dc5950059519019
3
+ metadata.gz: 5cefeb5c04a0122c4ddbcfe60ec89f10fa448986
4
+ data.tar.gz: 63308ba45f262576aa6208d7017accbbefcc71fd
5
5
  SHA512:
6
- metadata.gz: 1e7ccbaef417438934e5d2a49d587c138d7c230122d0b96241935e3cc211b38e3a755f9c7302268229ccc110ae1d9712b5d08ec7b4e271eb616947020488808a
7
- data.tar.gz: cebee1212f04f353fefb7457e0fcd65abd89cf8d4c008139684e497862ba26309fdfbea473c060773ce1865451d6767b33f71c09cf083565efbe4ce0409b70d5
6
+ metadata.gz: c9783ade21c7ca9ae041262c692730fd3db7fe6cd8eaa880ff910568d57729a88688c5583edbb297b6996797d4d04deb88e7231551e9686e189d2339a0904539
7
+ data.tar.gz: a3724c927f17da7c1933b69ccd82e06371c982040bf8e7840f6cf569049974373ec4961551afe7d49d0f405715da63748fa5ca206242bf989914117b6067500f
data/CHANGELOG.md CHANGED
@@ -1,26 +1,24 @@
1
1
  # Change log
2
2
 
3
- ## Version 0.1.0 (Mar 31, 2015)
3
+ ## Version 0.3.2 (Apr 28, 2015)
4
4
 
5
- Bootstrapped Luban::CLI
5
+ Minor enhancements:
6
+ * Add use_commands DSL to inject command definitions to a given Luban app or command
7
+ * Add an example to demonstrate command injection
6
8
 
7
- New features:
8
- * Support general command-line parsing
9
- * Support options
10
- * Support switches (boolean options)
11
- * Support arguments
12
- * Support subcommand
13
- * Provide base class (Luban::CLI::Base) for command-line application
9
+ Bug fixes:
10
+ * Add validation to help command
11
+ * Command dispatching within the correct context
14
12
 
15
- ## Version 0.2.0 (Apr 02, 2015)
13
+ ## Version 0.3.1 (Apr 15, 2015)
16
14
 
17
15
  Minor enhancements:
18
- * Refactor error class
19
- * Refactor argument validation
20
- * Validate required options/arguments in Luban::CLI::Base
21
- * Create singleton action handler method on application instance
22
- * Move parse error handling to action handler
23
- * Exclude examples and spec from the gem itself
16
+ * Simplify keyword arguments for dispatch_command and action handler
17
+ * Include command chain as part of action method name for commands
18
+ * Enrich README with more documentation
19
+
20
+ Bug fixes:
21
+ * Handle validation for multiple values correctly
24
22
 
25
23
  ## Version 0.3.0 (Apr 11, 2015)
26
24
 
@@ -40,12 +38,24 @@ Bug fixes:
40
38
  * Dispatch command under the right class context
41
39
  * Show correct command chain between program name and synopsis when composing parser banner
42
40
 
43
- ## Version 0.3.1 (Apr 15, 2015)
41
+ ## Version 0.2.0 (Apr 02, 2015)
44
42
 
45
43
  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
44
+ * Refactor error class
45
+ * Refactor argument validation
46
+ * Validate required options/arguments in Luban::CLI::Base
47
+ * Create singleton action handler method on application instance
48
+ * Move parse error handling to action handler
49
+ * Exclude examples and spec from the gem itself
49
50
 
50
- Bug fixes:
51
- * Handle validation for multiple values correctly
51
+ ## Version 0.1.0 (Mar 31, 2015)
52
+
53
+ Bootstrapped Luban::CLI
54
+
55
+ New features:
56
+ * Support general command-line parsing
57
+ * Support options
58
+ * Support switches (boolean options)
59
+ * Support arguments
60
+ * Support subcommand
61
+ * Provide base class (Luban::CLI::Base) for command-line application
data/README.md CHANGED
@@ -180,13 +180,13 @@ Invalid value of argument AGE: 90 (Luban::CLI::Argument::InvalidArgumentValue)
180
180
  ... ...
181
181
 
182
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}}
183
+ {:args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>nil}, :opts=>{:help=>false}}
184
184
 
185
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}}
186
+ {:args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>["john@company.com"]}, :opts=>{:help=>false}}
187
187
 
188
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}}
189
+ {:args=>{:name=>"john", :gender=>:male, :age=>30, :level=>2, :email=>["john@company.com", "john@personal.com"]}, :opts=>{:help=>false}}
190
190
  ```
191
191
 
192
192
  ### option
@@ -222,10 +222,10 @@ MyApp.new.run
222
222
 
223
223
  ```
224
224
  $ ruby my_app.rb --require bundler
225
- {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:libraries=>["bundler"], :help=>false}}
225
+ {:args=>{}, :opts=>{:libraries=>["bundler"], :help=>false}}
226
226
 
227
227
  $ ruby my_app.rb -r bundler,rails
228
- {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:libraries=>["bundler", "rails"], :help=>false}}
228
+ {:args=>{}, :opts=>{:libraries=>["bundler", "rails"], :help=>false}}
229
229
  ```
230
230
 
231
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.
@@ -248,13 +248,13 @@ MyApp.new.run
248
248
 
249
249
  ```
250
250
  $ ruby my_app.rb
251
- {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>nil, :help=>false}}
251
+ {:args=>{}, :opts=>{:inplace=>nil, :help=>false}}
252
252
 
253
253
  $ ruby my_app.rb --inplace
254
- {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>true, :help=>false}}
254
+ {:args=>{}, :opts=>{:inplace=>true, :help=>false}}
255
255
 
256
256
  $ ruby my_app.rb --inplace .bak
257
- {:cmd=>nil, :argv=>[], :args=>{}, :opts=>{:inplace=>".bak", :help=>false}}
257
+ {:args=>{}, :opts=>{:inplace=>".bak", :help=>false}}
258
258
  ```
259
259
 
260
260
  ### switch
@@ -449,6 +449,62 @@ end
449
449
 
450
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
451
 
452
+ ### Command injection
453
+
454
+ Commands can be defined directly within the Luban app class like examples shown in the previous sections. In addition, commands can be defined separately and injected into a given Luban app later. Command definition can be also namespaced by using module. With this feature, commands can be designed in a more re-usable and scalable way. This feature also implies that Luban CLI application supports namespaced commands.
455
+
456
+ Below is an example demonstrating how command injection is supposed to work.
457
+
458
+ ```ruby
459
+ module App
460
+ module ControlTasks
461
+ class Task1Command < Luban::CLI::Command
462
+ configure do
463
+ ... ...
464
+ end
465
+ end
466
+
467
+ class Task2Command < Luban::CLI::Command
468
+ configure do
469
+ ... ...
470
+ end
471
+ end
472
+ end
473
+ end
474
+
475
+ class MyApp < Luban::CLI::Application
476
+ configure do
477
+ ... ...
478
+ end
479
+
480
+ # Inject Luban commands directly defined under the given module
481
+ use_commands 'app:control_tasks'
482
+
483
+ # Alternatively, commands can be injected individually
484
+ # The following has the same effect
485
+ # command 'app:control_tasks:task1'
486
+ # command 'app:control_tasks:task2'
487
+ end
488
+ ```
489
+
490
+ ```
491
+ $ ruby my_app.rb app:control_tasks:task1
492
+
493
+ $ ruby my_app.rb app:control_tasks:task2
494
+ ```
495
+
496
+ As shown above, there are a few naming conventions about the command class naming and injection:
497
+
498
+ * Command class should has suffix "Command"
499
+ * For instances, Task1Command, Task2Command
500
+ * Module name to be injected should be fully qualified
501
+ * namespaces/modules should be in snake case and
502
+ * Separated by colon as the delimiter
503
+ * For instances, 'app:control_tasks'
504
+ * Alternatively, commands can be injected individually
505
+ * Command name should be fully qualified
506
+ * Suffix "Command" is no more needed
507
+
452
508
  ## Applications
453
509
 
454
510
  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.
@@ -61,7 +61,7 @@ module Luban
61
61
  def default_prefix; ''; end
62
62
 
63
63
  def action_method
64
- @action_method ||= "#{@prefix}#{@action_name}"
64
+ @action_method ||= "#{@prefix}#{@action_name.to_s.gsub(':', '_')}"
65
65
  end
66
66
 
67
67
  def parser
@@ -94,7 +94,12 @@ module Luban
94
94
 
95
95
  def dispatch_command(context, cmd:, argv:)
96
96
  validate_command(cmd)
97
- context.send(commands[cmd].action_method, argv)
97
+ cmd_method = commands[cmd].action_method
98
+ if respond_to?(cmd_method)
99
+ send(cmd_method, argv)
100
+ else
101
+ context.send(cmd_method, argv)
102
+ end
98
103
  end
99
104
 
100
105
  def validate_command(cmd)
@@ -12,10 +12,12 @@ module Luban
12
12
  if block_given?
13
13
  command(**opts, &blk)
14
14
  else
15
+ validator = method(:has_command?)
15
16
  command(:help, **opts) do
16
17
  desc "List all commands or help for one command"
17
18
  argument :command, "Command to help for",
18
- type: :symbol, required: false
19
+ type: :symbol, required: false,
20
+ assure: validator
19
21
  action :show_help_for_command
20
22
  end
21
23
  end
@@ -13,7 +13,7 @@ module Luban
13
13
  def default_prefix; '__command_'; end
14
14
 
15
15
  def action_method
16
- @action_method ||= "#{@prefix}#{command_chain.map(&:to_s).join('_')}"
16
+ @action_method ||= "#{@prefix}#{command_chain.map(&:to_s).join('_').gsub(':', '_')}"
17
17
  end
18
18
 
19
19
  protected
@@ -18,6 +18,31 @@ module Luban
18
18
  def has_commands?
19
19
  !commands.empty?
20
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
21
46
  end
22
47
 
23
48
  module ClassMethods
@@ -28,10 +53,11 @@ module Luban
28
53
  end
29
54
 
30
55
  def command_class(cmd)
31
- "#{classify(cmd)}Command"
56
+ "#{camelcase(cmd)}Command"
32
57
  end
33
58
 
34
59
  def command(app = self, cmd, **opts, &blk)
60
+ cmd = cmd.to_sym
35
61
  cmd_class = command_class(cmd)
36
62
  klass = if self.const_defined?(command_class(cmd))
37
63
  self.const_get(command_class(cmd))
@@ -44,16 +70,6 @@ module Luban
44
70
  def undef_command(cmd)
45
71
  undef_method(commands.delete(cmd).action_method)
46
72
  end
47
-
48
- protected
49
-
50
- def classify(cmd)
51
- cmd = cmd.to_s.dup
52
- cmd.gsub!(/\/(.?)/){ "::#{$1.upcase}" }
53
- cmd.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
54
- cmd.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase }
55
- cmd
56
- end
57
73
  end
58
74
 
59
75
  module InstanceMethods
@@ -64,6 +80,7 @@ module Luban
64
80
  end
65
81
 
66
82
  def command(cmd, **opts, &blk)
83
+ cmd = cmd.to_sym
67
84
  if self.is_a?(Command)
68
85
  opts[:command_chain] = self.command_chain.clone.push(cmd)
69
86
  self.class.command(self.app, cmd, **opts, &blk)
@@ -1,5 +1,5 @@
1
1
  module Luban
2
2
  module CLI
3
- VERSION = "0.3.1"
3
+ VERSION = "0.3.2"
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.1
4
+ version: 0.3.2
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-17 00:00:00.000000000 Z
11
+ date: 2015-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler