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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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