rspec-json_api 1.5.0 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6be977d812080cea5feea8a9c9a0a429196c43c06b2aa64e0e742a44e0eb66fd
4
- data.tar.gz: 77d21174747915fb1c33607eb331e4cfbd8ae9ed9dfa13936bd1cf2436de41af
3
+ metadata.gz: 1af83b52a7a2a283b8f88e3d35920a6a5222fe10ec786a7a7f09f509ee923b75
4
+ data.tar.gz: 7772ea842f6e73370494969b5f5ad867ef0cb67f77449a95607b71190fd37545
5
5
  SHA512:
6
- metadata.gz: ea7471b8cb4ed9e871917ec51f61426931c765f2c0c46f4bd0c2c5291b3af1fecd855646847897608eace564f734337d34290551b7fca82181b37f9a2eb856da
7
- data.tar.gz: ef41fe2e7c32dfc6b605cfce7f847ef48a7c0f84878c32ca72178923a0f53e6f556541ef5d28cddb1cd795dc061be7016fa7970266ef369b3f0c693ffb41d9e7
6
+ metadata.gz: 7bd1537b2ea54021789bc2bf2d9ace9df346c2a75ee1d3183eafd6ca0aaebfa332b534f233047605bb252cc0afb76e35d9e7e26d97c921cbc44f321760ef1fe9
7
+ data.tar.gz: 6a2161022a3bff6d5af28398653289eaee7be1e3cc27cddeedd74cffaeb644a7e2548c6e5e04891e211c826a68b6c0d0de2cf7d98253459b0bc90d35e7ca126c
data/CHANGELOG.md CHANGED
@@ -1,9 +1,56 @@
1
- ## [Unreleased]
1
+ ## [2.0.0] - 2026-09-14
2
+
3
+ ### Fixed
4
+ - Exact arrays enforce full key structure for Hash elements. Extra null-valued keys and missing keys that happen to accept blank values no longer pass inside tuples.
5
+ - Nested key checks preserve each key's parent association. Objects with the same nested key names attached to different parents no longer match when the affected values are `null`.
6
+ - Regexp schemas require a String value instead of matching `to_s`. Numeric values and `null` no longer satisfy permissive regular expressions.
7
+
8
+ ### Changed
9
+ - Removed ActiveSupport blank/present core extensions in favor of a JSON-focused internal helper with the same relevant semantics.
10
+ - Runtime dependencies are now only `diffy` and `rspec-expectations`. Rails, Railties, ActiveSupport, and `rspec-rails` are no longer installed for matcher-only consumers. Projects using the optional generators must provide Rails themselves.
11
+ - The compatibility matrix tests matcher behavior without Rails on Ruby 3.2, 3.3, 3.4, and 4.0. Separate generator jobs exercise Rails 6.1 and Rails 8.1 at the supported boundaries.
12
+ - The README now documents strict key behavior, schema dispatch, errors, array shorthand, supported runtimes, and the fact that this is a general JSON shape matcher rather than a JSON:API implementation.
13
+ - CI runs `actions/checkout@v7`, up from v5, alongside `ruby/setup-ruby@v1`. Both track their major tag, so upstream patch releases arrive without a commit here and Dependabot opens a pull request for each new major. Checkout also runs with `persist-credentials: false`: no step needs git credentials, and bundler evaluates gemspec and native-extension code from the branch under test.
14
+ - The workflow runs once per change rather than twice. `push` is scoped to `master`, so a branch with an open pull request no longer builds under both events, and a `concurrency` group cancels superseded pull request runs. Runs on `master` are left alone, so every commit there keeps a result.
15
+ - `rubocop` and `bundler-audit` share one job definition instead of two byte-identical ones, and every job carries `timeout-minutes: 30` in place of the six-hour default.
16
+
17
+ ### Added
18
+ - Top-level Class and Regexp schemas now work for scalar JSON documents, using the same dispatch rules as nested values.
19
+ - `match_json_schema` exposes an RSpec description for one-line and documentation formatter output.
20
+ - Direct unit coverage for schema matching, traversal, constraints, and the Rails generators; randomized spec ordering; Ruby warnings; and a 90% SimpleCov floor.
21
+ - Contributor and security policy documents, included in the packaged gem.
22
+ - A weekly `schedule` trigger, so `bundler-audit` reports an advisory published against a lockfile nobody has touched. Only the audit runs on that trigger. `workflow_dispatch` runs it on demand and is also how the cron is re-armed, since GitHub disables scheduled workflows after 60 days without repository activity.
23
+
24
+ ### Removed
25
+ - The internal example interface fixture from the packaged library; it now lives under test support.
26
+ - Redundant Rails 7.1, 7.2, and 8.0 appraisal files after reducing generator compatibility checks to the supported boundaries.
27
+
28
+ ## [1.6.0] - 2026-09-03
29
+
30
+ ### Fixed
31
+ - A list schema (`[String]`, `[INTERFACE]`) fails the match instead of raising `NoMethodError` when the response holds `null` or a scalar where an array was expected. This affected nested lists too, so an interface element with a scalar in place of a list crashed the example.
32
+ - `Types::URI` is anchored with `\A...\z`. It previously accepted any value that merely contained a URI, so `"see https://example.com for details"` matched. `EMAIL` and `UUID` were already anchored. Suites that relied on the substring behaviour will start failing.
33
+ - `match_json_schema` fails with `expected a JSON String to match against the schema, got NilClass` instead of raising `TypeError` when handed `nil`, an already-parsed Hash, or any other non-String.
34
+ - A schema `Proc` that returns something other than an options Hash, or that expects the value as an argument, raises a descriptive `ArgumentError` naming the mistake. Both previously surfaced as a bare `NoMethodError` or `wrong number of arguments` from inside the matcher.
35
+ - An object schema compared against array elements of another shape (`[{ id: Integer }]` against `["a", "b"]`) fails instead of raising `NoMethodError`. This is the same class of bug as the list-schema crash above, on the object comparison path.
36
+ - `expect(body).not_to match_json_schema(schema)` fails when the body is not a JSON String, rather than passing by default. A type error in the spec is now a failure whichever way the expectation is written.
37
+ - A non-lambda `proc { |value| ... }` used as a schema Proc is rejected alongside the lambda form. Ruby reports a non-lambda block parameter as optional, so the check reads the parameter list rather than the arity; a Proc declaring an optional parameter is rejected for the same reason.
38
+ - The `have_no_content` specs gave every body its own context. A repeated `let(:actual)` in one context meant the `"{}"` case never ran.
39
+
40
+ ### Changed
41
+ - The released gem contains the tracked files under `lib/` plus the licence, the README and the CHANGELOG, and nothing else. It previously packaged the repository's own tooling: the CI workflow, the RuboCop config, the Gemfile and lockfile, the Rakefile, `bin/` and `gemfiles/`. Scoping the file list to tracked paths also keeps an untracked local file in `lib/` out of a release.
42
+ - README regex examples anchor with `\A` and `\z` instead of `^` and `$`, with a note explaining that the line-boundary anchors let a multi-line value satisfy a schema.
43
+ - Updated the locked development dependencies past every advisory `bundler-audit` reported: rack, nokogiri, railties, activesupport, concurrent-ruby, loofah, crass, erb, json, rails-html-sanitizer and rack-session.
44
+
45
+ ### Added
46
+ - Exact-array schemas dispatch each element the same way any other schema value is dispatched, so `[Integer, Integer]` is a fixed-length tuple and `[RSpec::JsonApi::Types::URI]` type-checks its single element. Elements were previously compared with `==`, so a Class, Regexp or Proc in that position could never match.
47
+ - A `bundler-audit` job in CI, a Dependabot config for bundler and github-actions, and `permissions: contents: read` on the workflow.
48
+ - `ROADMAP.md`, the prioritised findings from a full review of the codebase.
2
49
 
3
50
  ## [1.5.0] - 2026-06-05
4
51
 
5
52
  ### Added
6
- - CI compatibility matrix across Ruby 3.2–3.4 and Rails 6.1, 7.1, 7.2, 8.0 and 8.1 (`gemfiles/` + GitHub Actions matrix), so the advertised version support is actually tested.
53
+ - CI compatibility matrix across Ruby 3.2-3.4 and Rails 6.1, 7.1, 7.2, 8.0 and 8.1 (`gemfiles/` + GitHub Actions matrix), so the advertised version support is actually tested.
7
54
  - `RSpec::JsonApi::Constraints` module encapsulating the schema `Proc` options DSL.
8
55
  - `RSpec::JsonApi::SchemaMatch` as the single comparison entry point, and `RSpec::JsonApi::Traversal` for the internal structural helpers.
9
56
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,24 @@
1
+ # Contributing
2
+
3
+ Bug reports and pull requests are welcome.
4
+
5
+ ## Setup
6
+
7
+ Use the Ruby version declared in `.ruby-version` and Bundler 4.0.4:
8
+
9
+ ```sh
10
+ gem install bundler -v 4.0.4
11
+ bundle install
12
+ ```
13
+
14
+ Run the local checks before opening a pull request:
15
+
16
+ ```sh
17
+ bundle exec rspec
18
+ bundle exec rubocop
19
+ bundle exec bundle-audit check --update
20
+ ```
21
+
22
+ Matcher changes should include focused examples under `spec/rspec/json_api`. Generator changes should include examples under `spec/generators` and be checked against both appraisal gemfiles.
23
+
24
+ Keep pull requests focused and explain user-visible behavior changes in `CHANGELOG.md`. By participating, you agree to follow the [code of conduct](CODE_OF_CONDUCT.md).
data/README.md CHANGED
@@ -1,319 +1,226 @@
1
1
  # RSpec::JsonApi
2
2
 
3
- [RSpec:JsonAPI](https://github.com/nomtek/rspec-json_api)
4
- is an extension for [RSpec](https://github.com/rspec)
5
- to easily allow testing JSON API responses.
3
+ `rspec-json_api` adds RSpec matchers for checking JSON values against compact Ruby schemas. Despite the name, it validates general JSON response shapes; it does not implement the [JSON:API specification](https://jsonapi.org/).
4
+
5
+ ## Requirements
6
+
7
+ - Ruby 3.2 or newer
8
+ - RSpec 3
9
+ - Rails 6.1 or newer only when using the optional generators
10
+
11
+ The CI suite covers matcher behavior on Ruby 3.2, 3.3, 3.4, and 4.0. Generator integration is exercised at the supported Rails boundaries: Rails 6.1 on Ruby 3.2 and Rails 8.1 on Ruby 3.4.
6
12
 
7
13
  ## Installation
8
14
 
9
- Add this line to your application's Gemfile:
15
+ Add the gem to your test group:
10
16
 
11
17
  ```ruby
12
- gem 'rspec-json_api'
18
+ group :test do
19
+ gem "rspec-json_api"
20
+ end
13
21
  ```
14
22
 
15
- And then execute:
23
+ Then run:
16
24
 
17
- $ bundle install
25
+ ```sh
26
+ bundle install
27
+ ```
28
+
29
+ Load the matchers from `spec/spec_helper.rb` (or your equivalent RSpec setup file):
18
30
 
19
- Or install it yourself as:
31
+ ```ruby
32
+ require "rspec/json_api"
33
+ ```
20
34
 
21
- $ gem install rspec-json_api
35
+ Rails projects can generate the definition directories:
22
36
 
23
- Generate directory tree:
37
+ ```sh
38
+ rails generate rspec:json_api:install
39
+ ```
24
40
 
25
- rails generate rspec:json_api:install
41
+ Load custom types before interfaces in `rails_helper.rb`, because interfaces may reference types:
26
42
 
27
- Require gem assets in your `rails_helper.rb`
28
43
  ```ruby
29
- Dir[File.join(__dir__, 'rspec', 'json_api', 'types', '*.rb')].each { |file| require file }
30
- Dir[File.join(__dir__, 'rspec', 'json_api', 'interfaces', '*.rb')].each { |file| require file }
44
+ Dir[File.join(__dir__, "rspec", "json_api", "types", "*.rb")].each { |file| require file }
45
+ Dir[File.join(__dir__, "rspec", "json_api", "interfaces", "*.rb")].each { |file| require file }
31
46
  ```
32
47
 
33
- ## Generators
48
+ The matchers themselves do not depend on Rails, ActiveSupport, or `rspec-rails`.
34
49
 
35
- Using build-in generators it's possible to create custom interface and type.
50
+ ## Matchers
36
51
 
37
- Generate new template:
52
+ ### `match_json_schema`
38
53
 
39
- rails generate rspec:json_api:interface interface-name
54
+ Pass the matcher a JSON String and describe the parsed value with Ruby values, classes, regular expressions, arrays, hashes, or constraint Procs:
40
55
 
41
- Generate new type:
56
+ ```ruby
57
+ schema = {
58
+ id: RSpec::JsonApi::Types::UUID,
59
+ name: String,
60
+ age: -> { { type: Integer, min: 18 } },
61
+ tags: [String]
62
+ }
42
63
 
43
- rails generate rspec:json_api:type type-name
64
+ expect(response.body).to match_json_schema(schema)
65
+ ```
44
66
 
67
+ The actual value must be a JSON String. Invalid JSON and non-String inputs fail the match. Schema keys must be symbols because JSON object keys are symbolized while parsing.
45
68
 
46
- ## Example usage
69
+ Object schemas are strict at every nesting level: every expected key must be present and unexpected keys fail the match. `allow_blank` permits a blank value; it does not make a key optional.
70
+
71
+ Root schemas may describe objects, arrays, or scalar JSON values:
47
72
 
48
73
  ```ruby
49
- # spec/controllers/users_controller_spec.rb
74
+ expect('"ready"').to match_json_schema(String)
75
+ expect('"ready"').to match_json_schema(/\Aready\z/)
76
+ expect("42").to match_json_schema(42)
77
+ ```
50
78
 
51
- RSpec.describe UsersController, type: :controller do
52
- describe '#index' do
53
- let(:expected_schema) do
54
- [{
55
- id: RSpec::JsonApi::Types::UUID,
56
- name: String,
57
- age: Integer,
58
- favoriteColorHex: /^\#([a-fA-F]|[0-9]){3,6}$/,
59
- number: -> { { type: Integer, min: 10, max: 20, lambda: lambda(&:even?) } }
60
- }]
61
- end
79
+ ### `have_no_content`
62
80
 
63
- it 'matches API response' do
64
- get :index
81
+ `have_no_content` matches only an empty String:
65
82
 
66
- expect(response.body).to match_json_schema(expected_schema)
67
- end
68
- end
69
-
70
- describe '#update' do
71
- it 'matches API response' do
72
- put :update, params: { name: 'John', age: 35 }
73
-
74
- expect(response.body).to have_no_content
75
- end
76
- end
77
- end
83
+ ```ruby
84
+ expect(response.body).to have_no_content
78
85
  ```
79
86
 
80
- ## Built-in matchers
81
- - ### match_json_schema
82
- ```
83
- expect(response.body).to match_json_schema(expected_schema)
84
- ```
87
+ JSON objects, JSON arrays, and whitespace-only bodies are considered content.
85
88
 
86
- - ### have_no_content
87
- ```
88
- expect(response.body).to have_no_content
89
+ ## Schema Values
90
+
91
+ ### Exact values
92
+
93
+ ```ruby
94
+ schema = { status: "ready", count: 2 }
89
95
  ```
90
96
 
91
- ## Interfaces
92
- The gem introduces interfaces to reuse them during test matches.
97
+ ### Classes
98
+
99
+ Classes use `instance_of?`, so subclasses do not match:
93
100
 
94
101
  ```ruby
95
- # spec/rspec/json_api/interfaces/example_interface.rb
102
+ schema = { id: Integer, name: String }
103
+ ```
96
104
 
97
- module RSpec
98
- module JsonApi
99
- module Interfaces
100
- EXAMPLE_INTERFACE = {
101
- id: Types::UUID,
102
- name: String,
103
- number: Integer,
104
- color: -> { { inclusion: %w[black red white], allow_blank: true } }
105
- }.freeze
106
- end
107
- end
108
- end
105
+ ### Regular expressions
106
+
107
+ A regular expression matches only a JSON String. Numbers, booleans, and `null` do not match after conversion:
108
+
109
+ ```ruby
110
+ schema = { color: /\A#[0-9a-fA-F]{6}\z/ }
109
111
  ```
110
- _Note: You can either generate file on your own or use generator._
111
- ## Types
112
112
 
113
- The gem allow users either to user build-in types or define owns.
114
- ### Build-in types
115
- - #### EMAIL
113
+ Use `\A` and `\z` for whole-string validation. Ruby's `^` and `$` are line anchors and may accept a matching line inside a multiline value.
114
+
115
+ ### Arrays
116
+
117
+ Array schemas have three forms:
118
+
116
119
  ```ruby
117
- RSpec::JsonApi::Types::EMAIL
120
+ [String] # any-length list of Strings
121
+ [{ id: Integer, name: String }] # any-length list of this object shape
122
+ [Integer, String] # an exact two-element tuple
118
123
  ```
119
- - #### URI
124
+
125
+ The one-element shorthand applies only to a Class or Hash. For example, `[Types::UUID]` means an exact one-element array because `Types::UUID` is a Regexp.
126
+
127
+ ### Constraint Procs
128
+
129
+ A constraint Proc takes no arguments and returns an options Hash:
130
+
120
131
  ```ruby
121
- RSpec::JsonApi::Types::URI
132
+ schema = {
133
+ age: -> { { type: Integer, min: 18, max: 120 } },
134
+ role: -> { { inclusion: %w[admin member] } },
135
+ code: -> { { regex: /\A[A-Z]{3}\z/ } },
136
+ even: -> { { lambda: ->(value) { value.even? } } },
137
+ nickname: -> { { type: String, allow_blank: true } }
138
+ }
122
139
  ```
123
- - #### UUID
140
+
141
+ Supported options are `allow_blank`, `type`, `value`, `min`, `max`, `inclusion`, `regex`, and `lambda`. All supplied constraints must pass. Unknown options, a non-Hash return value, or a Proc that declares an argument raises `ArgumentError` with usage guidance.
142
+
143
+ `allow_blank: true` accepts `null`, `false`, empty strings, whitespace-only strings, empty arrays, and empty objects. The key itself remains required.
144
+
145
+ ## Built-in Types
146
+
147
+ The built-in types are anchored regular expressions:
148
+
124
149
  ```ruby
150
+ RSpec::JsonApi::Types::EMAIL
151
+ RSpec::JsonApi::Types::URI
125
152
  RSpec::JsonApi::Types::UUID
126
153
  ```
127
154
 
155
+ `URI` accepts schemes supported by Ruby's standard URI parser, not only HTTP and HTTPS.
128
156
 
129
- Custom type example:
130
- ```ruby
131
- # spec/rspec/json_api/types/color_hex.rb
157
+ Generate a custom type with Rails:
158
+
159
+ ```sh
160
+ rails generate rspec:json_api:type color_hex
161
+ ```
162
+
163
+ Or define one directly:
132
164
 
165
+ ```ruby
133
166
  module RSpec
134
167
  module JsonApi
135
168
  module Types
136
- COLOR_HEX = /^#(?:[0-9a-fA-F]{3}){1,2}$/
169
+ COLOR_HEX = /\A#(?:[0-9a-fA-F]{3}){1,2}\z/
137
170
  end
138
171
  end
139
172
  end
140
-
141
- RSpec::JsonApi::Types::COLOR_HEX
142
173
  ```
143
174
 
144
- _Note: You can either generate file on your own or use generator._
145
- ## Matching methods
146
- The gem offers variety of possible matching methods.
175
+ ## Interfaces
147
176
 
148
- ### Presumptions
149
- - `match_json_schema` always require full keys match.
177
+ Interfaces are reusable strict object schemas:
150
178
 
151
- Failure Example:
152
- ```ruby
153
- let(:expected_schema) do
154
- {
155
- id: RSpec::JsonApi::Types::UUID,
179
+ ```ruby
180
+ module RSpec
181
+ module JsonApi
182
+ module Interfaces
183
+ PERSON = {
184
+ id: Types::UUID,
156
185
  name: String,
157
- age: Integer
158
- }
159
- end
160
-
161
- let(:actual) do
162
- {
163
- id: "0a2f911f-3767-4cc7-9c19-049f4350e38c",
164
- name: "Mikel",
165
- }
186
+ active: -> { { inclusion: [true, false] } }
187
+ }.freeze
166
188
  end
167
- ```
168
-
169
- Success Example:
170
- ```ruby
171
- let(:expected_schema) do
172
- {
173
- id: RSpec::JsonApi::Types::UUID,
174
- name: String,
175
- age: Integer
176
- }
177
189
  end
178
-
179
- let(:actual) do
180
- {
181
- id: "0a2f911f-3767-4cc7-9c19-049f4350e38c",
182
- name: "John",
183
- age: 24
184
- }
185
- end
186
- ```
187
-
188
- ### Value match
189
- ```ruby
190
- let(:expected_schema) do
191
- {
192
- id: "e0067346-4d24-4aa6-b303-f927a410a001",
193
- name: "John",
194
- age: 24,
195
- favoriteColorHex: "#FF5733"
196
- }
197
190
  end
198
191
  ```
199
192
 
200
- ### Class match
201
- ```ruby
202
- let(:expected_schema) do
203
- {
204
- id: Integer,
205
- name: String,
206
- age: Integer,
207
- notes: [String]
208
- }
209
- end
210
- ```
193
+ Generate one with:
211
194
 
212
- ### Type match
213
- ```ruby
214
- let(:expected_schema) do
215
- {
216
- id: RSpec::JsonApi::Types::UUID,
217
- email: RSpec::JsonApi::Types::EMAIL,
218
- }
219
- end
195
+ ```sh
196
+ rails generate rspec:json_api:interface person
220
197
  ```
221
198
 
222
- ### Regexp match
223
- ```ruby
224
- let(:expected_schema) do
225
- {
226
- color: /^\#([a-fA-F]|[0-9]){3,6}$/
227
- }
228
- end
229
- ```
199
+ Use an interface directly or as a homogeneous list schema:
230
200
 
231
- ### Interface match
232
201
  ```ruby
233
- let(:expected_schema) do
234
- [RSpec::JsonApi::Interfaces::PERSON]
235
- end
202
+ expect(response.body).to match_json_schema(RSpec::JsonApi::Interfaces::PERSON)
203
+ expect(response.body).to match_json_schema([RSpec::JsonApi::Interfaces::PERSON])
236
204
  ```
237
205
 
238
- ### Proc match
239
- Proc match allows to customize schema according needs using lambda shorthand notation `->`
206
+ ## Development
240
207
 
241
- Supported options:
242
- - #### type
243
- ```ruby
244
- let(:expected_schema) do
245
- {
246
- name: -> { { type: String } }
247
- }
248
- end
249
- ```
250
- - #### value
251
- ```ruby
252
- let(:expected_schema) do
253
- {
254
- name: -> { { value: "John" } }
255
- }
256
- end
257
- ```
258
- - #### min
259
- ```ruby
260
- let(:expected_schema) do
261
- {
262
- age: -> { { min: 15 } }
263
- }
264
- end
265
- ```
266
- - #### max
267
- ```ruby
268
- let(:expected_schema) do
269
- {
270
- age: -> { { max: 25 } }
271
- }
272
- end
273
- ```
274
- - #### inclusion
275
- ```ruby
276
- let(:expected_schema) do
277
- {
278
- letter: -> { { inclusion: %w[A B C] } }
279
- }
280
- end
281
- ```
282
- - #### regex
283
- ```ruby
284
- let(:expected_schema) do
285
- {
286
- hex: -> { { regex: /^\#([a-fA-F]|[0-9]){3,6}$/ } }
287
- }
288
- end
289
- ```
290
- - #### lambda
291
- ```ruby
292
- let(:expected_schema) do
293
- {
294
- number: -> { { lambda: lambda(&:even?) } }
295
- }
296
- end
297
- ```
298
- - #### allow_blank
208
+ Use the Ruby version in `.ruby-version` and Bundler 4.0.4:
299
209
 
300
- ```ruby
301
- let(:expected_schema) do
302
- {
303
- name: -> { { type: String, allow_blank: true } }
304
- }
305
- end
210
+ ```sh
211
+ gem install bundler -v 4.0.4
212
+ bundle install
213
+ bundle exec rspec
214
+ bundle exec rubocop
215
+ bundle exec bundle-audit check --update
306
216
  ```
307
- _Note: Default value is `false`_
308
-
309
- ## Contributing
310
217
 
311
- Bug reports and pull requests are welcome on GitHub at https://github.com/nomtek/rspec-json_api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nomtek/rspec-json_api/blob/master/CODE_OF_CONDUCT.md).
218
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for compatibility and contribution guidance. Please report vulnerabilities using [GitHub's private security advisory form](https://github.com/nomtek/rspec-json_api/security/advisories/new), as described in [SECURITY.md](SECURITY.md).
312
219
 
313
220
  ## License
314
221
 
315
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
222
+ The gem is available under the terms of the [MIT License](LICENSE.txt).
316
223
 
317
224
  ## Code of Conduct
318
225
 
319
- Everyone interacting in the RSpec::JsonApi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nomtek/rspec-json_api/blob/master/CODE_OF_CONDUCT.md).
226
+ Everyone participating in this project is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
data/SECURITY.md ADDED
@@ -0,0 +1,9 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Security fixes are made on the latest released version.
6
+
7
+ ## Reporting a Vulnerability
8
+
9
+ Please do not open a public issue for a suspected vulnerability. Use [GitHub's private security advisory form](https://github.com/nomtek/rspec-json_api/security/advisories/new) and include reproduction steps, affected versions, and the expected impact.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module JsonApi
5
+ module Blank
6
+ module_function
7
+
8
+ BLANK_STRING = /\A[[:space:]]*\z/
9
+
10
+ def blank?(value)
11
+ case value
12
+ when nil, false
13
+ true
14
+ when String
15
+ BLANK_STRING.match?(value)
16
+ else
17
+ value.respond_to?(:empty?) && value.empty?
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -21,7 +21,7 @@ module RSpec
21
21
  def match(value, options)
22
22
  validate!(options)
23
23
 
24
- return true if value.blank? && options[:allow_blank]
24
+ return true if Blank.blank?(value) && options[:allow_blank]
25
25
 
26
26
  options.except(:allow_blank).all? do |option, condition|
27
27
  satisfies?(value, option, condition)
@@ -29,6 +29,8 @@ module RSpec
29
29
  end
30
30
 
31
31
  def validate!(options)
32
+ raise ArgumentError, "options must be a Hash, got #{options.class}" unless options.is_a?(Hash)
33
+
32
34
  unknown = options.keys - SUPPORTED_OPTIONS
33
35
  return if unknown.empty?
34
36
 
@@ -40,7 +42,7 @@ module RSpec
40
42
  when :type then value.instance_of?(condition)
41
43
  when :value then value == condition
42
44
  when :inclusion then condition.include?(value)
43
- when :regex then condition.match?(value.to_s)
45
+ when :regex then matches_regexp?(value, condition)
44
46
  when :lambda then condition.call(value)
45
47
  when :min, :max then within_bound?(value, option, condition)
46
48
  end
@@ -51,6 +53,10 @@ module RSpec
51
53
 
52
54
  option == :min ? value >= condition : value <= condition
53
55
  end
56
+
57
+ def matches_regexp?(value, condition)
58
+ value.is_a?(String) && condition.match?(value)
59
+ end
54
60
  end
55
61
  end
56
62
  end