cliqr 1.1.0 → 1.2.0

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.
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)