rspec-json_api 1.4.0 → 1.6.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.
@@ -1,23 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
- module Rspec
4
- module JsonApi
5
- module Generators
6
- class TypeGenerator < Rails::Generators::NamedBase
7
- source_root File.expand_path("templates", __dir__)
8
-
9
- def copy_type_file
10
- create_file "spec/rspec/json_api/types/#{file_name}.rb", <<~FILE
11
- module RSpec
12
- module JsonApi
13
- module Types
14
- #{file_name.upcase} = //
15
- end
16
- end
17
- end
18
- FILE
19
- end
20
- end
21
- end
22
- end
23
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Rspec
4
+ module JsonApi
5
+ module Generators
6
+ class TypeGenerator < Rails::Generators::NamedBase
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ def copy_type_file
10
+ create_file "spec/rspec/json_api/types/#{file_name}.rb", <<~FILE
11
+ # frozen_string_literal: true
12
+
13
+ module RSpec
14
+ module JsonApi
15
+ module Types
16
+ #{file_name.upcase} = //
17
+ end
18
+ end
19
+ end
20
+ FILE
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module JsonApi
5
+ # Constraints evaluates the option hash produced by a schema Proc
6
+ # (e.g. `-> { { type: Integer, min: 1, max: 10, allow_blank: true } }`)
7
+ # against an actual value.
8
+ #
9
+ # allow_blank is a modifier, not a constraint of its own: when it is true a
10
+ # blank value is accepted and the remaining options are skipped; otherwise
11
+ # the value is checked against every other option.
12
+ module Constraints
13
+ module_function
14
+
15
+ SUPPORTED_OPTIONS = %i[allow_blank type value min max inclusion regex lambda].freeze
16
+
17
+ # @param value [Object] the actual value being matched.
18
+ # @param options [Hash] the option hash returned by the schema Proc.
19
+ # @return [Boolean] true when the value satisfies the options.
20
+ # @raise [ArgumentError] when an option key is not supported.
21
+ def match(value, options)
22
+ validate!(options)
23
+
24
+ return true if value.blank? && options[:allow_blank]
25
+
26
+ options.except(:allow_blank).all? do |option, condition|
27
+ satisfies?(value, option, condition)
28
+ end
29
+ end
30
+
31
+ def validate!(options)
32
+ raise ArgumentError, "options must be a Hash, got #{options.class}" unless options.is_a?(Hash)
33
+
34
+ unknown = options.keys - SUPPORTED_OPTIONS
35
+ return if unknown.empty?
36
+
37
+ raise ArgumentError, "Unsupported match option(s): #{unknown.join(", ")}"
38
+ end
39
+
40
+ def satisfies?(value, option, condition)
41
+ case option
42
+ when :type then value.instance_of?(condition)
43
+ when :value then value == condition
44
+ when :inclusion then condition.include?(value)
45
+ when :regex then condition.match?(value.to_s)
46
+ when :lambda then condition.call(value)
47
+ when :min, :max then within_bound?(value, option, condition)
48
+ end
49
+ end
50
+
51
+ def within_bound?(value, option, condition)
52
+ return false unless value.is_a?(Numeric) && condition.is_a?(Numeric)
53
+
54
+ option == :min ? value >= condition : value <= condition
55
+ end
56
+ end
57
+ end
58
+ end
@@ -23,38 +23,62 @@ module RSpec
23
23
  # @param actual [String] The JSON string to test against the expected schema.
24
24
  # @return [Boolean] true if the actual JSON matches the expected schema, false otherwise.
25
25
  def matches?(actual)
26
- @actual = JSON.parse(actual, symbolize_names: true)
27
- @diff = Diffy::Diff.new(expected, @actual, context: 5)
26
+ @diff = nil
27
+ @actual = actual
28
+ @type_error = !actual.is_a?(String)
29
+
30
+ return false if @type_error
28
31
 
29
- return false unless @actual.instance_of?(expected.class)
32
+ @actual = JSON.parse(actual, symbolize_names: true)
30
33
 
31
- if expected.instance_of?(Array)
32
- RSpec::JsonApi::CompareArray.compare(@actual, expected)
33
- else
34
- return false unless @actual.deep_keys.deep_sort == expected.deep_keys.deep_sort
34
+ RSpec::JsonApi::SchemaMatch.match(@actual, expected)
35
+ rescue JSON::ParserError
36
+ @actual = actual
37
+ false
38
+ end
35
39
 
36
- RSpec::JsonApi::CompareHash.compare(@actual, expected)
37
- end
40
+ # A non-String actual is a mistake in the spec rather than a fact about the
41
+ # response, so the negated form has to fail rather than pass by default.
42
+ # @param actual [String] The JSON string to test against the expected schema.
43
+ # @return [Boolean] true if the actual JSON does not match the expected schema.
44
+ def does_not_match?(actual)
45
+ !matches?(actual) && !@type_error
38
46
  end
39
47
 
40
48
  # Provides a failure message for when the JSON data does not match the expected schema.
41
49
  # @return [String] A descriptive message detailing the mismatch between expected and actual JSON.
42
50
  def failure_message
51
+ return type_error_message if @type_error
52
+
43
53
  <<~MSG
44
54
  expected: #{expected}
45
55
  got: #{actual}
46
56
 
47
57
  Diff:
48
- #{@diff}
58
+ #{diff}
49
59
  MSG
50
60
  end
51
61
 
52
62
  # Provides a failure message for when the JSON data matches the expected schema, but it was expected not to.
53
63
  # This is used in negative matchers.
54
- # @return [self] Returns itself, but typically this method should be implemented to return a descriptive message
64
+ # @return [String] A descriptive message indicating the JSON was expected not to match the schema.
55
65
  def failure_message_when_negated
66
+ return type_error_message if @type_error
67
+
56
68
  "expected the JSON data not to match the provided schema, but it did."
57
69
  end
70
+
71
+ private
72
+
73
+ # The diff is only needed to render a failure message, so it is built
74
+ # lazily and memoized rather than on every matches? call.
75
+ def diff
76
+ @diff ||= Diffy::Diff.new(expected, actual, context: 5)
77
+ end
78
+
79
+ def type_error_message
80
+ "expected a JSON String to match against the schema, got #{actual.class}"
81
+ end
58
82
  end
59
83
  end
60
84
  end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module JsonApi
5
+ # SchemaMatch compares parsed JSON (a Hash, an Array, or a scalar) against an
6
+ # expected schema. It is the single entry point behind the match_json_schema
7
+ # matcher: callers hand it the actual and expected values and it dispatches on
8
+ # shape internally, so the matcher does not need to know whether it is looking
9
+ # at an object or a collection.
10
+ module SchemaMatch
11
+ module_function
12
+
13
+ # Top-level comparison. Applies the shape guards (class equality and, for
14
+ # objects, key-set equality) before recursing.
15
+ def match(actual, expected)
16
+ return false unless actual.instance_of?(expected.class)
17
+
18
+ case expected
19
+ when Array
20
+ compare_array(actual, expected)
21
+ when Hash
22
+ return false unless same_key_structure?(actual, expected)
23
+
24
+ compare(actual, expected)
25
+ else
26
+ compare_simple_value(actual, expected)
27
+ end
28
+ end
29
+
30
+ def same_key_structure?(actual, expected)
31
+ Traversal.deep_sort(Traversal.deep_keys(actual)) ==
32
+ Traversal.deep_sort(Traversal.deep_keys(expected))
33
+ end
34
+
35
+ def compare(actual, expected)
36
+ return false unless actual.is_a?(Hash)
37
+ return false if actual.blank? && expected.present?
38
+
39
+ keys = Traversal.deep_key_paths(expected) | Traversal.deep_key_paths(actual)
40
+
41
+ compare_key_paths_and_values(keys, actual, expected)
42
+ end
43
+
44
+ def compare_key_paths_and_values(keys, actual, expected)
45
+ keys.all? do |key_path|
46
+ actual_value = dig_path(actual, key_path)
47
+ expected_value = dig_path(expected, key_path)
48
+
49
+ compare_values(actual_value, expected_value)
50
+ end
51
+ end
52
+
53
+ # Digs a key path without raising when an intermediate value is not a Hash.
54
+ # Plain Hash#dig raises TypeError if it walks into a scalar (e.g. a schema
55
+ # expects a nested object but the actual value is a String), so a mismatch
56
+ # would crash instead of failing the match.
57
+ def dig_path(data, key_path)
58
+ key_path.reduce(data) do |value, key|
59
+ break nil unless value.is_a?(Hash)
60
+
61
+ value[key]
62
+ end
63
+ end
64
+
65
+ def compare_values(actual_value, expected_value)
66
+ case expected_value
67
+ when Class then compare_class(actual_value, expected_value)
68
+ when Regexp then compare_regexp(actual_value, expected_value)
69
+ when Proc then compare_proc(actual_value, expected_value)
70
+ when Array then compare_array(actual_value, expected_value)
71
+ else compare_simple_value(actual_value, expected_value)
72
+ end
73
+ end
74
+
75
+ def compare_class(actual_value, expected_value)
76
+ actual_value.instance_of?(expected_value)
77
+ end
78
+
79
+ def compare_regexp(actual_value, expected_value)
80
+ expected_value.match?(actual_value.to_s)
81
+ end
82
+
83
+ # A schema Proc describes the constraints for a value; it is called without
84
+ # arguments and must return the option Hash. A Proc that expects the value
85
+ # as an argument is a common misreading of the DSL, and calling it here
86
+ # would raise a bare "wrong number of arguments" from deep in the matcher.
87
+ def compare_proc(actual_value, expected_value)
88
+ if declares_value_parameter?(expected_value)
89
+ raise ArgumentError,
90
+ "schema Proc must take no arguments; " \
91
+ "write -> { { lambda: ->(value) { ... } } } to test the value itself"
92
+ end
93
+
94
+ options = expected_value.call
95
+ raise ArgumentError, "schema Proc must return an options Hash, got #{options.class}" unless options.is_a?(Hash)
96
+
97
+ Constraints.match(actual_value, options)
98
+ end
99
+
100
+ # A non-lambda Proc reports its block parameters as optional, so
101
+ # `proc { |value| ... }` has to be caught on the parameter list rather
102
+ # than on arity. A bare splat states no expectation and is left alone.
103
+ def declares_value_parameter?(callable)
104
+ callable.parameters.any? { |type, _name| %i[req opt keyreq].include?(type) }
105
+ end
106
+
107
+ # A list schema only ever matches an actual Array. Without this guard the
108
+ # branches below call Array methods on whatever the response contained, so
109
+ # a null or a scalar where a list was expected raised NoMethodError
110
+ # instead of failing the match.
111
+ def compare_array(actual_value, expected_value)
112
+ return false unless actual_value.is_a?(Array)
113
+
114
+ if simple_type?(expected_value)
115
+ compare_typed_array(actual_value, expected_value)
116
+ elsif interface?(expected_value)
117
+ compare_interface_array(actual_value, expected_value)
118
+ else
119
+ compare_exact_array(actual_value, expected_value)
120
+ end
121
+ end
122
+
123
+ # [SomeClass] => every element must be an instance of SomeClass.
124
+ def compare_typed_array(actual_value, expected_value)
125
+ type = expected_value[0]
126
+
127
+ actual_value.all? { |elem| compare_class(elem, type) }
128
+ end
129
+
130
+ # [{ ...interface... }] => every element must match the single interface.
131
+ # Elements go through match (not compare) so each one is held to the same
132
+ # key-structure guard as a top-level object; otherwise an element with an
133
+ # extra null-valued key would slip through (nil == nil).
134
+ def compare_interface_array(actual_value, expected_value)
135
+ interface = expected_value[0]
136
+
137
+ actual_value.all? { |elem| match(elem, interface) }
138
+ end
139
+
140
+ # Any other array => element-by-element match, sizes must be equal.
141
+ def compare_exact_array(actual_value, expected_value)
142
+ return false if actual_value.size != expected_value.size
143
+
144
+ expected_value.each_with_index.all? do |elem, index|
145
+ elem.is_a?(Hash) ? compare(actual_value[index], elem) : compare_values(actual_value[index], elem)
146
+ end
147
+ end
148
+
149
+ def compare_simple_value(actual_value, expected_value)
150
+ actual_value == expected_value
151
+ end
152
+
153
+ def simple_type?(expected_value)
154
+ expected_value.size == 1 && expected_value[0].instance_of?(Class)
155
+ end
156
+
157
+ def interface?(expected_value)
158
+ expected_value.size == 1 && expected_value[0].is_a?(Hash)
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module JsonApi
5
+ # Traversal holds the structural helpers used to compare JSON shapes:
6
+ # collecting the nested key structure of a Hash and sorting it into a
7
+ # canonical order. These were previously monkey-patched onto core Hash and
8
+ # Array; keeping them here means the gem no longer mutates those classes in
9
+ # the host application.
10
+ module Traversal
11
+ module_function
12
+
13
+ # The nested keys of a hash, with each nested hash's keys inlined as an
14
+ # array, e.g. { a: 1, b: { c: 2 } } => [:a, :b, [:c]].
15
+ def deep_keys(hash)
16
+ hash.each_with_object([]) do |(key, value), keys|
17
+ keys << key
18
+ keys << deep_keys(value) if value.respond_to?(:keys)
19
+ end
20
+ end
21
+
22
+ # Every leaf key path of a hash, e.g. { a: { b: 1 }, c: 2 } => [[:a, :b], [:c]].
23
+ def deep_key_paths(hash)
24
+ stack = hash.map { |key, value| [[key], value] }
25
+ key_map = []
26
+
27
+ until stack.empty?
28
+ key, value = stack.pop
29
+
30
+ key_map << key unless value.is_a?(Hash)
31
+
32
+ next unless value.is_a?(Hash)
33
+
34
+ value.each { |k, v| stack.push([key.dup << k, v]) }
35
+ end
36
+
37
+ key_map.reverse
38
+ end
39
+
40
+ # Recursively sorts an array (and any nested arrays) into a canonical order
41
+ # so two key structures can be compared regardless of original order.
42
+ def deep_sort(array)
43
+ array
44
+ .map { |element| element.is_a?(Array) ? deep_sort(element) : element }
45
+ .sort_by { |element| element.is_a?(Array) ? element.first.to_s : element.to_s }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,7 +3,11 @@
3
3
  module RSpec
4
4
  module JsonApi
5
5
  module Types
6
- URI = URI::DEFAULT_PARSER.make_regexp
6
+ # URI::DEFAULT_PARSER.make_regexp is unanchored, and comparison uses
7
+ # Regexp#match?, so on its own it accepts any string that merely contains
8
+ # a URI ("see https://example.com for details"). \A...\z holds the whole
9
+ # value to the pattern, the same way EMAIL and UUID already are anchored.
10
+ URI = /\A#{::URI::DEFAULT_PARSER.make_regexp}\z/
7
11
  end
8
12
  end
9
13
  end
@@ -3,7 +3,7 @@
3
3
  module RSpec
4
4
  module JsonApi
5
5
  module Types
6
- UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
6
+ UUID = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/
7
7
  end
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
- # frozen_string_literal: true
2
-
3
- module RSpec
4
- module JsonApi
5
- VERSION = "1.4.0"
6
- end
7
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module JsonApi
5
+ VERSION = "1.6.0"
6
+ end
7
+ end
@@ -1,25 +1,23 @@
1
- # frozen_string_literal: true
2
-
3
- # Load 3rd party libraries
4
- require "json"
5
- require "diffy"
6
- require "active_support/core_ext/object/blank"
7
-
8
- # Load the json_api parts
9
- require "rspec/json_api/version"
10
- require "rspec/json_api/compare_hash"
11
- require "rspec/json_api/compare_array"
12
-
13
- # Load extensions
14
- require "extensions/hash"
15
- require "extensions/array"
16
-
17
- # Load matchers
18
- require "rspec/json_api/matchers"
19
- require "rspec/json_api/matchers/match_json_schema"
20
- require "rspec/json_api/matchers/have_no_content"
21
-
22
- # Load defined types
23
- require "rspec/json_api/types/email"
24
- require "rspec/json_api/types/uri"
25
- require "rspec/json_api/types/uuid"
1
+ # frozen_string_literal: true
2
+
3
+ # Load 3rd party libraries
4
+ require "json"
5
+ require "uri"
6
+ require "diffy"
7
+ require "active_support/core_ext/object/blank"
8
+
9
+ # Load the json_api parts
10
+ require "rspec/json_api/version"
11
+ require "rspec/json_api/traversal"
12
+ require "rspec/json_api/constraints"
13
+ require "rspec/json_api/schema_match"
14
+
15
+ # Load matchers
16
+ require "rspec/json_api/matchers"
17
+ require "rspec/json_api/matchers/match_json_schema"
18
+ require "rspec/json_api/matchers/have_no_content"
19
+
20
+ # Load defined types
21
+ require "rspec/json_api/types/email"
22
+ require "rspec/json_api/types/uri"
23
+ require "rspec/json_api/types/uuid"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-json_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Gajowiak
8
- autorequire:
9
- bindir: exe
8
+ bindir: bin
10
9
  cert_chain: []
11
- date: 2026-01-23 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -39,7 +38,7 @@ dependencies:
39
38
  - !ruby/object:Gem::Version
40
39
  version: 3.4.2
41
40
  - !ruby/object:Gem::Dependency
42
- name: rails
41
+ name: railties
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
@@ -66,47 +65,32 @@ dependencies:
66
65
  - - ">="
67
66
  - !ruby/object:Gem::Version
68
67
  version: 5.0.2
69
- description:
70
68
  email:
71
69
  - m.gajowiak@nomtek.com
72
70
  executables: []
73
71
  extensions: []
74
72
  extra_rdoc_files: []
75
73
  files:
76
- - ".github/workflows/main.yml"
77
- - ".gitignore"
78
- - ".rspec"
79
- - ".rubocop.yml"
80
- - ".ruby-version"
81
74
  - CHANGELOG.md
82
- - CODE_OF_CONDUCT.md
83
- - Gemfile
84
- - Gemfile.lock
85
75
  - LICENSE.txt
86
76
  - README.md
87
- - Rakefile
88
- - bin/console
89
- - bin/setup
90
- - lib/extensions/array.rb
91
- - lib/extensions/hash.rb
92
77
  - lib/generators/rspec/json_api/install/install_generator.rb
93
78
  - lib/generators/rspec/json_api/install/templates/rspec/json_api/interfaces/.empty_directory
94
79
  - lib/generators/rspec/json_api/install/templates/rspec/json_api/types/.empty_directory
95
80
  - lib/generators/rspec/json_api/interface/interface_generator.rb
96
- - lib/generators/rspec/json_api/interface/templates/interface.erb
97
81
  - lib/generators/rspec/json_api/type/type_generator.rb
98
82
  - lib/rspec/json_api.rb
99
- - lib/rspec/json_api/compare_array.rb
100
- - lib/rspec/json_api/compare_hash.rb
83
+ - lib/rspec/json_api/constraints.rb
101
84
  - lib/rspec/json_api/interfaces/example_interface.rb
102
85
  - lib/rspec/json_api/matchers.rb
103
86
  - lib/rspec/json_api/matchers/have_no_content.rb
104
87
  - lib/rspec/json_api/matchers/match_json_schema.rb
88
+ - lib/rspec/json_api/schema_match.rb
89
+ - lib/rspec/json_api/traversal.rb
105
90
  - lib/rspec/json_api/types/email.rb
106
91
  - lib/rspec/json_api/types/uri.rb
107
92
  - lib/rspec/json_api/types/uuid.rb
108
93
  - lib/rspec/json_api/version.rb
109
- - rspec-json_api.gemspec
110
94
  homepage: https://github.com/nomtek/rspec-json_api
111
95
  licenses:
112
96
  - MIT
@@ -115,7 +99,6 @@ metadata:
115
99
  source_code_uri: https://github.com/nomtek/rspec-json_api
116
100
  changelog_uri: https://github.com/nomtek/rspec-json_api/blob/master/CHANGELOG.md
117
101
  rubygems_mfa_required: 'true'
118
- post_install_message:
119
102
  rdoc_options: []
120
103
  require_paths:
121
104
  - lib
@@ -130,8 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
113
  - !ruby/object:Gem::Version
131
114
  version: '0'
132
115
  requirements: []
133
- rubygems_version: 3.4.10
134
- signing_key:
116
+ rubygems_version: 4.0.3
135
117
  specification_version: 4
136
118
  summary: RSpec extension to test JSON API response.
137
119
  test_files: []
@@ -1,20 +0,0 @@
1
- name: Ruby
2
-
3
- on: [push,pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: actions/checkout@v4
10
- - name: Set up Ruby
11
- uses: ruby/setup-ruby@v1
12
- with:
13
- ruby-version: 3.2.2
14
- bundler: 4.0.4
15
- - name: Bundle gems
16
- run: bundle install
17
- - name: Run rspec
18
- run: bundle exec rspec
19
- - name: Run rubocop
20
- run: bundle exec rubocop
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop.yml DELETED
@@ -1,50 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 3.2
3
- NewCops: enable
4
- SuggestExtensions: false
5
- Exclude:
6
- - "README.md"
7
-
8
- Style/StringLiterals:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Style/StringLiteralsInInterpolation:
13
- Enabled: true
14
- EnforcedStyle: double_quotes
15
-
16
- Layout/LineLength:
17
- Max: 120
18
-
19
- Metrics/BlockLength:
20
- Exclude:
21
- - "spec/**/*_spec.rb"
22
-
23
- Metrics/MethodLength:
24
- Exclude:
25
- - "lib/rspec/json_api/compare_hash.rb"
26
- - "lib/extensions/hash.rb"
27
-
28
- Metrics/AbcSize:
29
- Exclude:
30
- - "lib/rspec/json_api/compare_hash.rb"
31
- - "lib/rspec/json_api/matchers/match_json_schema.rb"
32
-
33
- Metrics/CyclomaticComplexity:
34
- Exclude:
35
- - "lib/rspec/json_api/compare_hash.rb"
36
-
37
- Metrics/PerceivedComplexity:
38
- Exclude:
39
- - "lib/rspec/json_api/compare_hash.rb"
40
-
41
- Style/Documentation:
42
- Enabled: false
43
-
44
- Naming/PredicatePrefix:
45
- Exclude:
46
- - "lib/rspec/json_api/matchers.rb"
47
-
48
- Naming/PredicateMethod:
49
- Exclude:
50
- - "lib/rspec/json_api/compare_hash.rb"
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 3.2.2