json-spec 0.1.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 +7 -0
- data/.gitignore +3 -0
- data/.gitmodules +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +960 -0
- data/Rakefile +10 -0
- data/api_documentation.md +611 -0
- data/cachivache/.gitignore +1 -0
- data/cachivache/Gemfile +4 -0
- data/cachivache/README.md +247 -0
- data/cachivache/Rakefile +19 -0
- data/cachivache/Vagrantfile +70 -0
- data/cachivache/cachivache.rb +59 -0
- data/cachivache/lib/let-behaviour.rb +27 -0
- data/cachivache/lib/rake-helper.rb +131 -0
- data/cachivache/lib/sh-file-context.rb +39 -0
- data/cachivache/lib/sh-if-context.rb +31 -0
- data/cachivache/stuff/.gitkeep +0 -0
- data/cachivache/stuff/ruby-json-spec.rb +22 -0
- data/examples/example-1-simple.rb +66 -0
- data/examples/example-2-default-expectations.rb +63 -0
- data/examples/example-3-each-field.rb +104 -0
- data/examples/example-4-to-be-as-defined-in.rb +117 -0
- data/examples/example-5-custom-expectations.rb +153 -0
- data/examples/example-6-custom-messages.rb +36 -0
- data/examples/example-7-full-example.rb +231 -0
- data/examples/fixtures.rb +77 -0
- data/examples/validation-printer.rb +47 -0
- data/json-spec.gemspec +29 -0
- data/lib/cabeza-de-termo/json-spec/errors/error.rb +6 -0
- data/lib/cabeza-de-termo/json-spec/errors/expectation-not-found-error.rb +8 -0
- data/lib/cabeza-de-termo/json-spec/errors/modifier-not-found-error.rb +8 -0
- data/lib/cabeza-de-termo/json-spec/errors/unkown-json-type-error.rb +8 -0
- data/lib/cabeza-de-termo/json-spec/errors/validation-error.rb +8 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/default-expectations/default-expectation-builder.rb +29 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/default-expectations/default-expectations-mapping.rb +54 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/block-expectation-definition.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/class-expectation-definition.rb +15 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expectation-definition.rb +19 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expectations-definition-builder.rb +136 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expecting-all-of-expectation-definition.rb +23 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expecting-any-of-expectation-definition.rb +23 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expecting-expectation-definition.rb +17 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/negating-expectation-definition.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-library-definition-builder.rb +35 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/modifier-builders/class-modifier-definition.rb +15 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/modifier-builders/composing-modifiers-definition.rb +28 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/modifier-builders/modifier-definition.rb +13 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/modifier-builders/modifiers-definition-builder.rb +73 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/expectations-library.rb +150 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/initializers/default-library-initializer.rb +265 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/initializers/library-initializer.rb +9 -0
- data/lib/cabeza-de-termo/json-spec/expectations-library/messages/expectation-messages-mapping.rb +27 -0
- data/lib/cabeza-de-termo/json-spec/expectations/abstract-expectation.rb +39 -0
- data/lib/cabeza-de-termo/json-spec/expectations/all-expectations-composite.rb +33 -0
- data/lib/cabeza-de-termo/json-spec/expectations/any-expectation-composite.rb +33 -0
- data/lib/cabeza-de-termo/json-spec/expectations/block-expectation.rb +28 -0
- data/lib/cabeza-de-termo/json-spec/expectations/expectation.rb +51 -0
- data/lib/cabeza-de-termo/json-spec/expectations/is-email-expectation.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/expectations/is-scalar-expectation.rb +17 -0
- data/lib/cabeza-de-termo/json-spec/expectations/is-url-expectation.rb +21 -0
- data/lib/cabeza-de-termo/json-spec/expectations/negated-expectation.rb +31 -0
- data/lib/cabeza-de-termo/json-spec/expectations/runner/abstract-expectations-runner.rb +27 -0
- data/lib/cabeza-de-termo/json-spec/expectations/runner/can-be-absent-expectations-runner.rb +50 -0
- data/lib/cabeza-de-termo/json-spec/expectations/runner/can-be-null-expectations-runner.rb +48 -0
- data/lib/cabeza-de-termo/json-spec/expectations/runner/expectations-runner.rb +43 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-any-of.rb +62 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-anything.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-each-field.rb +58 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-each.rb +58 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-expression.rb +314 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-field-name.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-field.rb +76 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-list.rb +40 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-object.rb +82 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-scalar.rb +20 -0
- data/lib/cabeza-de-termo/json-spec/expressions/json-spec.rb +174 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/abstract-instantiator.rb +9 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/all-expectations-composite-instantiator.rb +12 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/any-expectation-composite-instantiator.rb +12 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/block-expectation-instantiator.rb +16 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/composite-instantiator.rb +45 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/modifier-composite-instantiator.rb +12 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/negated-expectation-instantiator.rb +20 -0
- data/lib/cabeza-de-termo/json-spec/instantiators/patial-application-instantiator.rb +26 -0
- data/lib/cabeza-de-termo/json-spec/json-spec.rb +2 -0
- data/lib/cabeza-de-termo/json-spec/message-formatters/block-message-formatter.rb +15 -0
- data/lib/cabeza-de-termo/json-spec/message-formatters/erb-message-formatter.rb +60 -0
- data/lib/cabeza-de-termo/json-spec/message-formatters/message-formatter.rb +9 -0
- data/lib/cabeza-de-termo/json-spec/metaprogramming/message-send.rb +37 -0
- data/lib/cabeza-de-termo/json-spec/metaprogramming/message.rb +37 -0
- data/lib/cabeza-de-termo/json-spec/metaprogramming/object-method.rb +14 -0
- data/lib/cabeza-de-termo/json-spec/modifiers/can-be-absent-modifier.rb +13 -0
- data/lib/cabeza-de-termo/json-spec/modifiers/can-be-null-modifier.rb +13 -0
- data/lib/cabeza-de-termo/json-spec/modifiers/expression-modifier.rb +9 -0
- data/lib/cabeza-de-termo/json-spec/modifiers/modifier-composite.rb +27 -0
- data/lib/cabeza-de-termo/json-spec/signals/signal.rb +6 -0
- data/lib/cabeza-de-termo/json-spec/signals/skip-branch-signal.rb +8 -0
- data/lib/cabeza-de-termo/json-spec/utilities/bind.rb +20 -0
- data/lib/cabeza-de-termo/json-spec/utilities/range.rb +70 -0
- data/lib/cabeza-de-termo/json-spec/value-holders/accessors-chain.rb +27 -0
- data/lib/cabeza-de-termo/json-spec/value-holders/missing-value.rb +21 -0
- data/lib/cabeza-de-termo/json-spec/value-holders/value-holder.rb +135 -0
- data/lib/cabeza-de-termo/json-spec/version.rb +5 -0
- data/lib/cabeza-de-termo/json-spec/walkers/expression-walker.rb +66 -0
- data/lib/cabeza-de-termo/json-spec/walkers/json-expectations-runner.rb +214 -0
- data/lib/cabeza-de-termo/json-spec/walkers/json-expression-explainer.rb +183 -0
- data/lib/cabeza-de-termo/json-spec/walkers/reporter/expectation-report.rb +63 -0
- data/lib/cabeza-de-termo/json-spec/walkers/reporter/json-expectations-reporter.rb +111 -0
- data/lib/cabeza-de-termo/json-spec/walkers/validator/json-validator-error.rb +29 -0
- data/lib/cabeza-de-termo/json-spec/walkers/validator/json-validator.rb +133 -0
- data/lib/cabeza-de-termo/json-spec/walkers/value-holders-stack-behaviour.rb +57 -0
- metadata +242 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module CabezaDeTermo
|
|
2
|
+
module JsonSpec
|
|
3
|
+
#
|
|
4
|
+
# Models the chain of accessors to get to a value in a json tree.
|
|
5
|
+
# For instance, something like
|
|
6
|
+
#
|
|
7
|
+
# ['users', '[3]', 'addresses', '[0]', 'street']
|
|
8
|
+
#
|
|
9
|
+
class AccessorsChain
|
|
10
|
+
def initialize(accessors = ['@'])
|
|
11
|
+
@accessors = accessors
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def accessors()
|
|
15
|
+
@accessors
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def append_accessor(accessor)
|
|
19
|
+
self.class.new( accessors + [accessor] )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_s()
|
|
23
|
+
accessors.join('.')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module CabezaDeTermo
|
|
2
|
+
module JsonSpec
|
|
3
|
+
#
|
|
4
|
+
# This object only purpose is to model a missing value from a json field or index.
|
|
5
|
+
# For instance, if we have the following Json:
|
|
6
|
+
#
|
|
7
|
+
# <code>
|
|
8
|
+
# {
|
|
9
|
+
# "id": 123,
|
|
10
|
+
# }
|
|
11
|
+
# </code>
|
|
12
|
+
#
|
|
13
|
+
# and we ask for the field 'name', the answer will be a ValueHolder holding a MissingValue.
|
|
14
|
+
#
|
|
15
|
+
class MissingValue
|
|
16
|
+
def to_s()
|
|
17
|
+
'Missing value'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require_relative "missing-value"
|
|
2
|
+
require_relative "accessors-chain"
|
|
3
|
+
require "cabeza-de-termo/json-spec/errors/unkown-json-type-error"
|
|
4
|
+
|
|
5
|
+
module CabezaDeTermo
|
|
6
|
+
module JsonSpec
|
|
7
|
+
#
|
|
8
|
+
# The value holder is passed though the ExpressionWalker during the processing of
|
|
9
|
+
# a json object.
|
|
10
|
+
#
|
|
11
|
+
class ValueHolder
|
|
12
|
+
#
|
|
13
|
+
# Answer a new ValueHolder on a MissingValue.
|
|
14
|
+
#
|
|
15
|
+
def self.new_value_holder_with_missing_value(accessors_chain = nil)
|
|
16
|
+
self.new(MissingValue.new, accessors_chain)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize (value, accessors_chain = nil)
|
|
20
|
+
@value = value
|
|
21
|
+
@accessors_chain = accessors_chain.nil? ? AccessorsChain.new : accessors_chain
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Accessing
|
|
25
|
+
|
|
26
|
+
def value()
|
|
27
|
+
@value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accessors_chain()
|
|
31
|
+
@accessors_chain
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def value_holder_on_field_named(name)
|
|
35
|
+
return new_value_holder(value[name], name) if can_access_field_named?(name)
|
|
36
|
+
new_missing_value_holder(name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def value_holder_at_index(i)
|
|
40
|
+
name = '[' + i.to_s + ']'
|
|
41
|
+
|
|
42
|
+
return new_value_holder(value[i], name) if can_access_field_at_index?(i)
|
|
43
|
+
new_missing_value_holder(name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def field_names()
|
|
47
|
+
return value.keys if is_object?
|
|
48
|
+
[]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# Answer the length of the value.
|
|
53
|
+
# For {} is the number of fields.
|
|
54
|
+
# For [] the number of elements.
|
|
55
|
+
# For strings its length.
|
|
56
|
+
# And for any other type, it throws an UnkownJsonTypeError.
|
|
57
|
+
#
|
|
58
|
+
def length()
|
|
59
|
+
return value.length if is_object?
|
|
60
|
+
return value.length if is_list?
|
|
61
|
+
return value.length if is_string?
|
|
62
|
+
return 0 if is_missing_value?
|
|
63
|
+
|
|
64
|
+
raise UnkownJsonTypeError.new('Can not get the length of an unknown type')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Asking
|
|
68
|
+
|
|
69
|
+
def is_missing_value?()
|
|
70
|
+
value.kind_of?(MissingValue)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def not_missing_value?()
|
|
74
|
+
!is_missing_value?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#
|
|
78
|
+
# Answer whether the value is empty or not.
|
|
79
|
+
# For {} to be empty means not to have any field.
|
|
80
|
+
# For [], not to have any element.
|
|
81
|
+
# For strings, to be ''.
|
|
82
|
+
# And for any other type, it throws an UnkownJsonTypeError.
|
|
83
|
+
#
|
|
84
|
+
def is_empty?()
|
|
85
|
+
length == 0
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Instance creation
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Answer a new ValueHolder on the value and with the name added to its accessors_chain.
|
|
92
|
+
#
|
|
93
|
+
def new_value_holder(value, accessor)
|
|
94
|
+
new_nccessors_chain = accessors_chain.append_accessor(accessor)
|
|
95
|
+
ValueHolder.new(value, new_nccessors_chain)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#
|
|
99
|
+
# Answer a new ValueHolder on a MissingValue and with the name added to its accessors_chain.
|
|
100
|
+
#
|
|
101
|
+
def new_missing_value_holder(accessor)
|
|
102
|
+
new_nccessors_chain = accessors_chain.append_accessor(accessor)
|
|
103
|
+
self.class.new_value_holder_with_missing_value(new_nccessors_chain)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
protected
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
# Answer true if the value is a stdClass and has a property named name.
|
|
110
|
+
#
|
|
111
|
+
def can_access_field_named?(name)
|
|
112
|
+
is_object? && value.has_key?(name)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#
|
|
116
|
+
# Answer true if the value is an array and has an index i defined.
|
|
117
|
+
#
|
|
118
|
+
def can_access_field_at_index?(i)
|
|
119
|
+
is_list? && i.between?(0, length - 1)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def is_object?()
|
|
123
|
+
value.kind_of?(::Hash)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def is_list?()
|
|
127
|
+
value.kind_of?(::Array)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def is_string?()
|
|
131
|
+
value.kind_of?(::String)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module CabezaDeTermo
|
|
2
|
+
module JsonSpec
|
|
3
|
+
class ExpressionWalker
|
|
4
|
+
def walk_on(json_walkable)
|
|
5
|
+
json_walkable.accept_walker(self)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def walk_json_spec(json_spec)
|
|
9
|
+
raise 'Subclass responsibility'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def walk_json_object(json_object)
|
|
13
|
+
raise 'Subclass responsibility'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def walk_json_each_field(json_each_field)
|
|
17
|
+
raise 'Subclass responsibility'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def walk_json_field_name(json_field_name)
|
|
21
|
+
raise 'Subclass responsibility'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def walk_json_list(json_list)
|
|
25
|
+
raise 'Subclass responsibility'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def walk_json_each(json_each)
|
|
29
|
+
raise 'Subclass responsibility'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def walk_json_field(json_field)
|
|
33
|
+
raise 'Subclass responsibility'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def walk_json_scalar(json_scalar)
|
|
37
|
+
raise 'Subclass responsibility'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def walk_json_any_of(json_any_of)
|
|
41
|
+
raise 'Subclass responsibility'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def walk_json_anything(json_anything)
|
|
45
|
+
raise 'Subclass responsibility'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def walk_expectation_runner(expectation_runner)
|
|
49
|
+
raise 'Subclass responsibility'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def walk_expectation(expectation)
|
|
53
|
+
raise 'Subclass responsibility'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Error handling
|
|
57
|
+
def supressing(error_class, &block)
|
|
58
|
+
begin
|
|
59
|
+
block.call
|
|
60
|
+
rescue error_class
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require_relative "expression-walker"
|
|
2
|
+
require_relative "value-holders-stack-behaviour"
|
|
3
|
+
require "cabeza-de-termo/json-spec/errors/validation-error"
|
|
4
|
+
require 'cabeza-de-termo/json-spec/signals/skip-branch-signal'
|
|
5
|
+
|
|
6
|
+
module CabezaDeTermo
|
|
7
|
+
module JsonSpec
|
|
8
|
+
class JsonExpectationsRunner < ExpressionWalker
|
|
9
|
+
include ValueHoldersStackBehaviour
|
|
10
|
+
|
|
11
|
+
# Asking
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Answer true if all the walked Expectations was satisfied.
|
|
15
|
+
#
|
|
16
|
+
# @return boolean
|
|
17
|
+
#
|
|
18
|
+
def no_errors?()
|
|
19
|
+
raise 'Subclass responsibility'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def has_errors?()
|
|
23
|
+
!no_errors?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Hooks
|
|
28
|
+
def on_unwalked_field(field_name, value_holder)
|
|
29
|
+
raise 'Subclass responsibility'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_json_any_of_was_satisfied_with_runner(expectations_runner)
|
|
33
|
+
raise 'Subclass responsibility'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def on_json_any_of_failed_with_all_runners(expectations_runners)
|
|
37
|
+
raise 'Subclass responsibility'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_walked_expectation(expectation, value_holder, was_satisfied)
|
|
41
|
+
raise 'Subclass responsibility'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Instance creation
|
|
45
|
+
|
|
46
|
+
def new_json_expectations_runner()
|
|
47
|
+
raise 'Subclass responsibility'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Walking expressions
|
|
51
|
+
|
|
52
|
+
def in_isolation(&block)
|
|
53
|
+
supressing ValidationError, &block
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def in_isolation_walk_on(json_walkable)
|
|
57
|
+
in_isolation do
|
|
58
|
+
walk_on(json_walkable)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def walk_on(json_walkable)
|
|
63
|
+
supressing SkipBranchSignal do
|
|
64
|
+
super(json_walkable)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def walk_json_spec(json_spec)
|
|
69
|
+
in_isolation_walk_on(json_spec.root_expression)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def walk_json_object(json_object)
|
|
73
|
+
walk_expectations_of(json_object)
|
|
74
|
+
|
|
75
|
+
walked_fields = walk_each_defined_field_of(json_object)
|
|
76
|
+
|
|
77
|
+
if( json_object.has_each_field? )
|
|
78
|
+
more_walked_fields = walk_json_each_field(json_object.each_field_expression)
|
|
79
|
+
walked_fields = walked_fields + more_walked_fields
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
unexpected_fields_from_current_value_holder(walked_fields).each do |field_name|
|
|
83
|
+
on_unwalked_field(field_name, @value_holder)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def walk_each_defined_field_of(json_object)
|
|
88
|
+
walked_fields = []
|
|
89
|
+
|
|
90
|
+
json_object.fields.each do |field|
|
|
91
|
+
with_value_holder( @value_holder.value_holder_on_field_named(field.name) ) do
|
|
92
|
+
in_isolation_walk_on(field)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
walked_fields << field.name
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
walked_fields
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def walk_each_field_name(json_each_field, field_name)
|
|
102
|
+
with_value_holder( @value_holder.new_value_holder(field_name, ':' + field_name) ) do
|
|
103
|
+
walk_on(json_each_field.name_expression)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def walk_each_field_value(json_each_field, field_name)
|
|
108
|
+
with_value_holder( @value_holder.value_holder_on_field_named(field_name) ) do
|
|
109
|
+
in_isolation_walk_on(json_each_field.value_expression)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def walk_json_each_field(json_each_field)
|
|
114
|
+
walked_fields = []
|
|
115
|
+
|
|
116
|
+
@value_holder.field_names.each do |field_name|
|
|
117
|
+
walked_fields << field_name
|
|
118
|
+
in_isolation_walk_each_field(json_each_field, field_name)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
walked_fields
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def in_isolation_walk_each_field(json_each_field, field_name)
|
|
125
|
+
in_isolation do
|
|
126
|
+
walk_each_field_name(json_each_field, field_name)
|
|
127
|
+
walk_each_field_value(json_each_field, field_name)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def walk_json_field_name(json_field_name)
|
|
132
|
+
walk_expectations_of(json_field_name)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def walk_json_list(json_list_expression)
|
|
136
|
+
walk_expectations_of(json_list_expression)
|
|
137
|
+
|
|
138
|
+
walk_on(json_list_expression.each_expression)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def walk_json_each(json_each)
|
|
142
|
+
walk_expectations_of(json_each)
|
|
143
|
+
|
|
144
|
+
return if json_each.no_each_item_expression?
|
|
145
|
+
|
|
146
|
+
(0...@value_holder.length).each do |i|
|
|
147
|
+
with_value_holder( @value_holder.value_holder_at_index(i) ) do
|
|
148
|
+
in_isolation_walk_on(json_each.each_item_expression)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def walk_json_field(json_field)
|
|
154
|
+
walk_on(json_field.value_expression)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def walk_json_anything(json_anything)
|
|
158
|
+
walk_expectations_of(json_anything)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def walk_json_scalar(json_scalar)
|
|
162
|
+
walk_expectations_of(json_scalar)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def walk_json_any_of(any_of)
|
|
166
|
+
sub_walkers = []
|
|
167
|
+
any_of.possible_expressions.each do |possible_expression|
|
|
168
|
+
sub_walkers << walk_with_new_walker(possible_expression)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
sub_walkers.each do |each_sub_walker|
|
|
172
|
+
return on_json_any_of_was_satisfied_with_runner(each_sub_walker) if each_sub_walker.no_errors?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
on_json_any_of_failed_with_all_runners(sub_walkers)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def walk_expectations_of(expression)
|
|
179
|
+
# Intentionaly dont go through :walk_on to let the SkipBranchException errors bubble up
|
|
180
|
+
walk_expectation_runner( expression.expectations_runner )
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def walk_expectation_runner(expectation_runner)
|
|
184
|
+
expectation_runner.accept_walker_with_value_holder(self, @value_holder)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def walk_expectation(expectation)
|
|
188
|
+
was_satisfied = expectation.is_satisfied_by?(@value_holder)
|
|
189
|
+
on_walked_expectation(expectation, @value_holder, was_satisfied)
|
|
190
|
+
|
|
191
|
+
raise_validation_error(expectation) unless was_satisfied
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Helpers
|
|
195
|
+
|
|
196
|
+
def walk_with_new_walker(expression)
|
|
197
|
+
expectations_runner = new_json_expectations_runner
|
|
198
|
+
in_isolation do
|
|
199
|
+
expectations_runner.walk_with_value_holder(expression, @value_holder)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
expectations_runner
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def raise_validation_error(expectation)
|
|
206
|
+
raise ValidationError.new( expectation.failed_message_on(@value_holder) )
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def unexpected_fields_from_current_value_holder(walked_fields)
|
|
210
|
+
@value_holder.field_names.reject { |field| walked_fields.include?(field) }
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|