cliqr 0.1.0 → 1.0.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +46 -17
  4. data/lib/cliqr/argument_validation/argument_type_validator.rb +31 -0
  5. data/lib/cliqr/argument_validation/option_validator.rb +27 -0
  6. data/lib/cliqr/argument_validation/validator.rb +67 -0
  7. data/lib/cliqr/cli/command_context.rb +21 -12
  8. data/lib/cliqr/cli/config.rb +57 -9
  9. data/lib/cliqr/cli/executor.rb +17 -11
  10. data/lib/cliqr/cli/interface.rb +7 -3
  11. data/lib/cliqr/error.rb +17 -19
  12. data/lib/cliqr/parser/argument_parser.rb +21 -0
  13. data/lib/cliqr/parser/argument_tree_walker.rb +51 -0
  14. data/lib/cliqr/parser/boolean_option_token.rb +26 -0
  15. data/lib/cliqr/parser/parsed_input.rb +53 -0
  16. data/lib/cliqr/parser/parsed_input_builder.rb +61 -0
  17. data/lib/cliqr/parser/single_valued_option_token.rb +53 -0
  18. data/lib/cliqr/parser/token.rb +45 -0
  19. data/lib/cliqr/parser/token_factory.rb +69 -0
  20. data/lib/cliqr/validation/validation_set.rb +48 -0
  21. data/lib/cliqr/validation/validator_factory.rb +265 -0
  22. data/lib/cliqr/validation/verifiable.rb +89 -0
  23. data/lib/cliqr/validation_errors.rb +61 -0
  24. data/lib/cliqr/version.rb +1 -1
  25. data/spec/config/config_validator_spec.rb +51 -30
  26. data/spec/config/option_config_validator_spec.rb +143 -0
  27. data/spec/dsl/interface_spec.rb +48 -114
  28. data/spec/executor/executor_spec.rb +19 -1
  29. data/spec/fixtures/test_option_checker_command.rb +8 -0
  30. data/spec/parser/argument_parser_spec.rb +33 -39
  31. data/spec/validation/argument_validation_spec.rb +141 -0
  32. data/spec/validation/error_spec.rb +22 -0
  33. data/spec/validation/validation_spec.rb +11 -0
  34. metadata +27 -10
  35. data/lib/cliqr/cli/argument_validator.rb +0 -19
  36. data/lib/cliqr/cli/config_validator.rb +0 -104
  37. data/lib/cliqr/cli/parser/argument_parser.rb +0 -23
  38. data/lib/cliqr/cli/parser/argument_tree_walker.rb +0 -56
  39. data/lib/cliqr/cli/parser/option_token.rb +0 -72
  40. data/lib/cliqr/cli/parser/parsed_argument_builder.rb +0 -66
  41. data/lib/cliqr/cli/parser/token.rb +0 -38
  42. data/lib/cliqr/cli/parser/token_factory.rb +0 -58
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ module Cliqr
6
+ # A wrapper of validation errors which provides helpful methods for accessing it
7
+ #
8
+ # @api private
9
+ class ValidationErrors
10
+ # List of all error messages
11
+ #
12
+ # @return [Array<String]
13
+ attr_accessor :errors
14
+
15
+ # Create a new instance of the validation error wrapper
16
+ def initialize
17
+ @errors = Set.new []
18
+ end
19
+
20
+ # Add a new error message
21
+ #
22
+ # @param [String] error_message
23
+ #
24
+ # @return [Array] A collection of all error messages
25
+ def add(error_message)
26
+ @errors.add(error_message)
27
+ end
28
+
29
+ # Check if there are error or not
30
+ #
31
+ # @return [Boolean] <tt>false</tt> if there are no errors
32
+ def empty?
33
+ @errors.empty?
34
+ end
35
+
36
+ # Convert list of errors to a string representation
37
+ #
38
+ # @return [String] A comma separated list of all errors
39
+ def to_s
40
+ @errors.to_a.join(', ')
41
+ end
42
+
43
+ # Merge the list of errors from another
44
+ #
45
+ # @param [Cliqr::Validation::Errors] other Errors that need to be merged
46
+ #
47
+ # @return [Cliqr::Validation::Errors] Updated errors list
48
+ def merge(other)
49
+ @errors = @errors.union(other.errors)
50
+ end
51
+
52
+ # Iterate over each error message
53
+ #
54
+ # @param [Function] block The iterating function
55
+ #
56
+ # @return [Set<String>] A set of all error messages
57
+ def each(&block)
58
+ @errors.each(&block)
59
+ end
60
+ end
61
+ end
data/lib/cliqr/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Versioned gem
4
4
  module Cliqr
5
- VERSION = '0.1.0'
5
+ VERSION = '1.0.0'
6
6
  end
@@ -2,44 +2,55 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- require 'cliqr/cli/config_validator'
6
-
7
5
  require 'fixtures/test_command'
8
6
 
9
- describe Cliqr::CLI::ConfigValidator do
10
- it 'does not allow empty config' do
11
- expect { Cliqr::CLI::Interface.build(nil) }.to(
12
- raise_error(Cliqr::Error::ConfigNotFound, 'a valid config should be defined')
13
- )
7
+ describe Cliqr::CLI::Config do
8
+ it 'does not allow empty basename' do
9
+ expect do
10
+ Cliqr.interface do
11
+ basename ''
12
+ handler TestCommand
13
+ end
14
+ end.to(raise_error(Cliqr::Error::ValidationError,
15
+ "invalid Cliqr interface configuration - ['basename' cannot be empty]"))
14
16
  end
15
17
 
16
- it 'does not allow empty basename' do
17
- config = Cliqr::CLI::Config.new
18
- config.basename = ''
19
- config.finalize
20
- expect { Cliqr::CLI::Interface.build(config) }.to(
21
- raise_error(Cliqr::Error::BasenameNotDefined, 'basename not defined')
22
- )
18
+ it 'does not allow nil basename' do
19
+ expect do
20
+ Cliqr.interface do
21
+ basename nil
22
+ handler TestCommand
23
+ end
24
+ end.to(raise_error(Cliqr::Error::ValidationError,
25
+ "invalid Cliqr interface configuration - ['basename' cannot be nil]"))
26
+ end
27
+
28
+ it 'does not allow invalid characters in basename' do
29
+ expect do
30
+ Cliqr.interface do
31
+ basename 'invalid-char-!'
32
+ handler TestCommand
33
+ end
34
+ end.to(raise_error(Cliqr::Error::ValidationError,
35
+ "invalid Cliqr interface configuration - [value for 'basename' must match /^[a-zA-Z0-9_\\-]+$/; actual: \"invalid-char-!\"]"))
23
36
  end
24
37
 
25
38
  it 'does not allow command handler to be null' do
26
- config = Cliqr::CLI::Config.new
27
- config.basename = 'my-command'
28
- config.finalize
29
- expect { Cliqr::CLI::Interface.build(config) }.to(
30
- raise_error(Cliqr::Error::HandlerNotDefined, 'handler not defined for command "my-command"')
31
- )
39
+ expect do
40
+ Cliqr.interface do
41
+ basename 'my-command'
42
+ end
43
+ end.to(raise_error(Cliqr::Error::ValidationError, "invalid Cliqr interface configuration - ['handler' cannot be nil]"))
32
44
  end
33
45
 
34
46
  it 'only accepts command handler that extend from Cliqr::CLI::Command' do
35
- config = Cliqr::CLI::Config.new
36
- config.basename = 'my-command'
37
- config.handler = Object
38
- config.finalize
39
- expect { Cliqr::CLI::Interface.build(config) }.to(
40
- raise_error(Cliqr::Error::InvalidCommandHandler,
41
- 'handler for command "my-command" should extend from [Cliqr::CLI::Command]')
42
- )
47
+ expect do
48
+ Cliqr.interface do
49
+ basename 'my-command'
50
+ handler Object
51
+ end
52
+ end.to(raise_error(Cliqr::Error::ValidationError,
53
+ "invalid Cliqr interface configuration - [value 'Object' of type 'Class' for 'handler' does not extend from 'Cliqr::CLI::Command']"))
43
54
  end
44
55
 
45
56
  it 'expects that config options should not be nil' do
@@ -49,7 +60,17 @@ describe Cliqr::CLI::ConfigValidator do
49
60
  config.options = nil
50
61
  config.finalize
51
62
  expect { Cliqr::CLI::Interface.build(config) }.to(
52
- raise_error(Cliqr::Error::OptionsNotDefinedException, 'option array is nil for command "my-command"')
53
- )
63
+ raise_error(Cliqr::Error::ValidationError, "invalid Cliqr interface configuration - ['options' cannot be nil]"))
64
+ end
65
+
66
+ it 'throws multiple errors if more than one issue exists in config' do
67
+ expect do
68
+ Cliqr.interface do
69
+ basename 'invalid-name-!@#'
70
+ handler Object
71
+ end
72
+ end.to(raise_error(Cliqr::Error::ValidationError,
73
+ "invalid Cliqr interface configuration - [value for 'basename' must match /^[a-zA-Z0-9_\\-]+$/; actual: \"invalid-name-!@#\", " \
74
+ "value 'Object' of type 'Class' for 'handler' does not extend from 'Cliqr::CLI::Command']"))
54
75
  end
55
76
  end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+
3
+ require 'fixtures/test_command'
4
+
5
+ describe Cliqr::CLI::OptionConfig do
6
+ it 'does not allow multiple options with same long name' do
7
+ expect do
8
+ Cliqr.interface do
9
+ basename 'my-command'
10
+ description 'a command used to test cliqr'
11
+ handler TestCommand
12
+
13
+ option 'option-1' do
14
+ short 'p'
15
+ end
16
+
17
+ option 'option-1' do
18
+ short 't'
19
+ end
20
+ end
21
+ end.to(raise_error(Cliqr::Error::DuplicateOptions, 'multiple options with long name "option-1"'))
22
+ end
23
+
24
+ it 'does not allow multiple options with same short name' do
25
+ expect do
26
+ Cliqr.interface do
27
+ basename 'my-command'
28
+ description 'a command used to test cliqr'
29
+ handler TestCommand
30
+
31
+ option 'option-1' do
32
+ short 'p'
33
+ end
34
+
35
+ option 'option-2' do
36
+ short 'p'
37
+ end
38
+ end
39
+ end.to(raise_error(Cliqr::Error::DuplicateOptions, 'multiple options with short name "p"'))
40
+ end
41
+
42
+ it 'does not allow option with empty long name' do
43
+ expect do
44
+ Cliqr.interface do
45
+ basename 'my-command'
46
+ description 'a command used to test cliqr'
47
+ handler TestCommand
48
+
49
+ option '' do
50
+ short 'p'
51
+ end
52
+ end
53
+ end.to(raise_error(Cliqr::Error::ValidationError,
54
+ "invalid Cliqr interface configuration - [options[1] - 'name' cannot be empty]"))
55
+ end
56
+
57
+ it 'does not allow option with empty short name' do
58
+ expect do
59
+ Cliqr.interface do
60
+ basename 'my-command'
61
+ description 'a command used to test cliqr'
62
+ handler TestCommand
63
+
64
+ option 'option-1' do
65
+ short ''
66
+ end
67
+ end
68
+ end.to(raise_error(Cliqr::Error::ValidationError,
69
+ "invalid Cliqr interface configuration - [options[1] - 'short' cannot be empty]"))
70
+ end
71
+
72
+ it 'does not allow option with nil long name' do
73
+ expect do
74
+ Cliqr.interface do
75
+ basename 'my-command'
76
+ description 'a command used to test cliqr'
77
+ handler TestCommand
78
+
79
+ option nil
80
+ end
81
+ end.to(raise_error(Cliqr::Error::ValidationError,
82
+ "invalid Cliqr interface configuration - [options[1] - 'name' cannot be nil]"))
83
+ end
84
+
85
+ it 'does not allow option with nil long name for second option' do
86
+ expect do
87
+ Cliqr.interface do
88
+ basename 'my-command'
89
+ description 'a command used to test cliqr'
90
+ handler TestCommand
91
+
92
+ option 'option-1'
93
+ option ''
94
+ end
95
+ end.to(raise_error(Cliqr::Error::ValidationError,
96
+ "invalid Cliqr interface configuration - [options[2] - 'name' cannot be empty]"))
97
+ end
98
+
99
+ it 'does not allow multiple characters in short name' do
100
+ expect do
101
+ Cliqr.interface do
102
+ basename 'my-command'
103
+ description 'a command used to test cliqr'
104
+ handler TestCommand
105
+
106
+ option 'option-1' do
107
+ short 'p1'
108
+ end
109
+ end
110
+ end.to(raise_error(Cliqr::Error::ValidationError,
111
+ "invalid Cliqr interface configuration - [options[1] - value for 'short' must match /^[a-z0-9A-Z]$/; actual: \"p1\"]"))
112
+ end
113
+
114
+ it 'does not allow invalid type values' do
115
+ expect do
116
+ Cliqr.interface do
117
+ basename 'my-command'
118
+ description 'a command used to test cliqr'
119
+ handler TestCommand
120
+
121
+ option 'option-1' do
122
+ type :random
123
+ end
124
+ end
125
+ end.to(raise_error(Cliqr::Error::ValidationError,
126
+ "invalid Cliqr interface configuration - [options[1] - invalid type 'random']"))
127
+ end
128
+
129
+ it 'does not allow empty type values' do
130
+ expect do
131
+ Cliqr.interface do
132
+ basename 'my-command'
133
+ description 'a command used to test cliqr'
134
+ handler TestCommand
135
+
136
+ option 'option-1' do
137
+ type ''
138
+ end
139
+ end
140
+ end.to(raise_error(Cliqr::Error::ValidationError,
141
+ "invalid Cliqr interface configuration - [options[1] - invalid type '']"))
142
+ end
143
+ end
@@ -5,6 +5,12 @@ require 'spec_helper'
5
5
  require 'fixtures/test_command'
6
6
 
7
7
  describe Cliqr::CLI::Interface do
8
+ it 'does not allow empty config' do
9
+ expect do
10
+ Cliqr::CLI::Interface.build(nil)
11
+ end.to(raise_error(Cliqr::Error::ConfigNotFound, 'a valid config should be defined'))
12
+ end
13
+
8
14
  it 'builds a base command with name' do
9
15
  cli = Cliqr.interface do
10
16
  basename 'my-command'
@@ -58,7 +64,7 @@ Available options:
58
64
  EOS
59
65
  end
60
66
 
61
- it 'allows command options to optionally have description and short name' do
67
+ it 'allows command options to optionally have description, type and short name' do
62
68
  cli = Cliqr.interface do
63
69
  basename 'my-command'
64
70
  description 'a command used to test cliqr'
@@ -79,139 +85,67 @@ Available options:
79
85
  EOS
80
86
  end
81
87
 
82
- it 'does not allow multiple options with same long name' do
83
- expect do
84
- Cliqr.interface do
85
- basename 'my-command'
86
- description 'a command used to test cliqr'
87
- handler TestCommand
88
-
89
- option 'option-1' do
90
- short 'p'
91
- end
92
-
93
- option 'option-1' do
94
- short 't'
95
- end
96
- end
97
- end.to(raise_error(Cliqr::Error::DuplicateOptions, 'multiple options with long name "option-1"'))
98
- end
99
-
100
- it 'does not allow multiple options with same short name' do
101
- expect do
102
- Cliqr.interface do
103
- basename 'my-command'
104
- description 'a command used to test cliqr'
105
- handler TestCommand
106
-
107
- option 'option-1' do
108
- short 'p'
109
- end
110
-
111
- option 'option-1' do
112
- short 't'
113
- end
114
- end
115
- end.to(raise_error(Cliqr::Error::DuplicateOptions, 'multiple options with long name "option-1"'))
116
- end
88
+ it 'has options if added during build phase' do
89
+ cli = Cliqr.interface do
90
+ basename 'my-command'
91
+ description 'a command used to test cliqr'
92
+ handler TestCommand
117
93
 
118
- it 'does not allow multiple options with same short name' do
119
- expect do
120
- Cliqr.interface do
121
- basename 'my-command'
122
- description 'a command used to test cliqr'
123
- handler TestCommand
124
-
125
- option 'option-1' do
126
- short 'p'
127
- end
128
-
129
- option 'option-2' do
130
- short 'p'
131
- end
94
+ option 'option-1' do
95
+ short 'p'
96
+ description 'a nice option to have'
132
97
  end
133
- end.to(raise_error(Cliqr::Error::DuplicateOptions, 'multiple options with short name "p"'))
98
+ end
99
+ expect(cli.config.options?).to be_truthy
134
100
  end
135
101
 
136
- it 'does not allow option with empty long name' do
137
- expect do
138
- Cliqr.interface do
139
- basename 'my-command'
140
- description 'a command used to test cliqr'
141
- handler TestCommand
142
-
143
- option '' do
144
- short 'p'
145
- end
146
- end
147
- end.to(raise_error(Cliqr::Error::InvalidOptionDefinition, 'option number 1 does not have a name field'))
148
- end
102
+ it 'allows command options to have a numeric value type' do
103
+ cli = Cliqr.interface do
104
+ basename 'my-command'
105
+ description 'a command used to test cliqr'
106
+ handler TestCommand
149
107
 
150
- it 'does not allow option with empty short name' do
151
- expect do
152
- Cliqr.interface do
153
- basename 'my-command'
154
- description 'a command used to test cliqr'
155
- handler TestCommand
156
-
157
- option 'option-1' do
158
- short ''
159
- end
108
+ option 'option-1' do
109
+ description 'a numeric option'
110
+ short 'p'
111
+ type :numeric
160
112
  end
161
- end.to(raise_error(Cliqr::Error::InvalidOptionDefinition, "option \"option-1\" has empty short name"))
162
- end
163
-
164
- it 'does not allow option with nil long name' do
165
- expect do
166
- Cliqr.interface do
167
- basename 'my-command'
168
- description 'a command used to test cliqr'
169
- handler TestCommand
113
+ end
170
114
 
171
- option nil
172
- end
173
- end.to(raise_error(Cliqr::Error::InvalidOptionDefinition, 'option number 1 does not have a name field'))
174
- end
115
+ expect(cli.usage).to eq <<-EOS
116
+ my-command -- a command used to test cliqr
175
117
 
176
- it 'does not allow option with nil long name for second option' do
177
- expect do
178
- Cliqr.interface do
179
- basename 'my-command'
180
- description 'a command used to test cliqr'
181
- handler TestCommand
118
+ USAGE:
119
+ my-command [options]
182
120
 
183
- option 'option-1'
184
- option ''
185
- end
186
- end.to(raise_error(Cliqr::Error::InvalidOptionDefinition, 'option number 2 does not have a name field'))
187
- end
121
+ Available options:
188
122
 
189
- it 'does not allow multiple characters in short name' do
190
- expect do
191
- Cliqr.interface do
192
- basename 'my-command'
193
- description 'a command used to test cliqr'
194
- handler TestCommand
195
-
196
- option 'option-1' do
197
- short 'p1'
198
- end
199
- end
200
- end.to(raise_error(Cliqr::Error::InvalidOptionDefinition,
201
- 'short option name can not have more than one characters in "option-1"'))
123
+ --option-1, -p : <numeric> a numeric option
124
+ EOS
202
125
  end
203
126
 
204
- it 'has options if added during build phase' do
127
+ it 'allows command options to have a boolean value type' do
205
128
  cli = Cliqr.interface do
206
129
  basename 'my-command'
207
130
  description 'a command used to test cliqr'
208
131
  handler TestCommand
209
132
 
210
133
  option 'option-1' do
134
+ description 'a boolean option'
211
135
  short 'p'
212
- description 'a nice option to have'
136
+ type :boolean
213
137
  end
214
138
  end
215
- expect(cli.config.options?).to be_truthy
139
+
140
+ expect(cli.usage).to eq <<-EOS
141
+ my-command -- a command used to test cliqr
142
+
143
+ USAGE:
144
+ my-command [options]
145
+
146
+ Available options:
147
+
148
+ --[no-]option-1, -p : <boolean> a boolean option
149
+ EOS
216
150
  end
217
151
  end