Skip to content

Commit c019766

Browse files
authored
Merge pull request #159 from DannyBen/add/full-validation
Validate the entire config prior to generating
2 parents 9f3ff81 + 405ad90 commit c019766

24 files changed

+273
-103
lines changed

lib/bashly/cli.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ def self.runner
1010

1111
runner.route 'init', to: Commands::Init
1212
runner.route 'preview', to: Commands::Preview
13+
runner.route 'validate', to: Commands::Validate
1314
runner.route 'generate', to: Commands::Generate
1415
runner.route 'add', to: Commands::Add
1516

lib/bashly/commands/validate.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Bashly
2+
module Commands
3+
class Validate < Base
4+
help "Scan the configuration file for errors"
5+
6+
usage "bashly validate"
7+
usage "bashly validate (-h|--help)"
8+
9+
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
10+
11+
def run
12+
config = Config.new "#{Settings.source_dir}/bashly.yml"
13+
validator = ConfigValidator.new config
14+
validator.validate
15+
say "!txtgrn!OK"
16+
end
17+
end
18+
end
19+
end

lib/bashly/config_validator.rb

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
module Bashly
2+
class ConfigValidator
3+
attr_reader :data
4+
5+
def initialize(data)
6+
@data = data
7+
end
8+
9+
def validate
10+
assert_command "root", data
11+
end
12+
13+
private
14+
15+
def assert(valid, message)
16+
raise ConfigurationError, message unless valid
17+
end
18+
19+
def refute(invalid, message)
20+
assert !invalid, message
21+
end
22+
23+
def assert_string(key, value)
24+
assert value.is_a?(String), "#{key} must be a string"
25+
end
26+
27+
def assert_optional_string(key, value)
28+
assert_string key, value if value
29+
end
30+
31+
def assert_boolean(key, value)
32+
assert [true, false, nil].include?(value), "#{key} must be a boolean"
33+
end
34+
35+
def assert_array(key, value, of: nil)
36+
return unless value
37+
assert value.is_a?(Array), "#{key} must be an array"
38+
if of
39+
value.each_with_index do |val, i|
40+
send "assert_#{of}".to_sym, "#{key}[#{i}]", val
41+
end
42+
end
43+
end
44+
45+
def assert_hash(key, value)
46+
assert value.is_a?(Hash), "#{key} must be a hash"
47+
end
48+
49+
def assert_version(key, value)
50+
return unless value
51+
assert [String, Integer, Float].include?(value.class),
52+
"#{key} must be a string or a number"
53+
end
54+
55+
def assert_catch_all(key, value)
56+
return unless value
57+
assert [TrueClass, String, Hash].include?(value.class),
58+
"#{key} must be a boolean, a string or a hash"
59+
60+
assert_catch_all_hash key, value if value.is_a? Hash
61+
end
62+
63+
def assert_catch_all_hash(key, value)
64+
assert_string "#{key}.label", value['label']
65+
assert_optional_string "#{key}.help", value['help']
66+
assert_boolean "#{key}.required", value['required']
67+
end
68+
69+
def assert_extensible(key, value)
70+
return unless value
71+
assert [TrueClass, String].include?(value.class),
72+
"#{key} must be a boolean or a string"
73+
end
74+
75+
def assert_arg(key, value)
76+
assert_hash key, value
77+
assert_string "#{key}.name", value['name']
78+
assert_optional_string "#{key}.help", value['help']
79+
assert_optional_string "#{key}.default", value['default']
80+
assert_optional_string "#{key}.validate", value['validate']
81+
assert_boolean "#{key}.required", value['required']
82+
83+
assert_array "#{key}.allowed", value['allowed'], of: :string
84+
end
85+
86+
def assert_flag(key, value)
87+
assert_hash key, value
88+
assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
89+
90+
assert_optional_string "#{key}.long", value['long']
91+
assert_optional_string "#{key}.short", value['short']
92+
assert_optional_string "#{key}.help", value['help']
93+
assert_optional_string "#{key}.arg", value['arg']
94+
assert_optional_string "#{key}.default", value['default']
95+
assert_optional_string "#{key}.validate", value['validate']
96+
97+
assert_boolean "#{key}.required", value['required']
98+
assert_array "#{key}.allowed", value['allowed'], of: :string
99+
end
100+
101+
def assert_env_var(key, value)
102+
assert_hash key, value
103+
assert_string "#{key}.name", value['name']
104+
assert_optional_string "#{key}.help", value['help']
105+
assert_optional_string "#{key}.default", value['default']
106+
assert_boolean "#{key}.required", value['required']
107+
end
108+
109+
def assert_command(key, value)
110+
assert_hash key, value
111+
112+
refute value['commands'] && value['args'], "#{key} cannot have both commands and args"
113+
refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
114+
115+
assert_string "#{key}.name", value['name']
116+
assert_optional_string "#{key}.short", value['short']
117+
assert_optional_string "#{key}.help", value['help']
118+
assert_optional_string "#{key}.footer", value['footer']
119+
assert_optional_string "#{key}.group", value['group']
120+
121+
assert_boolean "#{key}.default", value['default']
122+
assert_version "#{key}.version", value['version']
123+
assert_catch_all "#{key}.catch_all", value['catch_all']
124+
assert_extensible "#{key}.extensible", value['extensible']
125+
126+
assert_array "#{key}.args", value['args'], of: :arg
127+
assert_array "#{key}.flags", value['flags'] , of: :flag
128+
assert_array "#{key}.commands", value['commands'], of: :command
129+
assert_array "#{key}.completions", value['completions'], of: :string
130+
assert_array "#{key}.dependencies", value['dependencies'], of: :string
131+
assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
132+
assert_array "#{key}.examples", value['examples'], of: :string
133+
end
134+
end
135+
end

lib/bashly/script/argument.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ class Argument < Base
44
def usage_string
55
required ? name.upcase : "[#{name.upcase}]"
66
end
7-
8-
def verify
9-
raise ConfigurationError, "Argument must have a name" unless name
10-
end
117
end
128
end
139
end

lib/bashly/script/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class Base
3333
def initialize(options)
3434
raise Error, "Invalid options provided" unless options.respond_to? :keys
3535
@options = options
36-
verify if respond_to? :verify
36+
validate_options if respond_to? :validate_options
3737
end
3838

3939
def optional

lib/bashly/script/command.rb

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,15 @@ def caption_string
3232

3333
# Returns a label for the catch_all directive
3434
def catch_all_label
35-
return nil unless catch_all
36-
37-
if catch_all.is_a? String
38-
"#{catch_all.upcase}..."
39-
elsif catch_all.is_a?(Hash) and catch_all['label'].is_a?(String)
40-
"#{catch_all['label'].upcase}..."
41-
else
42-
"..."
35+
case catch_all
36+
when nil then nil
37+
when String then "#{catch_all.upcase}..."
38+
when Hash then "#{catch_all['label'].upcase}..."
39+
else "..."
4340
end
4441
end
4542

46-
# Returns a used defined help string for the catch_all directive
43+
# Returns a user defined help string for the catch_all directive
4744
def catch_all_help
4845
return nil unless catch_all
4946

@@ -213,10 +210,9 @@ def user_lib
213210
end
214211

215212
# Raise an exception if there are some serious issues with the command
216-
# definition.
217-
def verify
218-
verify_commands if commands.any?
219-
raise ConfigurationError, "Command must have a name" unless name
213+
# definition. This is called by Base#initialize.
214+
def validate_options
215+
Bashly::ConfigValidator.new(options).validate
220216
end
221217

222218
# Returns an array of all the args with a whitelist
@@ -229,14 +225,6 @@ def whitelisted_flags
229225
flags.select &:allowed
230226
end
231227

232-
private
233-
234-
def verify_commands
235-
if args.any? or flags.any?
236-
raise ConfigurationError, "Error in the !txtgrn!#{full_name}!txtrst! command.\nThe !txtgrn!commands!txtrst! key cannot be at the same level as the !txtgrn!args!txtrst! or !txtgrn!flags!txtrst! keys."
237-
end
238-
end
239-
240228
end
241229
end
242230
end

lib/bashly/script/environment_variable.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ def usage_string(extended: false)
66
result << strings[:required] if required and extended
77
result.join " "
88
end
9-
10-
def verify
11-
raise ConfigurationError, "EnvironmentVariable must have a name" unless name
12-
end
139
end
1410
end
1511
end

lib/bashly/script/flag.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ def usage_string(extended: false)
2121
result << strings[:required] if required and extended
2222
result.join " "
2323
end
24-
25-
def verify
26-
raise ConfigurationError, "Flag must have a long and/or short property" unless short or long
27-
end
28-
2924
end
3025
end
3126
end

spec/approvals/cli/commands

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ Bashly - Bash CLI Generator
33
Commands:
44
init Initialize a new workspace
55
preview Generate the bash script to STDOUT
6+
validate Scan the configuration file for errors
67
generate Generate the bash script and required files
78
add Add extra features and customization to your script

spec/approvals/cli/validate/help

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Scan the configuration file for errors
2+
3+
Usage:
4+
bashly validate
5+
bashly validate (-h|--help)
6+
7+
Options:
8+
-h --help
9+
Show this help
10+
11+
Environment Variables:
12+
BASHLY_SOURCE_DIR
13+
The path containing the bashly configuration and source files [default: src]

0 commit comments

Comments
 (0)