rspec-json_matchers 0.1.0.alpha.1 → 0.1.0.alpha.2

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.
@@ -14,7 +14,7 @@ module RSpec
14
14
  end
15
15
 
16
16
  def matched?
17
- !!@matched
17
+ @matched
18
18
  end
19
19
  end
20
20
  end
@@ -12,15 +12,15 @@ module RSpec
12
12
 
13
13
  # @note with side effect on `#reasons`
14
14
  def has_matched_keys?
15
- actual_keys = Utils::CollectionKeysExtractor.extract(actual)
16
- expected_keys = Utils::CollectionKeysExtractor.extract(expected)
17
15
  (actual_keys == expected_keys).tap do |success|
18
- unless success
19
- diff_keys = (actual_keys - expected_keys) + (expected_keys - actual_keys)
20
- reasons.push(diff_keys.awesome_inspect)
21
- end
16
+ reasons.push(diff_keys.awesome_inspect) unless success
22
17
  end
23
18
  end
19
+
20
+ def diff_keys
21
+ (actual_keys - expected_keys) +
22
+ (expected_keys - actual_keys)
23
+ end
24
24
  end
25
25
  end
26
26
  end
@@ -12,15 +12,14 @@ module RSpec
12
12
 
13
13
  # @note with side effect on `#reasons`
14
14
  def has_matched_keys?
15
- actual_keys = Utils::CollectionKeysExtractor.extract(actual)
16
- expected_keys = Utils::CollectionKeysExtractor.extract(expected)
17
15
  (expected_keys.subset?(actual_keys)).tap do |success|
18
- unless success
19
- diff_keys = (expected_keys - actual_keys)
20
- reasons.push(diff_keys.awesome_inspect)
21
- end
16
+ reasons.push(diff_keys.awesome_inspect) unless success
22
17
  end
23
18
  end
19
+
20
+ def diff_keys
21
+ expected_keys - actual_keys
22
+ end
24
23
  end
25
24
  end
26
25
  end
@@ -3,7 +3,8 @@ require "abstract_class"
3
3
  module RSpec
4
4
  module JsonMatchers
5
5
  # Represents an expectation of an object (usually called `expected`)
6
- # Built to avoid {Object#===} usage like other matcher gems, like `rspec-json_matcher`
6
+ # Built to avoid {Object#===} usage like other matcher gems,
7
+ # like `rspec-json_matcher`
7
8
  # Actually `rspec-mocks` `3.x` also uses it, but only internally
8
9
  #
9
10
  # @api
@@ -11,18 +12,24 @@ module RSpec
11
12
  # But only used for this gem
12
13
  # @abstract
13
14
  # This class MUST be used after being inherited
14
- # Subclasses MUST override {#expect?} to allow this gem to determine the test result
15
+ # Subclasses MUST override {#expect?}
16
+ # to allow this gem to determine the test result
15
17
  class Expectation
16
18
  extend AbstractClass
17
19
 
20
+ # Determine the value passed in
21
+ # is "expected" by self or not
22
+ # And return the result
23
+ #
18
24
  # @abstract
19
- # This method MUST be overridden to allow this gem to determine the test result
25
+ # This method MUST be overridden
26
+ # to allow this gem to determine the test result
20
27
  #
21
- # @param value [Object] actual value to be evaluated
28
+ # @param _value [Object] actual value to be evaluated
22
29
  #
23
30
  # @return [Bool] Whether the `value` is expected
24
- def expect?(value)
25
- raise NotImplementedError
31
+ def expect?(_value)
32
+ fail NotImplementedError
26
33
  end
27
34
 
28
35
  class << self
@@ -35,30 +42,7 @@ module RSpec
35
42
  #
36
43
  # @return [Expectation]
37
44
  def build(value)
38
- return value if value.is_a?(self)
39
-
40
- if value.is_a?(Regexp)
41
- return Expectations::Private::MatchingRegexp[value]
42
- end
43
-
44
- if value.is_a?(Range)
45
- return Expectations::Private::InRange[value]
46
- end
47
-
48
- if value.respond_to?(:call)
49
- return Expectations::Private::SatisfyingCallable[value]
50
- end
51
-
52
- if value.is_a?(Class)
53
- # Subclass
54
- # See http://ruby-doc.org/core-2.2.2/Module.html#method-i-3C
55
- if value < Expectations::Core::SingletonExpectation
56
- return value::INSTANCE
57
- end
58
- return Expectations::Private::KindOf[value]
59
- end
60
-
61
- Expectations::Private::Eq[value]
45
+ Builder.new(value).build
62
46
  end
63
47
  # @api private
64
48
  #
@@ -67,7 +51,73 @@ module RSpec
67
51
  # @return [Array<Expectation>]
68
52
  # @see .build
69
53
  def build_many(values)
70
- values.flat_map{|v| build(v) }
54
+ values.flat_map { |v| build(v) }
55
+ end
56
+ end
57
+
58
+ # @api private
59
+ #
60
+ # Represents a builder that
61
+ # returns a {Expectation} object from an input object
62
+ class Builder
63
+ # Creates a bullder with an object that
64
+ # might or might not be a {Expectation} object
65
+ #
66
+ # @param object [Object]
67
+ # any object that should be "built"
68
+ # into a {Expectation} object
69
+ def initialize(object)
70
+ @object = object
71
+ end
72
+
73
+ # Create and return a {Expectation} object
74
+ # according to one of the following
75
+ # - the class of object
76
+ # - does the object respond to `#call`
77
+ # - the ancestors of the object (when it's a class)
78
+ #
79
+ # @return [Expectation] a {Expectation} object
80
+ def build
81
+ return object if object.is_a?(Expectation)
82
+ return expectation_by_class unless expectation_by_class.nil?
83
+ return expectation_for_call if object.respond_to?(:call)
84
+ return expectation_by_ancestors if object.is_a?(Class)
85
+
86
+ Expectations::Private::Eq[object]
87
+ end
88
+
89
+ private
90
+
91
+ OBJECT_CLASS_TO_EXPECTATION_HASH = {
92
+ Regexp => -> (obj) { Expectations::Private::MatchingRegexp[obj] },
93
+ Range => -> (obj) { Expectations::Private::InRange[obj] },
94
+ }.freeze
95
+ private_constant :OBJECT_CLASS_TO_EXPECTATION_HASH
96
+
97
+ attr_reader(*[:object])
98
+
99
+ def expectation_by_class
100
+ if instance_variable_defined?(:@expectation_by_object_class)
101
+ return @expectation_by_object_class
102
+ end
103
+
104
+ proc = OBJECT_CLASS_TO_EXPECTATION_HASH[object.class]
105
+ return nil if proc.nil?
106
+
107
+ proc.call(object)
108
+ end
109
+
110
+ def expectation_by_ancestors
111
+ # Subclass
112
+ # See http://ruby-doc.org/core-2.2.2/Module.html#method-i-3C
113
+ if object < Expectations::Core::SingletonExpectation
114
+ return object::INSTANCE
115
+ end
116
+ Expectations::Private::KindOf[object]
117
+ end
118
+
119
+ def expectation_for_call
120
+ Expectations::Private::SatisfyingCallable[object]
71
121
  end
72
122
  end
73
123
  end
@@ -6,12 +6,14 @@ module RSpec
6
6
  module JsonMatchers
7
7
  module Expectations
8
8
  # @api private
9
- # All classes within module should NOT be able to be used directly / extended
9
+ # All classes within module
10
+ # should NOT be able to be used directly / extended
10
11
  #
11
12
  # Classes in this namespace are depending on {Core}
12
13
  # and depended by some classes in {Expectations::Mixins::BuiltIn}
13
14
  # They are all abstract too, thus the naming, but might change
14
- # This namespace is created is to avoid require order problem when putting classes here in {Private}
15
+ # This namespace is created is to avoid require order problem
16
+ # when putting classes here in {Private}
15
17
  module Abstract
16
18
  # @abstract
17
19
  # Only for reducing code duplication
@@ -29,7 +31,6 @@ module RSpec
29
31
  value.is_a?(Numeric)
30
32
  end
31
33
  end
32
-
33
34
  end
34
35
  end
35
36
  end
@@ -14,9 +14,11 @@ module RSpec
14
14
  module Core
15
15
  # @abstract
16
16
  # This class MUST be used after being inherited
17
- # Subclasses will have a constant `INSTANCE` storing the only instance of that class
17
+ # Subclasses will have a constant `INSTANCE`
18
+ # storing the only instance of that class
18
19
  # @note
19
- # This class assumed descendants to NOT override {.inherited} or call `super` if overridden
20
+ # This class assumed descendants to NOT
21
+ # override {.inherited} or call `super` if overridden
20
22
  # Otherwise the constant `INSTANCE` won't work
21
23
  # @note
22
24
  # The constant `INSTANCE` will be referred with namespace,
@@ -69,22 +71,22 @@ module RSpec
69
71
  # But only 1 argument is accepted
70
72
  def self.[](*values)
71
73
  unless values.size == EXPECTED_VALUE_SIZE
72
- raise ArgumentError, "Exactly #{EXPECTED_VALUE_SIZE} argument is required"
74
+ fail(
75
+ ArgumentError,
76
+ "Exactly #{EXPECTED_VALUE_SIZE} argument is required",
77
+ )
73
78
  end
74
79
  super
75
80
  end
76
81
  end
77
82
 
78
- # Takes any number of objects and converts into expectation objects (if not already)
83
+ # Takes any number of objects and
84
+ # converts into expectation objects (if not already)
79
85
  #
80
86
  # @abstract
81
87
  class CompositeExpectation < CallableExpectation
82
88
  extend AbstractClass
83
89
 
84
- private
85
- attr_reader :expectations
86
- public
87
-
88
90
  # (see CallableExpectation.[])
89
91
  # Also all values will be converted into expectations
90
92
  def self.[](*values)
@@ -93,6 +95,8 @@ module RSpec
93
95
 
94
96
  private
95
97
 
98
+ attr_reader :expectations
99
+
96
100
  def initialize(expectations)
97
101
  @expectations = expectations
98
102
  end
@@ -9,7 +9,8 @@ module RSpec
9
9
  # @api
10
10
  # The modules under this module can be included (in RSpec)
11
11
  #
12
- # If this gem or extensions gems decide to add different groups of expectations classes
12
+ # If this gem or extensions gems decide to
13
+ # add different groups of expectations classes
13
14
  # Which aim to be included in example groups
14
15
  # They should add the namespace modules here
15
16
  module Mixins
@@ -22,7 +23,8 @@ module RSpec
22
23
  module BuiltIn
23
24
  # Whatever the value is, it just passes
24
25
  # A more verbose solution than passing {Object} in
25
- # (That also works since everything parsed by {JSON} inherits from {Object})
26
+ # (That also works since everything parsed
27
+ # by {JSON} inherits from {Object})
26
28
  #
27
29
  # @example
28
30
  # { key_with_unstable_content => Anything }
@@ -61,22 +63,20 @@ module RSpec
61
63
  end
62
64
  end
63
65
 
64
- # Takes exactly one object and converts to an expectation object (if not already)
66
+ # Takes exactly one object and converts to
67
+ # an expectation object (if not already)
65
68
  # Validates `value` to be {Array}
66
69
  # And uses stored expectation for checking all elements of `value`
67
70
  class ArrayOf < Expectations::Core::SingleValueCallableExpectation
68
- private
69
- attr_reader :children_elements_expectation
70
- public
71
-
72
71
  def expect?(value)
73
72
  value.is_a?(Array) &&
74
73
  (empty_allowed? || !value.empty?) &&
75
- value.all? {|v| children_elements_expectation.expect?(v) }
74
+ value.all? { |v| children_elements_expectation.expect?(v) }
76
75
  end
77
76
 
78
77
  # {Enumerable#all?} returns `true` when collection is empty
79
- # So this method can be called to signal the expectation to do or do not expect an empty collection
78
+ # So this method can be called to signal the expectation to
79
+ # do or do not expect an empty collection
80
80
  #
81
81
  # @param allow [Boolean]
82
82
  # optional
@@ -84,7 +84,7 @@ module RSpec
84
84
  #
85
85
  # @return [ArrayOf] the matcher itself
86
86
  def allow_empty(allow = true)
87
- @empty_allowed = !!allow
87
+ @empty_allowed = allow
88
88
  self
89
89
  end
90
90
 
@@ -97,13 +97,15 @@ module RSpec
97
97
 
98
98
  private
99
99
 
100
+ attr_reader :children_elements_expectation
101
+
100
102
  def initialize(value)
101
103
  @children_elements_expectation = Expectation.build(value)
102
104
  @empty_allowed = true
103
105
  end
104
106
 
105
107
  def empty_allowed?
106
- !!@empty_allowed
108
+ @empty_allowed
107
109
  end
108
110
  end
109
111
 
@@ -127,6 +129,15 @@ module RSpec
127
129
  end
128
130
  end
129
131
 
132
+ # (see AnyOf)
133
+ # It will pass regardless of {#expectations}
134
+ # if the value is `nil`
135
+ class NullableOf < AnyOf
136
+ def expect?(value)
137
+ value.nil? || super
138
+ end
139
+ end
140
+
130
141
  # Takes any number of {Integer} or {Range} (if not already)
131
142
  # Validates `value` to be {Array}
132
143
  # And the size matches any value passed in
@@ -134,7 +145,8 @@ module RSpec
134
145
  # @note
135
146
  # For behaviour of "and" (which should be a rare case)
136
147
  # Combine {AllOf} & {ArrayWithSize}
137
- # Or raise an issue to add support for switching to "and" with another method call
148
+ # Or raise an issue to add support for
149
+ # switching to "and" with another method call
138
150
  class ArrayWithSize < AnyOf
139
151
  # `Fixnum` & `Bignum` will be returned instead of `Integer`
140
152
  # in `#class` for numbers
@@ -149,7 +161,7 @@ module RSpec
149
161
  # Overrides {Expectation.build}
150
162
  def build(value)
151
163
  expectation_classes_mappings.fetch(value.class) do
152
- -> (_) { raise ArgumentError, <<-ERR }
164
+ -> (_) { fail ArgumentError, <<-ERR }
153
165
  Expected expection(s) to be kind of
154
166
  #{expectation_classes_mappings.keys.inspect}
155
167
  but found #{value.inspect}
@@ -7,10 +7,13 @@ module RSpec
7
7
  module JsonMatchers
8
8
  module Expectations
9
9
  # @api private
10
- # All classes within module should NOT be able to be used directly / extended
10
+ # All classes within module should NOT be able
11
+ # to be used directly / extended
11
12
  #
12
- # All classes in this module are internal expectations used when non-expectation object/class is passed in
13
- # Extension gems should have their own namespace and should NOT add new classes to this namespace
13
+ # All classes in this module are internal expectations used
14
+ # when non-expectation object/class is passed in
15
+ # Extension gems should have their own namespace and
16
+ # should NOT add new classes to this namespace
14
17
  # Classes here have dependency on {Core} & {Mixins::BuiltIn}
15
18
  #
16
19
  # TODO: Remove dependency on {Mixins::BuiltIn}
@@ -21,16 +24,14 @@ module RSpec
21
24
  # Takes exactly one object
22
25
  # Use stored value & `==` for checking `value`
23
26
  class Eq < Core::SingleValueCallableExpectation
24
- private
25
- attr_reader :expected_value
26
- public
27
-
28
27
  def expect?(value)
29
28
  value == expected_value
30
29
  end
31
30
 
32
31
  private
33
32
 
33
+ attr_reader :expected_value
34
+
34
35
  def initialize(value)
35
36
  @expected_value = value
36
37
  end
@@ -50,18 +51,18 @@ module RSpec
50
51
  EXPECTED_CLASS = Class
51
52
  private_constant :EXPECTED_CLASS
52
53
 
53
- private
54
- attr_reader :expected_class
55
- public
56
-
57
54
  def expect?(value)
58
55
  value.is_a?(expected_class)
59
56
  end
60
57
 
61
58
  private
62
59
 
60
+ attr_reader :expected_class
61
+
63
62
  def initialize(value)
64
- raise ArgumentError, "a #{EXPECTED_CLASS} is required" unless value.is_a?(EXPECTED_CLASS)
63
+ unless value.is_a?(EXPECTED_CLASS)
64
+ fail ArgumentError, "a #{EXPECTED_CLASS} is required"
65
+ end
65
66
  @expected_class = value
66
67
  end
67
68
  end
@@ -75,18 +76,18 @@ module RSpec
75
76
  EXPECTED_CLASS = Range
76
77
  private_constant :EXPECTED_CLASS
77
78
 
78
- private
79
- attr_reader :range
80
- public
81
-
82
79
  def expect?(value)
83
80
  range.cover?(value)
84
81
  end
85
82
 
86
83
  private
87
84
 
85
+ attr_reader :range
86
+
88
87
  def initialize(value)
89
- raise ArgumentError, "a #{EXPECTED_CLASS} is required" unless value.is_a?(EXPECTED_CLASS)
88
+ unless value.is_a?(EXPECTED_CLASS)
89
+ fail ArgumentError, "a #{EXPECTED_CLASS} is required"
90
+ end
90
91
  @range = value
91
92
  end
92
93
  end
@@ -100,20 +101,20 @@ module RSpec
100
101
  EXPECTED_CLASS = Regexp
101
102
  private_constant :EXPECTED_CLASS
102
103
 
103
- private
104
- attr_reader :regexp
105
- public
106
-
107
104
  def expect?(value)
108
105
  # regex =~ string seems to be fastest
109
106
  # @see https://stackoverflow.com/questions/11887145/fastest-way-to-check-if-a-string-matches-or-not-a-regexp-in-ruby
110
- value.is_a?(String) && !!(regexp =~ value)
107
+ value.is_a?(String) && regexp =~ value
111
108
  end
112
109
 
113
110
  private
114
111
 
112
+ attr_reader :regexp
113
+
115
114
  def initialize(value)
116
- raise ArgumentError, "a #{EXPECTED_CLASS} is required" unless value.is_a?(EXPECTED_CLASS)
115
+ unless value.is_a?(EXPECTED_CLASS)
116
+ fail ArgumentError, "a #{EXPECTED_CLASS} is required"
117
+ end
117
118
  @regexp = value
118
119
  end
119
120
  end
@@ -124,18 +125,19 @@ module RSpec
124
125
  # Takes exactly one object
125
126
  # Use stored proc for checking `value`
126
127
  class SatisfyingCallable < Core::SingleValueCallableExpectation
127
- private
128
- attr_reader :callable
129
- public
130
-
131
128
  def expect?(value)
132
129
  callable.call(value)
133
130
  end
134
131
 
135
132
  private
136
133
 
134
+ attr_reader :callable
135
+
137
136
  def initialize(value)
138
- raise ArgumentError, "an object which respond to `:call` is required" unless value.respond_to?(:call)
137
+ unless value.respond_to?(:call)
138
+ fail ArgumentError,
139
+ "an object which respond to `:call` is required"
140
+ end
139
141
  @callable = value
140
142
  end
141
143
  end
@@ -154,24 +156,29 @@ module RSpec
154
156
  # Used internally by a matcher method
155
157
  #
156
158
  # Comparing to {Expectations::Mixins::BuiltIn::ArrayWithSize}
157
- # This also accepts `Hash` and `Array`, and return false for collection matching
159
+ # This also accepts `Hash` and `Array`
160
+ # and return false for collection matching
158
161
  class ArrayWithSize < Expectations::Mixins::BuiltIn::ArrayWithSize
159
162
  # `Fixnum` & `Bignum` will be returned instead of `Integer`
160
163
  # in `#class` for numbers
161
- ADDITIONAL_EXPECTED_VALUE_CLASS_TO_EXPECTATION_CLASS_MAPPING = {
164
+ ADDITIONAL_EXPECTATION_CLASS_MAPPINGS = {
162
165
  Array => -> (_) { Expectations::Private::Nothing::INSTANCE },
163
166
  Hash => -> (_) { Expectations::Private::Nothing::INSTANCE },
164
167
  }.freeze
165
- private_constant :ADDITIONAL_EXPECTED_VALUE_CLASS_TO_EXPECTATION_CLASS_MAPPING
168
+ private_constant :ADDITIONAL_EXPECTATION_CLASS_MAPPINGS
166
169
 
167
170
  class << self
168
171
  private
169
172
 
170
- # Overrides {Expectations::Mixins::BuiltIn::ArrayWithSize.expectation_classes_mappings}
173
+ # Overrides
174
+ # {Expectations::Mixins::BuiltIn::ArrayWithSize.
175
+ # expectation_classes_mappings}
171
176
  #
172
177
  # @return [Hash]
173
178
  def expectation_classes_mappings
174
- super.merge(ADDITIONAL_EXPECTED_VALUE_CLASS_TO_EXPECTATION_CLASS_MAPPING)
179
+ super.merge(
180
+ ADDITIONAL_EXPECTATION_CLASS_MAPPINGS,
181
+ )
175
182
  end
176
183
  end
177
184
  end