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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/README.md +46 -17
- data/lib/cliqr/argument_validation/argument_type_validator.rb +31 -0
- data/lib/cliqr/argument_validation/option_validator.rb +27 -0
- data/lib/cliqr/argument_validation/validator.rb +67 -0
- data/lib/cliqr/cli/command_context.rb +21 -12
- data/lib/cliqr/cli/config.rb +57 -9
- data/lib/cliqr/cli/executor.rb +17 -11
- data/lib/cliqr/cli/interface.rb +7 -3
- data/lib/cliqr/error.rb +17 -19
- data/lib/cliqr/parser/argument_parser.rb +21 -0
- data/lib/cliqr/parser/argument_tree_walker.rb +51 -0
- data/lib/cliqr/parser/boolean_option_token.rb +26 -0
- data/lib/cliqr/parser/parsed_input.rb +53 -0
- data/lib/cliqr/parser/parsed_input_builder.rb +61 -0
- data/lib/cliqr/parser/single_valued_option_token.rb +53 -0
- data/lib/cliqr/parser/token.rb +45 -0
- data/lib/cliqr/parser/token_factory.rb +69 -0
- data/lib/cliqr/validation/validation_set.rb +48 -0
- data/lib/cliqr/validation/validator_factory.rb +265 -0
- data/lib/cliqr/validation/verifiable.rb +89 -0
- data/lib/cliqr/validation_errors.rb +61 -0
- data/lib/cliqr/version.rb +1 -1
- data/spec/config/config_validator_spec.rb +51 -30
- data/spec/config/option_config_validator_spec.rb +143 -0
- data/spec/dsl/interface_spec.rb +48 -114
- data/spec/executor/executor_spec.rb +19 -1
- data/spec/fixtures/test_option_checker_command.rb +8 -0
- data/spec/parser/argument_parser_spec.rb +33 -39
- data/spec/validation/argument_validation_spec.rb +141 -0
- data/spec/validation/error_spec.rb +22 -0
- data/spec/validation/validation_spec.rb +11 -0
- metadata +27 -10
- data/lib/cliqr/cli/argument_validator.rb +0 -19
- data/lib/cliqr/cli/config_validator.rb +0 -104
- data/lib/cliqr/cli/parser/argument_parser.rb +0 -23
- data/lib/cliqr/cli/parser/argument_tree_walker.rb +0 -56
- data/lib/cliqr/cli/parser/option_token.rb +0 -72
- data/lib/cliqr/cli/parser/parsed_argument_builder.rb +0 -66
- data/lib/cliqr/cli/parser/token.rb +0 -38
- 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,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::
|
10
|
-
it 'does not allow empty
|
11
|
-
expect
|
12
|
-
|
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
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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::
|
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
|
data/spec/dsl/interface_spec.rb
CHANGED
@@ -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 '
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
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
|
98
|
+
end
|
99
|
+
expect(cli.config.options?).to be_truthy
|
134
100
|
end
|
135
101
|
|
136
|
-
it '
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
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
|
-
|
172
|
-
|
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
|
-
|
177
|
-
|
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
|
-
|
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
|
-
|
190
|
-
|
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 '
|
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
|
-
|
136
|
+
type :boolean
|
213
137
|
end
|
214
138
|
end
|
215
|
-
|
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
|