cliqr 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/README.md +97 -71
  4. data/examples/README.md +12 -0
  5. data/examples/hbase +58 -0
  6. data/examples/my-command +63 -0
  7. data/examples/numbers +55 -0
  8. data/examples/vagrant +90 -0
  9. data/lib/cliqr.rb +17 -2
  10. data/lib/cliqr/argument_validation/argument_type_validator.rb +2 -2
  11. data/lib/cliqr/argument_validation/validator.rb +1 -1
  12. data/lib/cliqr/cli/argument_operator.rb +44 -0
  13. data/lib/cliqr/cli/argument_operator_context.rb +20 -0
  14. data/lib/cliqr/cli/command.rb +1 -1
  15. data/lib/cliqr/cli/command_context.rb +93 -12
  16. data/lib/cliqr/cli/command_runner_factory.rb +2 -2
  17. data/lib/cliqr/cli/config.rb +301 -33
  18. data/lib/cliqr/cli/executor.rb +14 -9
  19. data/lib/cliqr/cli/interface.rb +22 -7
  20. data/lib/cliqr/cli/router.rb +6 -2
  21. data/lib/cliqr/cli/shell_command.rb +69 -0
  22. data/lib/cliqr/cli/usage_builder.rb +185 -0
  23. data/lib/cliqr/config_validation/validator_factory.rb +59 -5
  24. data/lib/cliqr/error.rb +10 -4
  25. data/lib/cliqr/parser/action_token.rb +23 -0
  26. data/lib/cliqr/parser/argument_parser.rb +1 -1
  27. data/lib/cliqr/parser/argument_token.rb +1 -4
  28. data/lib/cliqr/parser/argument_tree_walker.rb +40 -8
  29. data/lib/cliqr/parser/option_token.rb +2 -1
  30. data/lib/cliqr/parser/parsed_input.rb +21 -2
  31. data/lib/cliqr/parser/parsed_input_builder.rb +11 -7
  32. data/lib/cliqr/parser/token.rb +3 -9
  33. data/lib/cliqr/parser/token_factory.rb +1 -1
  34. data/lib/cliqr/util.rb +135 -0
  35. data/lib/cliqr/version.rb +1 -1
  36. data/spec/argument_parser_spec_helper.rb +15 -0
  37. data/spec/config/action_config_validator_spec.rb +146 -0
  38. data/spec/config/config_finalize_spec.rb +1 -1
  39. data/spec/config/config_validator_spec.rb +29 -19
  40. data/spec/config/option_config_validator_spec.rb +13 -13
  41. data/spec/dsl/interface_spec.rb +1 -168
  42. data/spec/dsl/usage_spec.rb +705 -0
  43. data/spec/executor/action_executor_spec.rb +205 -0
  44. data/spec/executor/executor_spec.rb +405 -17
  45. data/spec/executor/help_executor_spec.rb +424 -0
  46. data/spec/executor/shell_executor_spec.rb +233 -0
  47. data/spec/fixtures/action_reader_command.rb +12 -0
  48. data/spec/fixtures/csv_argument_operator.rb +8 -0
  49. data/spec/fixtures/test_option_type_checker_command.rb +8 -0
  50. data/spec/parser/action_argument_parser_spec.rb +113 -0
  51. data/spec/parser/argument_parser_spec.rb +37 -44
  52. data/spec/spec_helper.rb +1 -0
  53. data/spec/validation/action_argument_validator_spec.rb +50 -0
  54. data/spec/validation/{argument_validation_spec.rb → command_argument_validation_spec.rb} +36 -18
  55. data/spec/validation/error_spec.rb +1 -1
  56. data/tasks/rdoc.rake +16 -0
  57. data/tasks/rubucop.rake +14 -0
  58. data/tasks/yard.rake +21 -0
  59. data/templates/usage.erb +39 -0
  60. metadata +48 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 779fd42a32e0ec2a3d11d5db83bc65c66926d56b
4
- data.tar.gz: a2c085fa4815bdcd88d04d1c52e6d7e1e2e4e4d2
3
+ metadata.gz: 9250bde8a2cd431432f1d68618d748b5e6ea378f
4
+ data.tar.gz: 1d2f297559d079b1b1592c5863f3b2ccb4782037
5
5
  SHA512:
6
- metadata.gz: f91257c83d83705c218151e96b5c0e68f183ab714df0b661d65287d819ef83b0099c152780e46a25c1aab151a4cb21439252c787f1a6ae99bab3d7f6b4fb68ac
7
- data.tar.gz: 609e715827bfe3cb93fb6dd9271856c0a9c7c256aa7ba89d070aa0906181452b5a3b34020dd3d4850b26d75483721931f1339fedb9f230f16dd60e841bf3394c
6
+ metadata.gz: aff0bc4667372517b12b61a84c4c4236179ec1aa37157be2676a71861daf6c639182cb11f06a698eb6b76cb1796f0c1b5c8142a4fde63f50b3d2f39ce9cec180
7
+ data.tar.gz: 5eee04fc1abd977ae5ff3156b02c4d694b33b30b9ec793dff92edd2d2c5f70605e15316aa67c18e8b1bd3cf04f9d88b304f7e4c8be9ab35bce1564288da8fb12
@@ -5,6 +5,30 @@ item in this nested table for further details.
5
5
  <!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc/generate-toc again -->
6
6
  **Table of Contents**
7
7
 
8
+ - [1.2.0 / 2015-06-18](#120--2015-06-18)
9
+ - [Features](#features)
10
+ - [Nested actions](#nested-actions)
11
+ - [Ability to operate on arguments](#ability-to-operate-on-arguments)
12
+ - [Anonymous Proc for command handling and argument operation](#anonymous-proc-for-command-handling-and-argument-operation)
13
+ - [Default value for options](#default-value-for-options)
14
+ - [Default command actions](#default-command-actions)
15
+ - [Help](#help)
16
+ - [Version](#version)
17
+ - [Shell](#shell)
18
+ - [Forward command from one handler to another](#forward-command-from-one-handler-to-another)
19
+ - [Improvements](#improvements)
20
+ - [Use name instead of basename](#use-name-instead-of-basename)
21
+ - [Reorganize tests](#reorganize-tests)
22
+ - [Can get value in operator by calling `value`](#can-get-value-in-operator-by-calling-value)
23
+ - [Run anonymous command handler in the the context of `CommandContext`](#run-anonymous-command-handler-in-the-the-context-of-commandcontext)
24
+ - [Call `help` action by default](#call-help-action-by-default)
25
+ - [Add Examples for using `Cliqr`](#add-examples-for-using-cliqr)
26
+ - [Update `README` to make it more relevant](#update-readme-to-make-it-more-relevant)
27
+ - [Bug-fixes](#bug-fixes)
28
+ - [Fix option parsing for option with symbolic name](#fix-option-parsing-for-option-with-symbolic-name)
29
+ - [Handle errors gracefully](#handle-errors-gracefully)
30
+ - [Include template in gem](#include-template-in-gem)
31
+ - [Fix shell in shell issue](#fix-shell-in-shell-issue)
8
32
  - [1.1.0 / 2015-06-05](#110--2015-06-05)
9
33
  - [Features](#features)
10
34
  - [Support for arbitrary arguments in a command](#support-for-arbitrary-arguments-in-a-command)
@@ -67,6 +91,109 @@ item in this nested table for further details.
67
91
 
68
92
  <!-- markdown-toc end -->
69
93
 
94
+ 1.2.0 / 2015-06-18
95
+ ==================
96
+
97
+ This is a pretty loaded release in terms of features, improvements and
98
+ bug-fixes. Here is a detailed list.
99
+
100
+ ## Features
101
+
102
+ ### Nested actions
103
+
104
+ Every command is a action and in this release, we add support for
105
+ building nested command structure.
106
+
107
+ For example:
108
+
109
+ ``` bash
110
+ $ vagrant up --provision
111
+ ```
112
+
113
+ This command has:
114
+
115
+ - Base-command: vagrant
116
+ - Sub action: up
117
+ - Option: provision
118
+
119
+ ### Ability to operate on arguments
120
+
121
+ You can now specify a operator that can pre-process the option arguments
122
+ and, if needed, do validation as well.
123
+
124
+ ### Anonymous Proc for command handling and argument operation
125
+
126
+ No need to define a separate class to handle a command or operate on
127
+ arguments.
128
+
129
+ ### Default value for options
130
+
131
+ Options can now have a default value. In some cases this is derived from
132
+ option type attribute.
133
+
134
+ ### Default command actions
135
+
136
+ Several default actions were added to a command.
137
+
138
+ #### Help
139
+
140
+ Help is added by default to every action. This includes a help action
141
+ and option.
142
+
143
+ #### Version
144
+
145
+ If specified, a version action and boolean option is added to the action.
146
+
147
+ #### Shell
148
+
149
+ Shell is enabled by default if there are any sub-actions for a command.
150
+
151
+ ### Forward command from one handler to another
152
+
153
+ A new method `forward` can be called with arguments that are parsed and
154
+ executed again.
155
+
156
+ ## Improvements
157
+
158
+ ### Use name instead of basename
159
+
160
+ The only reason for this was to make things simple and consistent.
161
+
162
+ ### Reorganize tests
163
+
164
+ Divide tests by functionality. In some cases group them together.
165
+
166
+ ### Can get value in operator by calling `value`
167
+
168
+ ### Run anonymous command handler in the the context of `CommandContext`
169
+
170
+ ### Call `help` action by default
171
+
172
+ If you call a command without specifying any arguments, its help action
173
+ will be invoked (assuming it is enabled).
174
+
175
+ ### Add Examples for using `Cliqr`
176
+
177
+ Examples for some popular commands like `vagrant` and `hbase` along with
178
+ some custom commands were added.
179
+
180
+ ### Update `README` to make it more relevant
181
+
182
+ ## Bug-fixes
183
+
184
+ ### Fix option parsing for option with symbolic name
185
+
186
+ ### Handle errors gracefully
187
+
188
+ Errors do not kill the running script anymore and they do not show stack
189
+ trace.
190
+
191
+ ### Include template in gem
192
+
193
+ ### Fix shell in shell issue
194
+
195
+ It should be not allowed to run a shell within another shell.
196
+
70
197
  1.1.0 / 2015-06-05
71
198
  ==================
72
199
 
@@ -77,7 +204,7 @@ for arbitrary arguments.
77
204
 
78
205
  ### Support for arbitrary arguments in a command
79
206
 
80
- For the first time you can invoke a command with a non-option arguments.
207
+ For the first time you can invoke a command with non-option arguments.
81
208
 
82
209
  ## Minor Improvements
83
210
 
data/README.md CHANGED
@@ -1,12 +1,10 @@
1
- # cliqr
1
+ # cliqr [![Version](http://img.shields.io/gem/v/cliqr.svg?style=flat-square)](https://rubygems.org/gems/cliqr)
2
2
 
3
3
  [![Build](http://img.shields.io/travis-ci/anshulverma/cliqr.svg?style=flat-square)](https://travis-ci.org/anshulverma/cliqr)
4
4
  [![Coverage](http://img.shields.io/codeclimate/coverage/github/anshulverma/cliqr.svg?style=flat-square)](https://codeclimate.com/github/anshulverma/cliqr)
5
5
  [![Quality](http://img.shields.io/codeclimate/github/anshulverma/cliqr.svg?style=flat-square)](https://codeclimate.com/github/anshulverma/cliqr)
6
6
  [![Dependencies](http://img.shields.io/gemnasium/anshulverma/cliqr.svg?style=flat-square)](https://gemnasium.com/anshulverma/cliqr)
7
- [![Inline docs](http://inch-ci.org/github/anshulverma/cliqr.svg)](http://inch-ci.org/github/anshulverma/cliqr)
8
-
9
- [![Version](http://img.shields.io/gem/v/cliqr.svg?style=flat-square)](https://rubygems.org/gems/cliqr)
7
+ [![Inline docs](http://inch-ci.org/github/anshulverma/cliqr.svg?style=flat-square)](http://inch-ci.org/github/anshulverma/cliqr)
10
8
  [![Downloads](http://img.shields.io/gem/dt/cliqr.svg?style=flat-square)](https://rubygems.org/gems/cliqr)
11
9
 
12
10
  <!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc/generate-toc again -->
@@ -15,7 +13,7 @@
15
13
  - [cliqr](#cliqr)
16
14
  - [Summary](#summary)
17
15
  - [Examples](#examples)
18
- - [Simple CLI app example](#simple-cli-app-example)
16
+ - [Quickstart](#quickstart)
19
17
  - [Installation](#installation)
20
18
  - [Building](#building)
21
19
  - [Contributing](#contributing)
@@ -25,91 +23,119 @@
25
23
 
26
24
  ## Summary
27
25
 
28
- `cliqr` is a lightweight framework and DSL to easily build a command
29
- line application. Features include:
26
+ `cliqr` is a lightweight yet feature rich framework which can be used to
27
+ build a powerful command line application. It provides a easy to use DSL
28
+ to define the interface of a application. Some of the features included:
30
29
 
31
- - Command Routing
32
- - DSL for simple interface definition
33
- - Usage info generation
34
- - Error handling
30
+ - Quick and easy method for defining CLI interface
31
+ - Command usage generation based on interface definition
32
+ - Argument parsing
33
+ - Argument validation
34
+ - Nested command actions
35
+ - Multiple command handler based on arguments
36
+ - Command routing to appropriate handler
37
+ - Inbuilt shell extension for your command
35
38
 
36
39
  ## Examples
37
40
 
38
41
  The DSL provides several helper methods to build interfaces of different
39
- styles. Here are some examples.
42
+ styles. Please refer to the examples folder to find some useful tips on
43
+ how to use `cliqr`.
40
44
 
41
- ### Simple CLI app example
45
+ ## Quickstart
42
46
 
43
- Here is a simple hello-world example for using Cliqr.
47
+ To get things started quickly here is an example of a basic `cliqr`
48
+ based CLI application (lets call this script `numbers`):
44
49
 
45
50
  ``` ruby
46
- require 'cliqr'
51
+ #!/usr/bin/env ruby
47
52
 
48
- # a custom command handler
49
- class MyCommandHandler < Cliqr.command
50
- def execute(context)
51
- puts 'executing my awesome command'
52
- puts "value for option 'an-option' is '#{context.option('an-option').value}'"
53
- puts "value for option 'count' is '#{context.option('count').value}'"
54
- puts "value for option 'single' is '#{context.option('single').value}'"
55
- puts "value for option 'test-1' is '#{context.option('test-1').value}'"
56
- puts "has 'count' argument" if context.option?('count')
57
- puts "does not have 'test-2' argument" unless context.option?('test-2')
58
- end
59
- end
53
+ require 'cliqr'
60
54
 
61
55
  cli = Cliqr.interface do
62
- basename 'my-command'
63
- description 'this is an awesome command...try it out'
64
- handler MyCommandHandler
65
-
66
- option 'an-option' do
67
- short 'a'
68
- description 'this is a option'
56
+ name 'numbers'
57
+ description 'A simplistic example for quickly getting started with cliqr.'
58
+ version '0.0.1' # optional; adds a version action to our simple command
59
+
60
+ # main command handler
61
+ handler do
62
+ puts "Hi #{name}" if name?
63
+ puts 'Nothing to do here. Please try the sort action.'
69
64
  end
70
65
 
71
- option 'count' do
72
- short 'c'
73
- description 'count of something'
74
- type :numeric
66
+ option :name do
67
+ description 'Your name.'
68
+ operator do
69
+ value.split(' ').first # only get the first name
70
+ end
75
71
  end
76
72
 
77
- option 'single' do
78
- short 's'
79
- description 'a boolean option'
80
- type :boolean
73
+ action :sort do
74
+ description 'Sort a set of random numbers'
75
+ shell :disable
76
+
77
+ handler do
78
+ fail StandardError, 'count should be a non-zero positive number' unless count > 0
79
+ result = [].tap { |numbers| count.times { numbers << rand(9999) } }.sort
80
+ result = result.reverse if order? && order == :descending
81
+ puts result
82
+ end
83
+
84
+ option :count do
85
+ short 'c' # optional, but usually a good idea to have it
86
+ description 'Count of something.'
87
+ type :numeric # restricts values for this option to numbers
88
+ end
89
+
90
+ option :order do
91
+ short 'o'
92
+ description 'Order of sort.'
93
+
94
+ # This is how you can make sure that the input is valid.
95
+ operator do
96
+ fail StandardError, "Unknown order #{value}" unless [:ascending, :descending].include?(value.to_sym)
97
+ value.to_sym
98
+ end
99
+ end
81
100
  end
82
-
83
- option 'test-1'
84
- option 'test-2'
85
101
  end
86
102
 
87
- puts cli.usage
88
- # my-command -- this is an awesome command...try it out
89
- #
90
- # USAGE:
91
- # my-command [options] [arguments]
92
- #
93
- # Available options:
94
- #
95
- # --an-option, -a : this is a option
96
- #
97
- # --count, -c : <numeric> count of something
98
- #
99
- # --[no-]single, -s : <boolean> a boolean option
100
- #
101
- # --test-1
102
- #
103
- # --test-2
104
-
105
- cli.execute %w(--an-option qwerty -c 86 --no-single --test-1 some-value)
106
- # executing my awesome command
107
- # value for option 'an-option' is 'qwerty'
108
- # value for option 'count' is '86'
109
- # value for option 'single' is 'false'
110
- # value for option 'test-1' is 'some-value'
111
- # has 'count' argument
112
- # does not have 'test-2' argument
103
+ cli.execute(ARGV)
104
+ ```
105
+
106
+ Now you can execute this script:
107
+
108
+ ``` bash
109
+ $ ./numbers
110
+ Nothing to do here. Please try the sort action.
111
+ $ ./numbers --name "Anshul Verma"
112
+ Hi Anshul
113
+ Nothing to do here. Please try the sort action.
114
+ $ ./numbers sort -c 5
115
+ 4519
116
+ 5612
117
+ 6038
118
+ 6872
119
+ 8259
120
+ $ ./numbers sort -c 5 --order descending
121
+ 8742
122
+ 7995
123
+ 6593
124
+ 2730
125
+ 806
126
+ ```
127
+
128
+ A shell command is auto generated for you by `cliqr`. Here is how it works:
129
+
130
+ ``` bash
131
+ $ ./numbers shell
132
+ Starting shell for command "numbers"
133
+ numbers > sort -c 5
134
+ 1259
135
+ 2031
136
+ 4864
137
+ 8355
138
+ 9824
113
139
  ```
114
140
 
115
141
  ## Installation
@@ -0,0 +1,12 @@
1
+ # examples
2
+
3
+ Here are some usage examples for `cliqr`. More can be added if needed.
4
+
5
+ Please go through the comment at the top of each script. It pretty much
6
+ explains what the script is.
7
+
8
+ To fire off the script and start using it (for `vagrant` example):
9
+
10
+ ``` bash
11
+ $ ./vagrant help
12
+ ```
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A fake hbase shell example
4
+ #
5
+ # This script mainly features how to build a custom shell
6
+
7
+ require 'cliqr'
8
+
9
+ cli = Cliqr.interface do
10
+ name 'hbase'
11
+ version '0.98.13RC2'
12
+ arguments :disable
13
+
14
+ # from hbase git repository https://github.com/apache/hbase
15
+ description <<-EOS
16
+ Apache HBase is an open-source, distributed, versioned, column-oriented
17
+ store modeled after Google' Bigtable: A Distributed Storage System for
18
+ Structured Data by Chang et al. Just as Bigtable leverages the distributed
19
+ data storage provided by the Google File System, HBase provides Bigtable-like
20
+ capabilities on top of Apache Hadoop.
21
+ EOS
22
+
23
+ option :server do
24
+ description 'to select the "server" VM'
25
+ default 'server'
26
+ end
27
+
28
+ TABLES = %w(people address phone_number)
29
+ action :list do
30
+ description 'List all tables in hbase.'
31
+
32
+ handler do
33
+ puts 'TABLE'
34
+ TABLES.each { |item| puts item }
35
+ puts '3 row(s) in 1.3310 seconds'
36
+ end
37
+ arguments :disable
38
+ end
39
+
40
+ action :scan do
41
+ description 'Scan a table.'
42
+
43
+ handler do
44
+ fail StandardError, 'please specify table name' unless arguments.length > 0
45
+ fail StandardError, 'too many arguments' if arguments.length > 1
46
+ table_name = arguments.first
47
+ fail StandardError, "unknown table \"#{table_name}\"" unless TABLES.include?(table_name)
48
+ puts <<-EOS
49
+ ROW COLUMN+CELL
50
+ my-test:1 column=c1, timestamp=1432748503634, value=0.5
51
+ my-test:2 column=c1, timestamp=1432748503781, value=1.5
52
+ 2 row(s) in 0.4250 seconds
53
+ EOS
54
+ end
55
+ end
56
+ end
57
+
58
+ cli.execute(ARGV)
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Here is an example on how to use a custom command handler
4
+ #
5
+ # This example also shows :boolean and :numeric option types
6
+ # with their default value settings
7
+
8
+ require 'cliqr'
9
+
10
+ # a custom command handler for base command
11
+ class MyCommandHandler < Cliqr.command
12
+ def execute(context)
13
+ puts 'executing my awesome command'
14
+ puts "value for option 'an-option' is '#{context.option('an-option').value}'"
15
+ puts "value for option 'count' is '#{context.option('count').value}'"
16
+ puts "value for option 'single' is '#{context.option('single').value}'"
17
+ puts "value for option 'test-1' is '#{context.option('test-1').value}'"
18
+ puts "has 'count' argument" if context.option?('count')
19
+ puts "does not have 'test-2' argument" unless context.option?('test-2')
20
+ end
21
+ end
22
+
23
+ # another custom command handler for a action
24
+ class MyActionHandler < Cliqr.command
25
+ def execute(context)
26
+ puts "command executed : #{context.command}"
27
+ end
28
+ end
29
+
30
+ cli = Cliqr.interface do
31
+ name 'my-command'
32
+ description 'this is an awesome command...try it out'
33
+ handler MyCommandHandler
34
+
35
+ option 'an-option' do
36
+ short 'a'
37
+ description 'this is a option'
38
+ default :tag => 'qwerty'
39
+ end
40
+
41
+ option 'count' do
42
+ short 'c'
43
+ description 'count of something'
44
+ type :numeric
45
+ default 10
46
+ end
47
+
48
+ option 'single' do
49
+ short 's'
50
+ description 'a boolean option'
51
+ type :boolean
52
+ end
53
+
54
+ option 'test-1'
55
+ option 'test-2'
56
+
57
+ action 'my-action' do
58
+ handler MyActionHandler
59
+ description 'a simple action handler'
60
+ end
61
+ end
62
+
63
+ cli.execute(ARGV)