rspec-rest 0.3.0 → 0.4.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: 47e2dfd9b5b01a159c9d81dffc650b0a807d6491669100f3e4c0939b9012eb3e
4
- data.tar.gz: 0b5f6d62d08a5139fa60ce2d2a193d92949193767a08378f5212e576bc762f50
3
+ metadata.gz: e72f8991e5d53bdb5bd0ccfa7524a42fcf5c99afead0caba45bb8876ef66bf8c
4
+ data.tar.gz: c33cdc064fc0d12c76f42f036b1490732ecbd6aaa1c6c8213bfaef6260e99d03
5
5
  SHA512:
6
- metadata.gz: ca75926bef46af5c64380a992281242afada213a4e4975ef41f0e1e8c525395c41a88618fd04accec24613cb13b107a8a0ae04b25a4e32cb4ded5464e6093760
7
- data.tar.gz: f9762615e58277d690c7cdc7b7426dac3818e3f1fe7f12eb6e3e753d9cf0e19d366a7eae198911b5f33d7911ef72ded6826dabe63dd5b66bde19f98c7954fd87
6
+ metadata.gz: 240876e213f49ac41ddc68015bd1d91744df3af41454e53f4cd561a10042f839544fae0ac7537dd3eee68f4c76727e9f1ff07136b8a1e060fcbc068e401aca69
7
+ data.tar.gz: d9c09b903fe84ef94c2290633b1770fd0d05263a909ffa4a9d2aeec6c8aa26e8a7e26dbff6622073d6bf99b8d24b988898a5040a12b7dc4b19a09981c78b02fd
data/.gitignore CHANGED
@@ -70,3 +70,6 @@ yarn-debug.log*
70
70
 
71
71
  # Local planning notes
72
72
  /docs/plans/
73
+
74
+ # Built gem artifacts
75
+ /rspec-rest-*.gem
data/CHANGELOG.md CHANGED
@@ -7,7 +7,18 @@ Semantic Versioning.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- No changes yet.
10
+ ## [0.4.0] - 2026-03-11
11
+
12
+ ### Added
13
+ - Verb DSL now supports keyword request paths:
14
+ - `get path: "/users", description: "..." do ... end`
15
+ - same keyword `path:` support for `post`, `put`, `patch`, and `delete`.
16
+
17
+ ### Deprecated
18
+ - Positional verb path arguments are deprecated and scheduled for removal in `1.0`:
19
+ - `get "/users", description: "..." do ... end`
20
+ Use keyword paths instead:
21
+ - `get path: "/users", description: "..." do ... end`
11
22
 
12
23
  ## [0.3.0] - 2026-03-10
13
24
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-rest (0.3.0)
4
+ rspec-rest (0.4.0)
5
5
  rack-test (~> 2.1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -48,13 +48,13 @@ RSpec.describe "Users API" do
48
48
  end
49
49
 
50
50
  resource "/users" do
51
- get "/" do
51
+ get path: "/" do
52
52
  expect_status 200
53
53
  expect_header "Content-Type", "application/json"
54
54
  expect_json array_of(hash_including("id" => integer, "email" => string))
55
55
  end
56
56
 
57
- post "/" do
57
+ post path: "/" do
58
58
  json "email" => "carl@example.com", "name" => "Carl"
59
59
  expect_status 201
60
60
  capture :user_id, "$.id"
@@ -112,7 +112,7 @@ RSpec.describe "Posts API" do
112
112
  resource "/posts" do
113
113
  with_auth auth_token
114
114
 
115
- get "/", description: "returns posts page 1" do
115
+ get path: "/", description: "returns posts page 1" do
116
116
  query page: 1, per_page: 10
117
117
 
118
118
  expect_status 200
@@ -152,14 +152,14 @@ RSpec.describe "Posts API" do
152
152
  resource "/posts" do
153
153
  with_auth auth_token
154
154
 
155
- get "/" do
155
+ get path: "/" do
156
156
  query page: 1, per_page: 10
157
157
  expect_status 200
158
158
  expect_json array_of(contract(:post_summary))
159
159
  expect_page_size 10
160
160
  end
161
161
 
162
- get "/{id}" do
162
+ get path: "/{id}" do
163
163
  path_params id: 999_999
164
164
  expect_error status: 404, message: "Post not found"
165
165
  end
@@ -168,7 +168,7 @@ RSpec.describe "Posts API" do
168
168
  resource "/uploads" do
169
169
  with_auth auth_token
170
170
 
171
- post "/" do
171
+ post path: "/" do
172
172
  multipart!
173
173
  file :file, Rails.root.join("spec/fixtures/files/sample_upload.txt"), content_type: "text/plain"
174
174
  expect_status 201
@@ -206,16 +206,17 @@ Supported config:
206
206
 
207
207
  - `resource "/users" do ... end`
208
208
  - `get`, `post`, `put`, `patch`, `delete`
209
- - preferred description form: `get(path, description: "...") { ... }`
210
- - legacy positional form `get(path, "description")` is deprecated and will be removed in `1.0`.
209
+ - preferred form: `get(path: "/users", description: "...") { ... }`
210
+ - legacy positional path form `get("/users", description: "...")` is deprecated and will be removed in `1.0`.
211
211
  It is deprecated to avoid `Rails/HttpPositionalArguments` false-positives in RuboCop.
212
+ - legacy positional description form `get "/users", "description"` is deprecated and will be removed in `1.0`.
212
213
 
213
214
  Resource paths are composable and support placeholders:
214
215
 
215
216
  ```ruby
216
217
  resource "/users" do
217
218
  resource "/{id}/posts" do
218
- get "/" do
219
+ get path: "/" do
219
220
  path_params id: 1
220
221
  expect_status 404
221
222
  end
@@ -227,7 +228,7 @@ Example with an explicit behavior name:
227
228
 
228
229
  ```ruby
229
230
  resource "/users" do
230
- get "/", description: "returns public users for authenticated client" do
231
+ get path: "/", description: "returns public users for authenticated client" do
231
232
  expect_status 200
232
233
  end
233
234
  end
@@ -243,9 +244,8 @@ Note: Example names (including `base_path`) are composed when the verb macro is
243
244
  `rspec-rest` verbs (`get`, `post`, etc.) define examples via DSL macros, and can trigger
244
245
  false-positives in some RuboCop cops:
245
246
 
246
- - `Rails/HttpPositionalArguments`: use keyword descriptions (`description:`), not positional descriptions.
247
+ - `Rails/HttpPositionalArguments`: use keyword paths (`path:`) and keyword descriptions (`description:`), not positional arguments.
247
248
  - `RSpec/EmptyExampleGroup`: a `context` that only contains `resource` + verb DSL may be flagged as empty.
248
-
249
249
  Recommended mitigation is scoped configuration for files that use `rspec-rest`:
250
250
 
251
251
  ```yaml
@@ -261,7 +261,7 @@ If you only have a few affected groups, use an inline disable around the DSL gro
261
261
  # rubocop:disable RSpec/EmptyExampleGroup
262
262
  context "when authenticated" do
263
263
  resource "/posts" do
264
- get "/", description: "returns posts" do
264
+ get path: "/", description: "returns posts" do
265
265
  expect_status 200
266
266
  end
267
267
  end
@@ -303,12 +303,12 @@ resource "/posts" do
303
303
  with_auth ENV.fetch("API_TOKEN", "token-123")
304
304
  with_headers "X-Client" => "mobile"
305
305
 
306
- get "/" do
306
+ get path: "/" do
307
307
  query page: 2
308
308
  expect_status 200
309
309
  end
310
310
 
311
- get "/admin" do
311
+ get path: "/admin" do
312
312
  header "X-Client", "internal-tool" # request-level override
313
313
  query locale: "fr" # request-level override
314
314
  expect_status 200
@@ -333,7 +333,7 @@ Inside verb blocks:
333
333
  Example:
334
334
 
335
335
  ```ruby
336
- post "/" do
336
+ post path: "/" do
337
337
  headers "X-Trace-Id" => "abc-123"
338
338
  bearer "token-123"
339
339
  query include_details: "true"
@@ -345,7 +345,7 @@ end
345
345
  Multipart upload example:
346
346
 
347
347
  ```ruby
348
- post "/uploads" do
348
+ post path: "/uploads" do
349
349
  multipart!
350
350
  file :file, Rails.root.join("spec/fixtures/files/sample_upload.txt"), content_type: "text/plain"
351
351
  expect_status 201
@@ -404,7 +404,7 @@ expect_json_last { |item| expect(item["id"]).to integer }
404
404
  `expect_error` is a convenience helper for common API error payload assertions:
405
405
 
406
406
  ```ruby
407
- get "/{id}" do
407
+ get path: "/{id}" do
408
408
  path_params id: 999
409
409
  expect_error status: 404, message: "Post not found"
410
410
  end
@@ -413,7 +413,7 @@ end
413
413
  Pagination helpers:
414
414
 
415
415
  ```ruby
416
- get "/" do
416
+ get path: "/" do
417
417
  query page: 2, per_page: 10
418
418
  expect_status 200
419
419
  expect_page_size 10
@@ -436,7 +436,7 @@ contract :post_summary do
436
436
  )
437
437
  end
438
438
 
439
- get "/" do
439
+ get path: "/" do
440
440
  expect_status 200
441
441
  expect_json array_of(contract(:post_summary))
442
442
  end
@@ -465,7 +465,7 @@ Selector syntax (minimal JSON selector):
465
465
  Example:
466
466
 
467
467
  ```ruby
468
- post "/" do
468
+ post path: "/" do
469
469
  json "email" => "flow@example.com", "name" => "Flow"
470
470
  expect_status 201
471
471
  capture :user_id, "$.id"
@@ -117,11 +117,44 @@ module RSpec
117
117
  end
118
118
  end
119
119
 
120
+ module PathArgumentSupport
121
+ private
122
+
123
+ def warn_on_deprecated_positional_path(_method)
124
+ Deprecation.warn(
125
+ key: :verb_positional_path,
126
+ message: "Positional request paths (for example: get(\"/users\")) are deprecated and will be " \
127
+ "removed in 1.0. Use keyword paths instead (for example: get(path: \"/users\")). " \
128
+ "This avoids RuboCop Rails/HttpPositionalArguments false-positives."
129
+ )
130
+ end
131
+
132
+ def resolve_path_options(method:, positional_path:, keyword_path:)
133
+ if !positional_path.nil? && !keyword_path.nil?
134
+ raise ArgumentError,
135
+ "#{method}(...) received both positional and keyword paths. Use only `path:`."
136
+ end
137
+
138
+ effective_path = keyword_path.nil? ? positional_path : keyword_path
139
+ if effective_path.nil?
140
+ raise ArgumentError,
141
+ "#{method}(...) requires a request path. Pass it as `path:` (preferred) " \
142
+ "or as the first positional argument."
143
+ end
144
+
145
+ {
146
+ path: effective_path,
147
+ using_positional_path: !positional_path.nil? && keyword_path.nil?
148
+ }
149
+ end
150
+ end
151
+
120
152
  module ClassMethods
121
153
  include ClassLevelContracts
122
154
  include ClassLevelPresets
123
155
  include RouteNamingSupport
124
156
  include DescriptionArgumentSupport
157
+ include PathArgumentSupport
125
158
 
126
159
  def api(&)
127
160
  builder = ApiConfigBuilder.new(rest_config)
@@ -141,25 +174,28 @@ module RSpec
141
174
  end
142
175
 
143
176
  HTTP_METHODS.each do |method|
144
- define_method(method) do |path, positional_description = nil, description: nil, &block|
145
- description_options = resolve_verb_description_options(method, positional_description, description)
177
+ define_method(method) do |positional_path = nil, positional_description = nil, path: nil,
178
+ description: nil, &block|
179
+ path_options, description_options = resolve_verb_options(
180
+ method, positional_path, path, positional_description, description
181
+ )
146
182
  resource_path = current_resource_path
147
183
  request_presets = deep_dup_presets(current_request_presets)
148
184
  example_name = build_example_name(
149
185
  method: method,
150
- path: path,
186
+ path: path_options[:path],
151
187
  resource_path: resource_path,
152
188
  description: description_options[:description]
153
189
  )
154
190
  it(example_name) do
155
191
  start_rest_request(
156
192
  method: method,
157
- path: path,
193
+ path: path_options[:path],
158
194
  resource_path: resource_path,
159
195
  presets: request_presets
160
196
  )
161
197
  instance_eval(&block) if block
162
- self.class.send(:emit_positional_description_warning, method, description_options)
198
+ self.class.send(:emit_verb_deprecation_warnings, method, path_options, description_options)
163
199
  execute_rest_request_if_pending
164
200
  end
165
201
  end
@@ -181,12 +217,31 @@ module RSpec
181
217
 
182
218
  private
183
219
 
220
+ def emit_verb_deprecation_warnings(method, path_options, description_options)
221
+ emit_positional_path_warning(method, path_options)
222
+ emit_positional_description_warning(method, description_options)
223
+ end
224
+
225
+ def emit_positional_path_warning(method, path_options)
226
+ return unless path_options[:using_positional_path]
227
+
228
+ send(:warn_on_deprecated_positional_path, method)
229
+ end
230
+
184
231
  def emit_positional_description_warning(method, description_options)
185
232
  return unless description_options[:using_positional_description]
186
233
 
187
234
  send(:warn_on_deprecated_positional_description, method)
188
235
  end
189
236
 
237
+ def resolve_verb_path_options(method, positional_path, path)
238
+ resolve_path_options(
239
+ method: method,
240
+ positional_path: positional_path,
241
+ keyword_path: path
242
+ )
243
+ end
244
+
190
245
  def resolve_verb_description_options(method, positional_description, description)
191
246
  resolve_description_options(
192
247
  method: method,
@@ -194,6 +249,13 @@ module RSpec
194
249
  keyword_description: description
195
250
  )
196
251
  end
252
+
253
+ def resolve_verb_options(method, positional_path, path, positional_description, description)
254
+ [
255
+ resolve_verb_path_options(method, positional_path, path),
256
+ resolve_verb_description_options(method, positional_description, description)
257
+ ]
258
+ end
197
259
  end
198
260
 
199
261
  module InstanceMethods
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module Rest
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-10 00:00:00.000000000 Z
11
+ date: 2026-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack-test