luban-cli 0.3.1 → 0.3.2

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