cli_miami 0.0.9 → 1.0.1.pre
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/README.md +9 -3
- data/i18n/en.yml +34 -0
- data/lib/cli_miami/ask.rb +174 -34
- data/lib/cli_miami/core.rb +84 -0
- data/lib/cli_miami/error.rb +108 -0
- data/lib/cli_miami/say.rb +62 -52
- data/lib/cli_miami/validation.rb +189 -0
- data/lib/cli_miami.rb +1 -1
- data/lib/namespaced.rb +71 -21
- data/spec/fixtures/i18n.yml +36 -0
- data/spec/lib/cli_miami/ask_spec.rb +134 -0
- data/spec/lib/cli_miami/error_spec.rb +75 -0
- data/spec/lib/cli_miami/say_spec.rb +87 -0
- data/spec/lib/cli_miami/validation_spec.rb +52 -0
- data/spec/lib/cli_miami_spec.rb +81 -0
- data/spec/spec_helper.rb +83 -0
- metadata +122 -28
- data/.gitignore +0 -2
- data/.rspec +0 -3
- data/.travis.yml +0 -6
- data/Gemfile +0 -11
- data/Gemfile.lock +0 -100
- data/Guardfile +0 -5
- data/yuyi_menu +0 -8
@@ -0,0 +1,189 @@
|
|
1
|
+
#
|
2
|
+
# validates user input values against their expected type
|
3
|
+
#
|
4
|
+
class CliMiami::Validation
|
5
|
+
attr_reader :error, :valid, :value
|
6
|
+
|
7
|
+
# validates string provided by the user
|
8
|
+
#
|
9
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
10
|
+
def initialize initial_value, options
|
11
|
+
@options = CliMiami.get_options options
|
12
|
+
@value = initial_value
|
13
|
+
@valid = true
|
14
|
+
@error = nil
|
15
|
+
|
16
|
+
# convert
|
17
|
+
# only attempt to convert strings
|
18
|
+
# if not a string, skip conversion and go straight to validation
|
19
|
+
if initial_value.is_a? String
|
20
|
+
begin
|
21
|
+
@value = send("convert_string_to_#{options[:type]}", initial_value, @options)
|
22
|
+
rescue
|
23
|
+
@error = CliMiami::Error.new(initial_value, @options, :convert).message
|
24
|
+
@valid = false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
return if @value.nil? || !@valid
|
29
|
+
|
30
|
+
# validate
|
31
|
+
begin
|
32
|
+
# keep all values the same unless validation fails
|
33
|
+
send "validate_#{options[:type]}", @value, @options
|
34
|
+
rescue => error
|
35
|
+
# if validation fails, set values, including error to the specific validation error
|
36
|
+
@error = error.to_s
|
37
|
+
@valid = false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
41
|
+
|
42
|
+
def valid?
|
43
|
+
@valid
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
#
|
49
|
+
# TYPE CONVERSIONS
|
50
|
+
# all type conversions must raise an exception if they fail
|
51
|
+
#
|
52
|
+
def convert_string_to_boolean string, _options
|
53
|
+
return true if CliMiami::BOOLEAN_TRUE_VALUES.include? string.downcase
|
54
|
+
return false if CliMiami::BOOLEAN_FALSE_VALUES.include? string.downcase
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
|
58
|
+
def convert_string_to_file string, _options
|
59
|
+
return string if string.empty?
|
60
|
+
File.absolute_path(File.expand_path(string))
|
61
|
+
end
|
62
|
+
|
63
|
+
# the `Integer` class fails when passed a float, so we convert to a float and back to an integer
|
64
|
+
# this allows users to enter a float when an integer is requested without it failing
|
65
|
+
#
|
66
|
+
def convert_string_to_fixnum string, _options
|
67
|
+
Integer Float(string).to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
def convert_string_to_float string, _options
|
71
|
+
Float string
|
72
|
+
end
|
73
|
+
|
74
|
+
def convert_string_to_range string, _options
|
75
|
+
range_array = string.split('..').map { |i| Float(i) }.sort
|
76
|
+
|
77
|
+
# check that array has 2 supported range values
|
78
|
+
return Range.new(range_array[0], range_array[1]) if range_array.length == 2
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
|
82
|
+
# pass-through method for converting a string to a string
|
83
|
+
def convert_string_to_string string, _options
|
84
|
+
string
|
85
|
+
end
|
86
|
+
|
87
|
+
def convert_string_to_symbol string, _options
|
88
|
+
raw_symbol = string.underscore.to_sym
|
89
|
+
|
90
|
+
# if the symbol is quoted, remove some characters and try converting again
|
91
|
+
if raw_symbol.inspect =~ /\:\"/
|
92
|
+
# convert non a-z to underscores, then remove duplicate or leading/trailing underscores
|
93
|
+
converted_symbol = raw_symbol.to_s.gsub(/[^a-z]/, '_').gsub(/\_+/, '_').gsub(/^_/, '').gsub(/_$/, '').to_sym
|
94
|
+
|
95
|
+
# if symbol was converted, return it, otherwise raise an error
|
96
|
+
return converted_symbol unless converted_symbol.empty?
|
97
|
+
raise
|
98
|
+
end
|
99
|
+
|
100
|
+
raw_symbol
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# TYPE VALIDATORS
|
105
|
+
# all type validators must raise an exception if they fail
|
106
|
+
#
|
107
|
+
|
108
|
+
# validate the value is within the min and max values
|
109
|
+
#
|
110
|
+
def validate_length value, length, options
|
111
|
+
unless (options[:min]..options[:max]).cover? length
|
112
|
+
options[:value_length] = length
|
113
|
+
|
114
|
+
# replace empty values with i18n `empty` string
|
115
|
+
value = I18n.t('cli_miami.core.empty') if value.respond_to?(:empty?) && value.empty?
|
116
|
+
|
117
|
+
raise CliMiami::Error.new(value, options, :length).message
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# validate that the value passes a regular expression
|
122
|
+
#
|
123
|
+
def validate_regexp value, options
|
124
|
+
unless value =~ options[:regexp]
|
125
|
+
raise CliMiami::Error.new(value, options, :regexp).message
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def validate_array array, options
|
130
|
+
validate_length array, array.length, options
|
131
|
+
end
|
132
|
+
|
133
|
+
# no validation needed for boolean types
|
134
|
+
# type conversion is all the validation required
|
135
|
+
def validate_boolean boolean, options
|
136
|
+
end
|
137
|
+
|
138
|
+
def validate_file file, options
|
139
|
+
raise if file.empty?
|
140
|
+
File.new File.expand_path file
|
141
|
+
rescue
|
142
|
+
raise CliMiami::Error.new(file, options, :validate).message
|
143
|
+
end
|
144
|
+
|
145
|
+
def validate_fixnum fixnum, options
|
146
|
+
validate_length fixnum, fixnum, options
|
147
|
+
end
|
148
|
+
|
149
|
+
def validate_float float, options
|
150
|
+
validate_length float, float, options
|
151
|
+
end
|
152
|
+
|
153
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
154
|
+
def validate_hash hash, options
|
155
|
+
hash.deep_symbolize_keys!
|
156
|
+
|
157
|
+
validate_length hash, hash.keys.length, options
|
158
|
+
|
159
|
+
# return if no value options are set
|
160
|
+
value_options = options[:value_options]
|
161
|
+
return unless value_options
|
162
|
+
|
163
|
+
# validate required keys are set
|
164
|
+
if value_options[:keys]
|
165
|
+
missing_keys = Set.new(value_options[:keys].map(&:to_sym)) - Set.new(hash.keys)
|
166
|
+
value_options[:missing_values] = missing_keys.to_a.to_sentence
|
167
|
+
|
168
|
+
unless missing_keys.empty?
|
169
|
+
raise CliMiami::Error.new(hash, options, :keys).message
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
174
|
+
|
175
|
+
def validate_range range, options
|
176
|
+
range_diff = (range.max - range.min) + 1
|
177
|
+
validate_length range, range_diff, options
|
178
|
+
end
|
179
|
+
|
180
|
+
def validate_string string, options
|
181
|
+
validate_length string, string.length, options
|
182
|
+
validate_regexp string, options
|
183
|
+
end
|
184
|
+
|
185
|
+
def validate_symbol symbol, options
|
186
|
+
validate_length symbol, symbol.to_s.length, options
|
187
|
+
validate_regexp symbol, options
|
188
|
+
end
|
189
|
+
end
|
data/lib/cli_miami.rb
CHANGED
data/lib/namespaced.rb
CHANGED
@@ -1,30 +1,80 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# root namespace class
|
2
|
+
#
|
3
3
|
class CliMiami
|
4
|
+
# require & include core first
|
5
|
+
require 'cli_miami/core'
|
6
|
+
include CliMiami::Core
|
7
|
+
|
8
|
+
# CliMiami library
|
9
|
+
require 'cli_miami/error'
|
10
|
+
require 'cli_miami/validation'
|
4
11
|
require 'cli_miami/ask'
|
5
12
|
require 'cli_miami/say'
|
6
13
|
|
7
|
-
# default presets
|
8
|
-
@@presets = {
|
9
|
-
:fail => {
|
10
|
-
:color => :red
|
11
|
-
},
|
12
|
-
:warn => {
|
13
|
-
:color => :yellow
|
14
|
-
},
|
15
|
-
:success => {
|
16
|
-
:color => :green
|
17
|
-
}
|
18
|
-
}
|
19
|
-
|
20
|
-
# Returns all presets
|
21
|
-
def self.presets
|
22
|
-
@@presets
|
23
|
-
end
|
24
|
-
|
25
14
|
# Create a new custom preset
|
26
15
|
def self.set_preset type, options
|
27
|
-
raise 'Preset must be a hash of options' unless options.is_a? Hash
|
16
|
+
raise ArgumentError, 'Preset must be a hash of options' unless options.is_a? Hash
|
28
17
|
@@presets[type] = options
|
29
18
|
end
|
19
|
+
|
20
|
+
# build an options hash from preset options and/or additional options
|
21
|
+
#
|
22
|
+
# build shared validation hash object
|
23
|
+
#
|
24
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
25
|
+
def self.get_options user_options = {}
|
26
|
+
# lookup preset if passed in as a symbol, or as a :preset key in an options hash
|
27
|
+
if user_options.is_a? Hash
|
28
|
+
user_options.deep_symbolize_keys!
|
29
|
+
options = @@presets[user_options.delete(:preset)] || {}
|
30
|
+
options.deep_merge! user_options
|
31
|
+
else
|
32
|
+
options = @@presets[user_options.to_sym] || {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# make sure all keys are symbols
|
36
|
+
options.deep_symbolize_keys!
|
37
|
+
|
38
|
+
# set defaults
|
39
|
+
options.reverse_merge!(
|
40
|
+
description: I18n.t('cli_miami.core.no_description'),
|
41
|
+
max: Float::INFINITY,
|
42
|
+
min: options.delete(:required) ? 1 : 0,
|
43
|
+
newline: true,
|
44
|
+
justify: :left,
|
45
|
+
padding: 0,
|
46
|
+
regexp: //,
|
47
|
+
style: [],
|
48
|
+
type: :string
|
49
|
+
)
|
50
|
+
|
51
|
+
# prepare style array
|
52
|
+
options[:style] = [options[:style]].flatten
|
53
|
+
|
54
|
+
# lookup type in type map
|
55
|
+
options[:type] = TYPE_MAP[options[:type].to_sym] || :string
|
56
|
+
|
57
|
+
# convert range to min/max values
|
58
|
+
range = options.delete(:range)
|
59
|
+
if range
|
60
|
+
options[:min] = range.min
|
61
|
+
options[:max] = range.max
|
62
|
+
end
|
63
|
+
|
64
|
+
# convert length to min/max values
|
65
|
+
length = options.delete(:length)
|
66
|
+
if length
|
67
|
+
options[:min] = options[:max] = length
|
68
|
+
end
|
69
|
+
|
70
|
+
# if value options are set, apply the same treatment to them
|
71
|
+
value_options = options[:value_options]
|
72
|
+
if value_options
|
73
|
+
options[:value_options] = get_options value_options
|
74
|
+
options[:value_options][:description] = options[:description]
|
75
|
+
end
|
76
|
+
|
77
|
+
options
|
78
|
+
end
|
79
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
30
80
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
en:
|
2
|
+
cli_miami:
|
3
|
+
errors:
|
4
|
+
spec: generic error
|
5
|
+
|
6
|
+
type:
|
7
|
+
spec: "type: %{value} - %{allowed_values}"
|
8
|
+
nested:
|
9
|
+
spec: nested spec
|
10
|
+
|
11
|
+
array:
|
12
|
+
spec: "array: %{value} - %{allowed_values}"
|
13
|
+
|
14
|
+
boolean:
|
15
|
+
spec: "boolean: %{value} - %{allowed_values}"
|
16
|
+
|
17
|
+
file:
|
18
|
+
spec: "file: %{value} - %{allowed_values}"
|
19
|
+
|
20
|
+
fixnum:
|
21
|
+
spec: "fixnum: %{value} - %{allowed_values}"
|
22
|
+
|
23
|
+
float:
|
24
|
+
spec: "float: %{value} - %{allowed_values}"
|
25
|
+
|
26
|
+
hash:
|
27
|
+
spec: "hash: %{value} - %{allowed_values}"
|
28
|
+
|
29
|
+
range:
|
30
|
+
spec: "range: %{value} - %{allowed_values}"
|
31
|
+
|
32
|
+
string:
|
33
|
+
spec: "string: %{value} - %{allowed_values}"
|
34
|
+
|
35
|
+
symbol:
|
36
|
+
spec: "symbol: %{value} - %{allowed_values}"
|
@@ -0,0 +1,134 @@
|
|
1
|
+
describe CliMiami::A do
|
2
|
+
before do
|
3
|
+
@q = 'Who am i?'
|
4
|
+
allow(CliMiami::S).to receive(:ay).and_call_original
|
5
|
+
allow_any_instance_of(CliMiami::A).to receive(:request_string).and_return 'Jane Doe'
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
allow_any_instance_of(CliMiami::A).to receive(:request_string).and_call_original
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.sk' do
|
13
|
+
context 'when passed a block' do
|
14
|
+
it 'should yield a response' do
|
15
|
+
CliMiami::A.sk @q do |response|
|
16
|
+
expect(response).to eq 'Jane Doe'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when not passed a block' do
|
22
|
+
it 'should return a response' do
|
23
|
+
expect(CliMiami::A.sk(@q).value).to eq 'Jane Doe'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# COMPLEX REQUEST TYPES
|
30
|
+
#
|
31
|
+
describe 'ARRAY' do
|
32
|
+
it 'should allow user to enter values until they enter an empty string' do
|
33
|
+
allow($stdin).to receive(:gets).and_return 'foo', 'bar', ''
|
34
|
+
ask = CliMiami::A.sk @q, type: :array
|
35
|
+
expect(ask.value).to eq %w(foo bar)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should prevent user from entering less than the minimum' do
|
39
|
+
allow($stdin).to receive(:gets).and_return 'foo', '', 'bar', ''
|
40
|
+
ask = CliMiami::A.sk @q, type: :array, min: 2
|
41
|
+
expect(ask.value).to eq %w(foo bar)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should stop prompting user when the maximum is reached' do
|
45
|
+
allow($stdin).to receive(:gets).and_return 'foo', 'bar'
|
46
|
+
ask = CliMiami::A.sk @q, type: :array, max: 2
|
47
|
+
expect(ask.value).to eq %w(foo bar)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'HASH' do
|
52
|
+
it 'should allow user to enter keys & values until they enter an empty key string' do
|
53
|
+
allow($stdin).to receive(:gets).and_return 'foo1', '1', 'bar1', '2', ''
|
54
|
+
ask = CliMiami::A.sk @q, type: :hash
|
55
|
+
expect(ask.value).to eq(foo1: '1', bar1: '2')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should prevent user from entering less than the minimum' do
|
59
|
+
allow($stdin).to receive(:gets).and_return 'foo2', '1', '', 'bar2', '2', ''
|
60
|
+
ask = CliMiami::A.sk @q, type: :hash, min: 2
|
61
|
+
expect(ask.value).to eq(foo2: '1', bar2: '2')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should stop prompting user when the maximum is reached' do
|
65
|
+
allow($stdin).to receive(:gets).and_return 'foo3', '1'
|
66
|
+
ask = CliMiami::A.sk @q, type: :hash, max: 1
|
67
|
+
expect(ask.value).to eq(foo3: '1')
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when keys options is set' do
|
71
|
+
it 'should prompt for keys first, then user-defined keys' do
|
72
|
+
allow($stdin).to receive(:gets).and_return '1', 'bar4', '2', ''
|
73
|
+
ask = CliMiami::A.sk @q, type: :hash, keys: [:foo4]
|
74
|
+
expect(ask.value).to eq(foo4: '1', bar4: '2')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'RANGE' do
|
80
|
+
it 'should allow user to enter floats or integers for start and end values' do
|
81
|
+
allow($stdin).to receive(:gets).and_return '1.1', '3'
|
82
|
+
ask = CliMiami::A.sk @q, type: :range
|
83
|
+
expect(ask.value).to eq((1.1..3.0))
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should prevent user from entering non-numeric values' do
|
87
|
+
allow($stdin).to receive(:gets).and_return 'foo', '1', 'bar', '3'
|
88
|
+
ask = CliMiami::A.sk @q, type: :range
|
89
|
+
expect(ask.value).to eq((1.0..3.0))
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should re-prompt user if range is invalid' do
|
93
|
+
allow($stdin).to receive(:gets).and_return '1', '3', '1', '4'
|
94
|
+
ask = CliMiami::A.sk @q, type: :range, min: 4
|
95
|
+
expect(ask.value).to eq((1.0..4.0))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# SIMPLE REQUEST TYPES
|
101
|
+
#
|
102
|
+
context 'with simple types' do
|
103
|
+
shared_examples 'when prompting the user' do |type, invalid_string, valid_string, value|
|
104
|
+
it 'should prompt until a valid value is entered' do
|
105
|
+
allow($stdin).to receive(:gets).and_return(invalid_string, valid_string)
|
106
|
+
allow(Readline).to receive(:readline).and_return(invalid_string, valid_string)
|
107
|
+
ask = CliMiami::A.sk type.to_s, type: type
|
108
|
+
expect(ask.value).to eq value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'BOOLEAN' do
|
113
|
+
it_behaves_like 'when prompting the user', :boolean, 'foo', 'TrUe', true
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'FILE' do
|
117
|
+
it_behaves_like 'when prompting the user', :file, 'foo', '.', Dir.pwd
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'FLOAT' do
|
121
|
+
it_behaves_like 'when prompting the user', :float, 'foo', '2.0', 2.0
|
122
|
+
it_behaves_like 'when prompting the user', :float, 'foo', '3', 3
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'FIXNUM' do
|
126
|
+
it_behaves_like 'when prompting the user', :fixnum, 'foo', '2', 2
|
127
|
+
it_behaves_like 'when prompting the user', :fixnum, 'foo', '3.8', 3
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'SYMBOL' do
|
131
|
+
it_behaves_like 'when prompting the user', :symbol, '$', '$f_ 0o8<0o', :f_o_o
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
describe CliMiami::Error do
|
2
|
+
describe '.initialize' do
|
3
|
+
context 'with valid type error' do
|
4
|
+
CliMiami::TYPE_MAP.values.uniq.each do |type|
|
5
|
+
case type
|
6
|
+
|
7
|
+
when :array
|
8
|
+
value_type = [1, nil, :foo, '', (1..2)]
|
9
|
+
value_string = '1, [EMPTY], foo, [EMPTY], and 1-2'
|
10
|
+
allowed_values = '1-38'
|
11
|
+
|
12
|
+
when :boolean
|
13
|
+
value_type = true
|
14
|
+
value_string = 'true'
|
15
|
+
allowed_values = (CliMiami::BOOLEAN_TRUE_VALUES + CliMiami::BOOLEAN_FALSE_VALUES).to_sentence
|
16
|
+
|
17
|
+
when :file
|
18
|
+
value_type = File.new('.')
|
19
|
+
value_string = File.expand_path('.')
|
20
|
+
allowed_values = '?'
|
21
|
+
|
22
|
+
when :fixnum
|
23
|
+
value_type = 6
|
24
|
+
value_string = '6'
|
25
|
+
allowed_values = '1-38'
|
26
|
+
|
27
|
+
when :float
|
28
|
+
value_type = 6.66
|
29
|
+
value_string = '6.66'
|
30
|
+
allowed_values = '1-38'
|
31
|
+
|
32
|
+
when :hash
|
33
|
+
value_type = { foo: 1, bar: 2 }
|
34
|
+
value_string = 'foo: 1 and bar: 2'
|
35
|
+
allowed_values = '1-38'
|
36
|
+
|
37
|
+
when :range
|
38
|
+
value_type = (4..20)
|
39
|
+
value_string = '4-20'
|
40
|
+
allowed_values = '1-38'
|
41
|
+
|
42
|
+
when :string
|
43
|
+
value_type = 'foo'
|
44
|
+
value_string = 'foo'
|
45
|
+
allowed_values = '1-38'
|
46
|
+
|
47
|
+
when :symbol
|
48
|
+
value_type = :foo
|
49
|
+
value_string = 'foo'
|
50
|
+
allowed_values = '1-38'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should convert #{type} to string" do
|
54
|
+
expect(CliMiami::Error.new(value_type, { type: type, min: 1, max: 38, regexp: /6{3}/ }, :spec).message).to eq "#{type}: #{value_string} - #{allowed_values}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should support multiple/nested keys' do
|
59
|
+
expect(CliMiami::Error.new('foo', { type: :type }, :nested, :spec).message).to eq 'nested spec'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with invalid type error' do
|
64
|
+
it 'should have a generic error' do
|
65
|
+
expect(CliMiami::Error.new('foo', { type: :foo }, :spec).message).to eq 'generic error'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when no error is found' do
|
70
|
+
it 'should have an unknown error' do
|
71
|
+
expect(CliMiami::Error.new('foo', { type: :foo }, :bar).message).to eq 'Unknown Error'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
describe CliMiami::S do
|
2
|
+
subject { CliMiami::S }
|
3
|
+
|
4
|
+
describe '.ay' do
|
5
|
+
it 'should not accept no arguments' do
|
6
|
+
expect { subject.ay 'no arguments' }.to_not raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'with options' do
|
10
|
+
it 'should accept color' do
|
11
|
+
expect($stdout).to receive(:puts).with "\e[31mcolor\e[0m"
|
12
|
+
subject.ay 'color', color: :red
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should accept bgcolor' do
|
16
|
+
expect($stdout).to receive(:puts).with "\e[41mbgcolor\e[0m"
|
17
|
+
subject.ay 'bgcolor', bgcolor: :red
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should accept justify / padding' do
|
21
|
+
expect($stdout).to receive(:puts).with(/^\s{1}justify\s{2}$/)
|
22
|
+
subject.ay 'justify', justify: :center, padding: 10
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should accept indent' do
|
26
|
+
expect($stdout).to receive(:puts).with(/^\s{10}indent$/)
|
27
|
+
subject.ay 'indent', indent: 10
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should accept newline' do
|
31
|
+
# when true, use puts
|
32
|
+
expect($stdout).to receive(:puts).with 'newline'
|
33
|
+
subject.ay 'newline', newline: true
|
34
|
+
|
35
|
+
# when false, use print
|
36
|
+
expect($stdout).to receive(:print).with 'newline '
|
37
|
+
subject.ay 'newline', newline: false
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should accept overwrite' do
|
41
|
+
expect($stdout).to receive(:print).with "overwrite\r "
|
42
|
+
subject.ay 'overwrite', overwrite: true
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with style' do
|
46
|
+
it 'should accept style' do
|
47
|
+
expect($stdout).to receive(:puts).with "\e[4mstyle\e[0m"
|
48
|
+
subject.ay 'style', style: :underline
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should apply bright style to color' do
|
52
|
+
expect($stdout).to receive(:puts).with "\e[91mbright\e[0m"
|
53
|
+
subject.ay 'bright', color: :red, style: :bright
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should accept multiple styles' do
|
57
|
+
expect($stdout).to receive(:puts).with "\e[4m\e[1m\e[91mmultiple\e[0m\e[0m\e[0m"
|
58
|
+
subject.ay 'multiple', color: :red, style: [:bright, :bold, :underline]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with preset' do
|
64
|
+
before do
|
65
|
+
CliMiami.set_preset :preset_symbol,
|
66
|
+
color: :blue,
|
67
|
+
bgcolor: :white,
|
68
|
+
style: :underline
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should apply preset as symbol' do
|
72
|
+
expect($stdout).to receive(:puts).with "\e[4m\e[47m\e[34mpreset\e[0m\e[0m\e[0m"
|
73
|
+
subject.ay 'preset', :preset_symbol
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should apply preset as option' do
|
77
|
+
expect($stdout).to receive(:puts).with "\e[4m\e[47m\e[34mpreset\e[0m\e[0m\e[0m"
|
78
|
+
subject.ay 'preset', preset: :preset_symbol
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should override presets with additional options' do
|
82
|
+
expect($stdout).to receive(:puts).with "\e[4m\e[47m\e[31mpreset\e[0m\e[0m\e[0m"
|
83
|
+
subject.ay 'preset', preset: :preset_symbol, color: :red
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
describe CliMiami::Validation do
|
2
|
+
describe '.initialize' do
|
3
|
+
shared_examples 'a valid type' do |type, valid_string, value|
|
4
|
+
it "and should return a #{type}" do
|
5
|
+
validation = CliMiami::Validation.new valid_string, type: type
|
6
|
+
expect(validation.value).to eq value
|
7
|
+
expect(validation.valid?).to eq true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
shared_examples 'an invalid type' do |type, invalid_string|
|
12
|
+
it 'and should set the error field' do
|
13
|
+
validation = CliMiami::Validation.new invalid_string, type: type
|
14
|
+
expect(validation.value).to eq invalid_string
|
15
|
+
expect(validation.valid?).to eq false
|
16
|
+
expect(validation.error).to_not be_empty
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'BOOLEAN' do
|
21
|
+
it_behaves_like 'a valid type', :boolean, 'TrUe', true
|
22
|
+
it_behaves_like 'an invalid type', :boolean, 'foo'
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'FILE' do
|
26
|
+
it_behaves_like 'a valid type', :file, '.', Dir.pwd
|
27
|
+
it_behaves_like 'an invalid type', :file, File.join(Dir.pwd, 'foo')
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'FLOAT' do
|
31
|
+
it_behaves_like 'a valid type', :float, '2.0', 2.0
|
32
|
+
it_behaves_like 'a valid type', :float, '3', 3.0
|
33
|
+
it_behaves_like 'an invalid type', :float, 'foo'
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'FIXNUM' do
|
37
|
+
it_behaves_like 'a valid type', :fixnum, '3', 3
|
38
|
+
it_behaves_like 'a valid type', :fixnum, '3.8', 3
|
39
|
+
it_behaves_like 'an invalid type', :fixnum, 'foo'
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'RANGE' do
|
43
|
+
it_behaves_like 'a valid type', :range, '2..4', (2..4)
|
44
|
+
it_behaves_like 'an invalid type', :range, 'foo'
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'SYMBOL' do
|
48
|
+
it_behaves_like 'a valid type', :symbol, '$F 8o*: .o.!', :f_o_o
|
49
|
+
it_behaves_like 'an invalid type', :symbol, '$'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|