easol-canvas 0.1.1 → 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/lib/canvas/checks/valid_block_schemas_check.rb +72 -0
- data/lib/canvas/checks/valid_footer_schema_check.rb +81 -0
- data/lib/canvas/checks/valid_json_check.rb +24 -0
- data/lib/canvas/checks/valid_liquid_check.rb +1 -1
- data/lib/canvas/checks/valid_menu_schema_check.rb +81 -0
- data/lib/canvas/constants.rb +12 -0
- data/lib/canvas/services/expand_attributes.rb +40 -0
- data/lib/canvas/services/fetch_custom_types.rb +27 -0
- data/lib/canvas/validators/block_schema.rb +86 -0
- data/lib/canvas/validators/footer_schema.rb +29 -0
- data/lib/canvas/validators/html.rb +2 -2
- data/lib/canvas/validators/json.rb +21 -0
- data/lib/canvas/validators/liquid.rb +1 -1
- data/lib/canvas/validators/menu_schema.rb +95 -0
- data/lib/canvas/validators/schema_attribute.rb +147 -0
- data/lib/canvas/validators/schema_attributes/base.rb +104 -0
- data/lib/canvas/validators/schema_attributes/color.rb +81 -0
- data/lib/canvas/validators/schema_attributes/image.rb +45 -0
- data/lib/canvas/validators/schema_attributes/link.rb +51 -0
- data/lib/canvas/validators/schema_attributes/number.rb +17 -0
- data/lib/canvas/validators/schema_attributes/page.rb +49 -0
- data/lib/canvas/validators/schema_attributes/post.rb +49 -0
- data/lib/canvas/validators/schema_attributes/product.rb +49 -0
- data/lib/canvas/validators/schema_attributes/radio.rb +55 -0
- data/lib/canvas/validators/schema_attributes/range.rb +24 -0
- data/lib/canvas/validators/schema_attributes/select.rb +59 -0
- data/lib/canvas/validators/schema_attributes/variant.rb +49 -0
- data/lib/canvas/version.rb +1 -1
- data/lib/canvas.rb +14 -3
- metadata +43 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8570c1c4d1a8f7097f621722730ca8c882b633b901c8d22fb13f7dcbcb2ac8df
|
4
|
+
data.tar.gz: d10b1319c6e1be3e0e43a77575fbf73f71f4a9f77582486d5b34d374d9c42105
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dbfd55872ab41728e9243392a02c64551379cf42b99f59e455ade832fc028aa154b78e93600ec8bba8ac4fdcc52001850f2fd9cead452d64b896e1a997a0c63
|
7
|
+
data.tar.gz: d6507b81b2fe0f72aad4407601606fa305b45e0d2142753eccc03cda5760277ceeaf54f83b6d8468274490e33afa4360da7174dd23599b4e5aa0a9fe484ab5d3
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each block template file.
|
7
|
+
#
|
8
|
+
# Example of block Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# my_title:
|
12
|
+
# type: string
|
13
|
+
# my_color:
|
14
|
+
# type: color
|
15
|
+
# label: My color
|
16
|
+
# hint: "Select your favourite color"
|
17
|
+
# ---
|
18
|
+
#
|
19
|
+
# <p>My block HTML</p>
|
20
|
+
#
|
21
|
+
class ValidBlockSchemasCheck < Check
|
22
|
+
def run
|
23
|
+
custom_types = Canvas::FetchCustomTypes.call
|
24
|
+
block_files.each do |filename|
|
25
|
+
file = File.read(filename)
|
26
|
+
front_matter = extract_front_matter(file)
|
27
|
+
validate_format(filename, front_matter) &&
|
28
|
+
validate_schema(filename, front_matter, custom_types)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def block_files
|
35
|
+
Dir.glob("blocks/**/*.{html,liquid}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(filename, front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) &&
|
40
|
+
front_matter.values.all? { |attr| attr.is_a?(Hash) }
|
41
|
+
|
42
|
+
@offenses << Offense.new(
|
43
|
+
message: "Invalid Block Schema: #{filename} - \nSchema is not in a valid format"
|
44
|
+
)
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_schema(filename, front_matter, custom_types)
|
49
|
+
schema = extract_schema(front_matter)
|
50
|
+
validator = Validator::BlockSchema.new(schema: schema, custom_types: custom_types)
|
51
|
+
return if validator.validate
|
52
|
+
|
53
|
+
validator.errors.each do |message|
|
54
|
+
@offenses << Offense.new(
|
55
|
+
message: "Invalid Block Schema: #{filename} - \n#{message}"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_front_matter(file)
|
61
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
62
|
+
front_matter = extractor.front_matter
|
63
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
64
|
+
end
|
65
|
+
|
66
|
+
def extract_schema(front_matter)
|
67
|
+
{
|
68
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each footer template file.
|
7
|
+
#
|
8
|
+
# Example of footer Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# max_item_levels: 2
|
12
|
+
# supports_open_new_tab: true
|
13
|
+
# attributes:
|
14
|
+
# my_title:
|
15
|
+
# type: string
|
16
|
+
# my_color:
|
17
|
+
# type: color
|
18
|
+
# label: My color
|
19
|
+
# hint: "Select your favourite color"
|
20
|
+
# ---
|
21
|
+
#
|
22
|
+
# <p>My footer HTML</p>
|
23
|
+
#
|
24
|
+
class ValidFooterSchemaCheck < Check
|
25
|
+
def run
|
26
|
+
file = File.read(footer_filename)
|
27
|
+
front_matter = extract_front_matter(file)
|
28
|
+
validate_format(front_matter) &&
|
29
|
+
validate_schema(front_matter)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def footer_filename
|
35
|
+
Dir.glob("partials/footer/index.{html,liquid}").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) && attributes_valid_format(front_matter)
|
40
|
+
|
41
|
+
@offenses << Offense.new(
|
42
|
+
message: "Invalid Footer Schema: #{footer_filename} - \nSchema is not in a valid format"
|
43
|
+
)
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes_valid_format(front_matter)
|
48
|
+
return true unless front_matter.key?("attributes")
|
49
|
+
|
50
|
+
front_matter["attributes"].is_a?(Hash) &&
|
51
|
+
front_matter["attributes"].values.all? { |attr| attr.is_a?(Hash) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_schema(front_matter)
|
55
|
+
schema = extract_schema(front_matter)
|
56
|
+
validator = Validator::FooterSchema.new(
|
57
|
+
schema: schema,
|
58
|
+
custom_types: Canvas::FetchCustomTypes.call
|
59
|
+
)
|
60
|
+
return if validator.validate
|
61
|
+
|
62
|
+
validator.errors.each do |message|
|
63
|
+
@offenses << Offense.new(
|
64
|
+
message: "Invalid Footer Schema: #{footer_filename} - \n#{message}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_front_matter(file)
|
70
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
71
|
+
front_matter = extractor.front_matter
|
72
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_schema(front_matter)
|
76
|
+
front_matter.merge(
|
77
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter["attributes"])
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Canvas
|
2
|
+
class ValidJsonCheck < Check
|
3
|
+
def run
|
4
|
+
json_files.each do |filename|
|
5
|
+
file = File.read(filename)
|
6
|
+
validator = Validator::Json.new(file)
|
7
|
+
|
8
|
+
next if validator.validate
|
9
|
+
|
10
|
+
validator.errors.each do |message|
|
11
|
+
@offenses << Offense.new(
|
12
|
+
message: "Invalid JSON: #{filename} - \n#{message}"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def json_files
|
21
|
+
Dir.glob("**/*.json")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each menu template file.
|
7
|
+
#
|
8
|
+
# Example of menu Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# max_item_levels: 2
|
12
|
+
# supports_open_new_tab: true
|
13
|
+
# attributes:
|
14
|
+
# my_title:
|
15
|
+
# type: string
|
16
|
+
# my_color:
|
17
|
+
# type: color
|
18
|
+
# label: My color
|
19
|
+
# hint: "Select your favourite color"
|
20
|
+
# ---
|
21
|
+
#
|
22
|
+
# <p>My menu HTML</p>
|
23
|
+
#
|
24
|
+
class ValidMenuSchemaCheck < Check
|
25
|
+
def run
|
26
|
+
file = File.read(menu_filename)
|
27
|
+
front_matter = extract_front_matter(file)
|
28
|
+
validate_format(front_matter) &&
|
29
|
+
validate_schema(front_matter)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def menu_filename
|
35
|
+
Dir.glob("partials/menu/index.{html,liquid}").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) && attributes_valid_format(front_matter)
|
40
|
+
|
41
|
+
@offenses << Offense.new(
|
42
|
+
message: "Invalid Menu Schema: #{menu_filename} - \nSchema is not in a valid format"
|
43
|
+
)
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes_valid_format(front_matter)
|
48
|
+
return true unless front_matter.key?("attributes")
|
49
|
+
|
50
|
+
front_matter["attributes"].is_a?(Hash) &&
|
51
|
+
front_matter["attributes"].values.all? { |attr| attr.is_a?(Hash) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_schema(front_matter)
|
55
|
+
schema = extract_schema(front_matter)
|
56
|
+
validator = Validator::MenuSchema.new(
|
57
|
+
schema: schema,
|
58
|
+
custom_types: Canvas::FetchCustomTypes.call
|
59
|
+
)
|
60
|
+
return if validator.validate
|
61
|
+
|
62
|
+
validator.errors.each do |message|
|
63
|
+
@offenses << Offense.new(
|
64
|
+
message: "Invalid Menu Schema: #{menu_filename} - \n#{message}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_front_matter(file)
|
70
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
71
|
+
front_matter = extractor.front_matter
|
72
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_schema(front_matter)
|
76
|
+
front_matter.merge(
|
77
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter["attributes"])
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This service will convert the attributes from a hash, as they are defined in the front matter,
|
6
|
+
# into an array of hashes that the {Canvas::Validator::SchemaAttribute} class expects.
|
7
|
+
# e.g. the following front matter:
|
8
|
+
#
|
9
|
+
# my_title:
|
10
|
+
# type: string
|
11
|
+
# my_color:
|
12
|
+
# type: color
|
13
|
+
#
|
14
|
+
# will get converted to:
|
15
|
+
#
|
16
|
+
# [
|
17
|
+
# {
|
18
|
+
# "name" => "my_title",
|
19
|
+
# "type" => "string"
|
20
|
+
# },
|
21
|
+
# {
|
22
|
+
# "name" => "my_color",
|
23
|
+
# "type" => "color"
|
24
|
+
# }
|
25
|
+
# ]
|
26
|
+
#
|
27
|
+
class ExpandAttributes
|
28
|
+
class << self
|
29
|
+
# @param attributes_hash [Hash] hash of attributes pulled from front matter
|
30
|
+
# @return [Array<Hash>] array of hashes that represent each attribute
|
31
|
+
def call(attributes_hash)
|
32
|
+
return [] if attributes_hash.nil?
|
33
|
+
|
34
|
+
attributes_hash.each_with_object([]) do |(name, attribute_hash), attrs|
|
35
|
+
attrs << attribute_hash.merge("name" => name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Canvas
|
6
|
+
# :documented:
|
7
|
+
# This service can be used to fetch the custom types from the /types directory.
|
8
|
+
class FetchCustomTypes
|
9
|
+
class << self
|
10
|
+
# @return [Array<Hash>] a list of all the custom types defined in the
|
11
|
+
# theme within the /types directory.
|
12
|
+
def call
|
13
|
+
filenames = Dir.glob("types/*.json")
|
14
|
+
filenames.map { |filename| extract_json(filename) }.compact
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def extract_json(filename)
|
20
|
+
file = File.read(filename)
|
21
|
+
JSON.parse(file)
|
22
|
+
rescue JSON::ParserError
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
require "liquid"
|
5
|
+
|
6
|
+
module Canvas
|
7
|
+
module Validator
|
8
|
+
# :documented:
|
9
|
+
# This class is used to validate a schema for a block.
|
10
|
+
# Example of a valid block schema:
|
11
|
+
# {
|
12
|
+
# "attributes" => [
|
13
|
+
# {
|
14
|
+
# "name" => "my_title",
|
15
|
+
# "type" => "string"
|
16
|
+
# },
|
17
|
+
# {
|
18
|
+
# "name" => "my_color",
|
19
|
+
# "type" => "color",
|
20
|
+
# "label" => "My color",
|
21
|
+
# "hint" => "Select your favourite color"
|
22
|
+
# }
|
23
|
+
# ]
|
24
|
+
# }
|
25
|
+
class BlockSchema
|
26
|
+
PERMITTED_KEYS = %w[attributes].freeze
|
27
|
+
|
28
|
+
attr_reader :errors, :schema
|
29
|
+
|
30
|
+
# @param schema [Hash] the schema to be validated
|
31
|
+
# @param custom_types [Array<Hash>] a list of custom types
|
32
|
+
def initialize(schema:, custom_types: [])
|
33
|
+
@schema = schema
|
34
|
+
@custom_types = custom_types
|
35
|
+
@errors = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate
|
39
|
+
if ensure_valid_format
|
40
|
+
ensure_no_unrecognized_keys
|
41
|
+
ensure_attributes_are_valid
|
42
|
+
end
|
43
|
+
|
44
|
+
@errors.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def ensure_valid_format
|
50
|
+
return true if schema.is_a?(Hash) &&
|
51
|
+
(schema["attributes"].nil? || attributes_array_of_hashes?(schema))
|
52
|
+
|
53
|
+
@errors << "Schema is not in a valid format"
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_no_unrecognized_keys
|
58
|
+
unrecognized_keys = schema.keys - PERMITTED_KEYS
|
59
|
+
return true if unrecognized_keys.empty?
|
60
|
+
|
61
|
+
@errors << "Unrecognized keys: #{unrecognized_keys.join(', ')}"
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def ensure_attributes_are_valid
|
66
|
+
return true unless schema["attributes"]
|
67
|
+
|
68
|
+
schema["attributes"].each do |attribute_schema|
|
69
|
+
attr_validator = Validator::SchemaAttribute.new(
|
70
|
+
attribute: attribute_schema,
|
71
|
+
custom_types: @custom_types
|
72
|
+
)
|
73
|
+
next if attr_validator.validate
|
74
|
+
|
75
|
+
@errors << "Attribute \"#{attribute_schema['name']}\" is invalid "\
|
76
|
+
"- #{attr_validator.errors.join(', ')}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def attributes_array_of_hashes?(schema)
|
81
|
+
schema["attributes"].is_a?(Array) &&
|
82
|
+
schema["attributes"].all? { |attr| attr.is_a?(Hash) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# We need to require this file t ensure it is loaded first
|
4
|
+
require_relative "menu_schema"
|
5
|
+
|
6
|
+
module Canvas
|
7
|
+
module Validator
|
8
|
+
# :documented:
|
9
|
+
# This class is used to validate a schema for a footer. For now the logic is exactly the
|
10
|
+
# same as the menu schema.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# {
|
14
|
+
# "max_item_levels": 2,
|
15
|
+
# "supports_open_new_tab": "true",
|
16
|
+
# "attributes": {
|
17
|
+
# "fixed": {
|
18
|
+
# "group": "design",
|
19
|
+
# "label": "Fixed when scrolling",
|
20
|
+
# "hint": "The menu will stay fixed to the top when scrolling down the page.",
|
21
|
+
# "type": "boolean",
|
22
|
+
# "default: "false"
|
23
|
+
# }
|
24
|
+
# }
|
25
|
+
# }
|
26
|
+
#
|
27
|
+
class FooterSchema < MenuSchema; end
|
28
|
+
end
|
29
|
+
end
|
@@ -4,8 +4,8 @@ require "liquid"
|
|
4
4
|
module Canvas
|
5
5
|
module Validator
|
6
6
|
class Html
|
7
|
-
LIQUID_TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
|
8
|
-
LIQUID_VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
|
7
|
+
LIQUID_TAG = /#{::Liquid::TagStart}.*?#{::Liquid::TagEnd}/om
|
8
|
+
LIQUID_VARIABLE = /#{::Liquid::VariableStart}.*?#{::Liquid::VariableEnd}/om
|
9
9
|
LIQUID_TAG_OR_VARIABLE = /#{LIQUID_TAG}|#{LIQUID_VARIABLE}/om
|
10
10
|
|
11
11
|
attr_reader :errors
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class Json
|
6
|
+
attr_reader :errors
|
7
|
+
|
8
|
+
def initialize(file)
|
9
|
+
@file = file
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate
|
13
|
+
::JSON.parse(@file)
|
14
|
+
true
|
15
|
+
rescue ::JSON::ParserError => e
|
16
|
+
@errors = [e.message]
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
# :documented:
|
6
|
+
# This class is used to validate a schema for a menu.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# {
|
10
|
+
# "max_item_levels": 2,
|
11
|
+
# "supports_open_new_tab": "true",
|
12
|
+
# "attributes": {
|
13
|
+
# "fixed": {
|
14
|
+
# "group": "design",
|
15
|
+
# "label": "Fixed when scrolling",
|
16
|
+
# "hint": "The menu will stay fixed to the top when scrolling down the page.",
|
17
|
+
# "type": "boolean",
|
18
|
+
# "default: "false"
|
19
|
+
# }
|
20
|
+
# }
|
21
|
+
# }
|
22
|
+
#
|
23
|
+
class MenuSchema
|
24
|
+
PERMITTED_KEYS = %w[max_item_levels supports_open_new_tab attributes].freeze
|
25
|
+
ADDITIONAL_RESERVED_NAMES = %w[items type].freeze
|
26
|
+
|
27
|
+
attr_reader :schema, :errors
|
28
|
+
|
29
|
+
# @param schema [Hash] the schema to be validated
|
30
|
+
# @param custom_types [Array<Hash>] a list of custom types
|
31
|
+
def initialize(schema:, custom_types: [])
|
32
|
+
@schema = schema
|
33
|
+
@custom_types = custom_types
|
34
|
+
@errors = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate
|
38
|
+
if ensure_valid_format
|
39
|
+
ensure_no_unrecognized_keys
|
40
|
+
ensure_max_item_levels_is_valid
|
41
|
+
ensure_attributes_are_valid
|
42
|
+
end
|
43
|
+
|
44
|
+
@errors.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def ensure_valid_format
|
50
|
+
return true if schema.is_a?(Hash) &&
|
51
|
+
(schema["attributes"].nil? || attributes_array_of_hashes?(schema))
|
52
|
+
|
53
|
+
@errors << "Schema is not in a valid format"
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_no_unrecognized_keys
|
58
|
+
unrecognized_keys = schema.keys - PERMITTED_KEYS
|
59
|
+
return true if unrecognized_keys.empty?
|
60
|
+
|
61
|
+
@errors << "Unrecognized keys: #{unrecognized_keys.join(', ')}"
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def ensure_max_item_levels_is_valid
|
66
|
+
return true unless schema.key?("max_item_levels")
|
67
|
+
return true if [1, 2, 3].include?(schema["max_item_levels"].to_i)
|
68
|
+
|
69
|
+
@errors << "\"max_item_levels\" must be a number between 1 and 3"
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def ensure_attributes_are_valid
|
74
|
+
return true unless schema["attributes"]
|
75
|
+
|
76
|
+
schema["attributes"].each do |attribute_schema|
|
77
|
+
attr_validator = Validator::SchemaAttribute.new(
|
78
|
+
attribute: attribute_schema,
|
79
|
+
custom_types: @custom_types,
|
80
|
+
additional_reserved_names: ADDITIONAL_RESERVED_NAMES
|
81
|
+
)
|
82
|
+
next if attr_validator.validate
|
83
|
+
|
84
|
+
@errors << "Attribute \"#{attribute_schema['name']}\" is invalid "\
|
85
|
+
"- #{attr_validator.errors.join(', ')}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def attributes_array_of_hashes?(schema)
|
90
|
+
schema["attributes"].is_a?(Array) &&
|
91
|
+
schema["attributes"].all? { |attr| attr.is_a?(Hash) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|