jsonrpc-middleware 0.2.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: ea48adea36c2a7f311bb0b5c8d1868999f9ec7b6530a2b933600f8433655b63c
4
- data.tar.gz: 8e4abe926bb02da6155650d2296d5df0b07562de6ce08163ad433df09ac40e1d
3
+ metadata.gz: a45b1d353d7fd1547e7e984edba9c9ed1e02669914c00294d08f259cb5bb76e6
4
+ data.tar.gz: 25d2b7802a03378c90e181d5e5b580489299d41a3070817d762b43063a9d68b3
5
5
  SHA512:
6
- metadata.gz: 90b63747650ce423ae7f5f881db75c0c8fceabc05e627fa0fd6647cd6b67c1445eab3a09dfc19cac3b5d62d9814e9a692b96e50e0bbdc41bd8b83d9d3582b6bc
7
- data.tar.gz: 64c40bf854999ce3c0cdc8261a921d603ed8ec664c46a6e0d4578afbc9404e693f7b8ff5456385fdcb671867049d4e07028efa51ac48276a5adfd5c4a24b194d
6
+ metadata.gz: c3a417f6096c195940c782cb313f9b4e7a5e6ef2c1c6ed994a4d23e226e7a70ba7df4b635b03c56f392983b45b1d0239f0a121c2b2982101c5ad5c47680bd7f8
7
+ data.tar.gz: 601bc95a7d965e6721af683765881d73ac34fc5ced0791c398b1ddc4acff054b586d0c74ab0107bf139bb177afcebc760460d96cb86896afec1394972b98b6d9
data/.aiexclude CHANGED
@@ -1,3 +1,4 @@
1
+ .kiro
1
2
  .ruby-lsp
2
3
  .vscode
3
4
  coverage
@@ -0,0 +1,561 @@
1
+ # Test
2
+
3
+ Write RSpec tests for a given file, module, class or method. Ensure it meets the guidelines. If the file already has
4
+ tests, simply review the test guidelines.
5
+
6
+ ## Best Practices
7
+
8
+ ### Describe Your Methods
9
+
10
+ Be clear about what method you are describing. For instance, use the Ruby documentation convention of `.` when
11
+ referring to a class method's name and `#` when referring to an instance method's name.
12
+
13
+ **bad**
14
+
15
+ ```ruby
16
+ describe 'the authenticate method for User' do
17
+ end
18
+
19
+ describe 'if the user is an admin' do
20
+ end
21
+ ```
22
+
23
+ **good**
24
+
25
+ ```ruby
26
+ describe '.authenticate' do
27
+ end
28
+
29
+ describe '#admin?' do
30
+ end
31
+ ```
32
+
33
+ ### Use contexts
34
+
35
+ Contexts are a powerful method to make your tests clear and well organized (they keep tests easy to read).
36
+ When describing a context, start its description with 'when', 'with' or 'without'.
37
+
38
+ **bad**
39
+
40
+ ```ruby
41
+ it 'has 200 status code if logged in' do
42
+ expect(response).to respond_with 200
43
+ end
44
+
45
+ it 'has 401 status code if not logged in' do
46
+ expect(response).to respond_with 401
47
+ end
48
+ ```
49
+
50
+ **good**
51
+
52
+ ```ruby
53
+ context 'when logged in' do
54
+ it { is_expected.to respond_with 200 }
55
+ end
56
+
57
+ context 'when logged out' do
58
+ it { is_expected.to respond_with 401 }
59
+ end
60
+ ```
61
+
62
+ ### Keep your description short
63
+
64
+ A spec description should never be longer than 40 characters. If this happens you should split it using a context.
65
+ In the example below, we removed the description related to the status code, which has been replaced by the
66
+ expectation `is_expected`. If you run this test typing `rspec filename` you will obtain a readable output.
67
+
68
+ **bad**
69
+
70
+ ```ruby
71
+ it 'has 422 status code if an unexpected params will be added' do
72
+ ```
73
+
74
+ **good**
75
+
76
+ ```ruby
77
+ context 'when not valid' do
78
+ it { is_expected.to respond_with 422 }
79
+ end
80
+ ```
81
+
82
+ **Formatted output**
83
+
84
+ ```
85
+ when not valid
86
+ it should respond with 422
87
+ ```
88
+
89
+ ### Single expectation test
90
+
91
+ The 'one expectation' tip is more broadly expressed as 'each test should make only one assertion'. This helps you on
92
+ finding possible errors, going directly to the failing test, and to make your code readable. In isolated unit specs,
93
+ you want each example to specify one (and only one) behavior. Multiple expectations in the same example are a signal
94
+ that you may be specifying multiple behaviors.
95
+
96
+ **good (isolated)**
97
+
98
+ ```ruby
99
+ it { is_expected.to respond_with_content_type(:json) }
100
+ it { is_expected.to assign_to(:resource) }
101
+ ```
102
+
103
+ Anyway, in tests that are not isolated (e.g. ones that integrate with a DB, an external webservice, or
104
+ end-to-end-tests), you take a massive performance hit to do the same setup over and over again, just to set a different
105
+ expectation in each test. In these sorts of slower tests, I think it's fine to specify more than one isolated behavior.
106
+
107
+ **good (not isolated)**
108
+
109
+ ```ruby
110
+ it 'creates a resource' do
111
+ expect(response).to respond_with_content_type(:json)
112
+ expect(response).to assign_to(:resource)
113
+ end
114
+ ```
115
+
116
+ ### Test all possible cases
117
+
118
+ Testing is a good practice, but if you do not test the edge cases, it will not be useful. Test valid, edge and invalid
119
+ case. For example, consider the following action.
120
+
121
+ **Destroy Action**
122
+
123
+ ```ruby
124
+ before_action :find_owned_resources
125
+ before_action :find_resource
126
+
127
+ def destroy
128
+ render 'show'
129
+ @consumption.destroy
130
+ end
131
+ ```
132
+
133
+ The error I usually see lies in testing only whether the resource has been removed. But there are at least two edge
134
+ cases: when the resource is not found and when it's not owned. As a rule of thumb think of all the possible inputs
135
+ and test them.
136
+
137
+ **bad**
138
+
139
+ ```ruby
140
+ it 'shows the resource'
141
+ ```
142
+
143
+ **good**
144
+
145
+ ```ruby
146
+ describe '#destroy' do
147
+ context 'when resource is found' do
148
+ it 'responds with 200'
149
+ it 'shows the resource'
150
+ end
151
+
152
+ context 'when resource is not found' do
153
+ it 'responds with 404'
154
+ end
155
+
156
+ context 'when resource is not owned' do
157
+ it 'responds with 404'
158
+ end
159
+ end
160
+ ```
161
+
162
+ ### Expect vs Should syntax
163
+
164
+ On new projects always use the `expect` syntax.
165
+
166
+ **bad**
167
+
168
+ ```ruby
169
+ it 'creates a resource' do
170
+ response.should respond_with_content_type(:json)
171
+ end
172
+ ```
173
+
174
+ **good**
175
+
176
+ ```ruby
177
+ it 'creates a resource' do
178
+ expect(response).to respond_with_content_type(:json)
179
+ end
180
+ ```
181
+
182
+ Configure the RSpec to only accept the new syntax on new projects, to avoid having the 2 syntax all over the place.
183
+
184
+ **good**
185
+
186
+ ```ruby
187
+ # spec_helper.rb
188
+ RSpec.configure do |config|
189
+ # ...
190
+ config.expect_with :rspec do |c|
191
+ c.syntax = :expect
192
+ end
193
+ end
194
+ ```
195
+
196
+ On one line expectations or with implicit subject we should use `is_expected.to`.
197
+
198
+ **bad**
199
+
200
+ ```ruby
201
+ context 'when not valid' do
202
+ it { should respond_with 422 }
203
+ end
204
+ ```
205
+
206
+ **good**
207
+
208
+ ```ruby
209
+ context 'when not valid' do
210
+ it { is_expected.to respond_with 422 }
211
+ end
212
+ ```
213
+
214
+ ### Use subject
215
+
216
+ If you have several tests related to the same subject use `subject{}` to DRY them up.
217
+
218
+ **bad**
219
+
220
+ ```ruby
221
+ it { expect(assigns('message')).to match /it was born in Bellville/ }
222
+ ```
223
+
224
+ **good**
225
+
226
+ ```ruby
227
+ subject { assigns('message') }
228
+
229
+ it { is_expected.to match /it was born in Billville/ }
230
+ ```
231
+
232
+ RSpec has also the ability to use a named subject (learn more about [rspec subject](https://rspec.info/features/3-12/rspec-core/subject/)).
233
+
234
+ **good**
235
+
236
+ ```ruby
237
+ subject(:hero) { Hero.first }
238
+
239
+ it 'carries a sword' do
240
+ expect(hero.equipment).to include 'sword'
241
+ end
242
+ ```
243
+
244
+ ### Use let and let!
245
+
246
+ When you have to assign a variable instead of using a `before` block to create an instance variable, use `let`. Using
247
+ `let` the variable lazy loads only when it is used the first time in the test and get cached until that specific test
248
+ is finished. A really good and deep description of what `let` does can be found in this
249
+ [stackoverflow answer](http://stackoverflow.com/questions/5359558/when-to-use-rspec-let/5359979#5359979).
250
+
251
+ **bad**
252
+
253
+ ```ruby
254
+ describe '#type_id' do
255
+ before { @resource = FactoryBot.create :device }
256
+ before { @type = Type.find @resource.type_id }
257
+
258
+ it 'sets the type_id field' do
259
+ expect(@resource.type_id).to eq(@type.id)
260
+ end
261
+ end
262
+ ```
263
+
264
+ **good**
265
+
266
+ ```ruby
267
+ describe '#type_id' do
268
+ let(:resource) { FactoryBot.create :device }
269
+ let(:type) { Type.find resource.type_id }
270
+
271
+ it 'sets the type_id field' do
272
+ expect(resource.type_id).to eq(type.id)
273
+ end
274
+ end
275
+ ```
276
+
277
+ Use `let` to initialize actions that are lazy loaded to test your specs.
278
+
279
+ **good**
280
+
281
+ ```ruby
282
+ context 'when updates a not existing property value' do
283
+ let(:properties) { { id: Settings.resource_id, value: 'on' } }
284
+
285
+ def update
286
+ resource.properties = properties
287
+ end
288
+
289
+ it 'raises a not found error' do
290
+ expect { update }.to raise_error Mongoid::Errors::DocumentNotFound
291
+ end
292
+ end
293
+ ```
294
+
295
+ Use `let!` if you want to define the variable when the block is defined. This can be useful to populate your database
296
+ to test queries or scopes. Here an example of what let actually is (learn more about
297
+ [rspec let](https://rspec.info/features/3-12/rspec-core/helper-methods/let/)).
298
+
299
+ **Explanation**
300
+
301
+ ```ruby
302
+ # this use of let
303
+ let(:foo) { Foo.new }
304
+
305
+ # is very nearly equivalent to this:
306
+ def foo
307
+ @foo ||= Foo.new
308
+ end
309
+ ```
310
+
311
+ ### Mock or not to mock
312
+
313
+ As general rule do not (over)use mocks and test real behavior when possible, as testing real cases is useful when
314
+ validating your application flow.
315
+
316
+ **good**
317
+
318
+ ```ruby
319
+ # simulate a not found resource
320
+ context 'when not found' do
321
+ before do
322
+ allow(Resource).to receive(:where).with(created_from: params[:id]).and_return(false)
323
+ end
324
+
325
+ it { is_expected.to respond_with 404 }
326
+ end
327
+ ```
328
+
329
+ Mocking makes your specs faster but they are difficult to use. You need to understand them well to use them well. Read
330
+ [this article](https://web.archive.org/web/20220612005103/http://myronmars.to/n/dev-blog/2012/06/thoughts-on-mocking)
331
+ to learn more about mocks.
332
+
333
+ ### Create only the data you need
334
+
335
+ If you have ever worked in a medium size project (but also in small ones), test suites can be heavy to run. To solve
336
+ this problem, it's important not to load more data than needed. Also, if you think you need dozens of records, you are
337
+ probably wrong.
338
+
339
+ **good**
340
+
341
+ ```ruby
342
+ describe 'User' do
343
+ describe '.top' do
344
+ before { FactoryBot.create_list(:user, 3) }
345
+
346
+ it { expect(User.top(2)).to have(2).items }
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### Use factories and not fixtures
352
+
353
+ This is an old topic, but it's still good to remember it. Do not use fixtures because they are difficult to control,
354
+ use factories instead. Use them to reduce the verbosity on creating new data (learn about
355
+ [Factory Bot](https://github.com/thoughtbot/factory_bot)).
356
+
357
+ **bad**
358
+
359
+ ```ruby
360
+ user = User.create(
361
+ name: 'Genoveffa',
362
+ surname: 'Piccolina',
363
+ city: 'Billyville',
364
+ birth: '17 August 1982',
365
+ active: true
366
+ )
367
+ ```
368
+
369
+ **good**
370
+
371
+ ```ruby
372
+ user = FactoryBot.create :user
373
+ ```
374
+
375
+ One important note. When talking about unit tests the best practice would be to use neither fixtures or factories. Put
376
+ as much of your domain logic in libraries that can be tested without needing complex, time consuming setup with either
377
+ factories or fixtures. Read more in
378
+ [this article](http://blog.steveklabnik.com/posts/2012-07-14-why-i-don-t-like-factory_girl).
379
+
380
+ ### Easy to read matchers
381
+
382
+ Use readable matchers and double check the available
383
+ [rspec matchers](https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/).
384
+
385
+ **bad**
386
+
387
+ ```ruby
388
+ lambda { model.save! }.to raise_error Mongoid::Errors::DocumentNotFound
389
+ ```
390
+
391
+ **good**
392
+
393
+ ```ruby
394
+ expect { model.save! }.to raise_error Mongoid::Errors::DocumentNotFound
395
+ ```
396
+
397
+ ### Shared examples
398
+
399
+ Avoid shared examples.
400
+
401
+ ### Test what you see
402
+
403
+ Deeply test your models and your application behaviour (integration tests). Do not add useless complexity testing
404
+ controllers.
405
+
406
+ When I first started testing my apps I was testing controllers, now I don't. Now I only create integration tests using
407
+ RSpec and Capybara. Why? Because I believe that you should test what you see and because testing controllers is an
408
+ extra step you wont usually need. You'll find out that most of your tests go into the models and that integration
409
+ tests can be easily grouped into shared examples, building a clear and readable test suite.
410
+
411
+ This is an open debate in the Ruby community and both sides have good arguments supporting their idea. People
412
+ supporting the need of testing controllers will tell you that your integration tests don't cover all use cases and that
413
+ they are slow. Both are wrong. You can easily cover all use cases (why shouldn't you?) and you can run single file specs
414
+ using automated tools like Guard. In this way you will run only the specs you need to test blazing fast without stopping
415
+ your flow.
416
+
417
+ ### Don't use should
418
+
419
+ Do not use should when describing your tests. Use the third person in the present tense. Even better start using the
420
+ new [expectation](http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/) syntax.
421
+
422
+ **bad**
423
+
424
+ ```ruby
425
+ it 'should not change timings' do
426
+ consumption.occur_at.should == valid.occur_at
427
+ end
428
+ ```
429
+
430
+ **good**
431
+
432
+ ```ruby
433
+ it 'does not change timings' do
434
+ expect(consumption.occur_at).to eq(valid.occur_at)
435
+ end
436
+ ```
437
+
438
+ See [the should_not gem](https://github.com/should-not/should_not) for a way to enforce this in RSpec and
439
+ [the should_clean](https://github.com/siyelo/should_clean) gem for a way to clean up existing RSpec examples that begin
440
+ with 'should.'
441
+
442
+ ### Stubbing HTTP requests
443
+
444
+ Sometimes you need to access external services. In these cases you can't rely on the real service but you should stub
445
+ it with solutions like webmock.
446
+
447
+ **good**
448
+
449
+ ```ruby
450
+ context 'with unauthorized access' do
451
+ let(:uri) { 'http://api.lelylan.com/types' }
452
+
453
+ before { stub_request(:get, uri).to_return(status: 401, body: fixture('401.json')) }
454
+
455
+ it 'gets a not authorized notification' do
456
+ page.driver.get uri
457
+ expect(page).to have_content 'Access denied'
458
+ end
459
+ end
460
+ ```
461
+
462
+ Learn more about [webmock](https://github.com/bblimke/webmock) and [VCR](https://github.com/vcr/vcr). Here a
463
+ [nice presentation](http://marnen.github.io/webmock-presentation/webmock.html) explaining how to mix them together.
464
+
465
+ ### Test Structure
466
+
467
+ Use the context-driven approach instead of inline conditions:
468
+
469
+ **Good:**
470
+ ```ruby
471
+ describe '#method' do
472
+ context 'when a condition is met' do
473
+ it 'does something' do
474
+ # test implementation
475
+ end
476
+ end
477
+
478
+ context 'when another condition is met' do
479
+ it 'does something else' do
480
+ # test implementation
481
+ end
482
+ end
483
+ end
484
+ ```
485
+
486
+ **Avoid:**
487
+ ```ruby
488
+ describe '#method' do
489
+ it 'does something when condition is met' do
490
+ # test implementation
491
+ end
492
+
493
+ it 'does something else when another condition is met' do
494
+ # test implementation
495
+ end
496
+ end
497
+ ```
498
+
499
+ ### AAA Pattern (Arrange, Act, Assert)
500
+
501
+ Structure each test using the AAA pattern with RSpec's `describe`, `context`, `let`, and `before` hooks:
502
+
503
+ ```ruby
504
+ describe Calculator do
505
+ # Arrange - Set up test data using let and/or before blocks
506
+ let(:calculator) { described_class.new }
507
+
508
+ describe '#add' do
509
+ context 'when given positive numbers' do
510
+ it 'returns the sum' do
511
+ # Act - Execute the method being tested
512
+ result = calculator.add(2, 3)
513
+
514
+ # Assert - Verify the expected outcome
515
+ expect(result).to eq(5)
516
+ end
517
+ end
518
+
519
+ context 'when calculator is in debug mode' do
520
+ before do
521
+ # Arrange - Additional setup using before hook
522
+ calculator.enable_debug_mode
523
+ allow(calculator).to receive(:log).and_return(true)
524
+ end
525
+
526
+ it 'logs the operation' do
527
+ # Act
528
+ calculator.add(2, 3)
529
+
530
+ # Assert
531
+ expect(calculator).to have_received(:log).with('Adding 2 + 3')
532
+ end
533
+
534
+ it 'returns the sum' do
535
+ # Act
536
+ result = calculator.add(2, 3)
537
+
538
+ # Assert
539
+ expect(result).to eq(5)
540
+ end
541
+ end
542
+ end
543
+ end
544
+ ```
545
+
546
+ ## Guidelines
547
+
548
+ 1. **Use descriptive context names** that describe the condition being tested
549
+ 3. **Use blank lines** to separate Arrange, Act, and Assert sections
550
+ 4. **Use let blocks** for shared setup data
551
+ 5. **Use before blocks** for imperative setup (method calls, mocks, state changes)
552
+ 6. **Use described_class** instead of the class name directly
553
+ 7. **Describe one method per describe block** using Ruby documentation conventions (`#method` for instance methods, `.method` for class methods)
554
+ 8. **Follow the existing codebase patterns** for consistency
555
+
556
+ ## Running Tests
557
+
558
+ - Run all tests: `bundle exec rspec`
559
+ - Run specific file: `bundle exec rspec spec/path/to/file_spec.rb`
560
+ - Run with coverage: `COVERAGE=true bundle exec rspec`
561
+
@@ -3,7 +3,8 @@
3
3
  "allow": [
4
4
  "Bash(bundle exec rspec:*)",
5
5
  "Bash(bundle exec rubocop:*)",
6
- "Bash(bundle exec rake:*)"
6
+ "Bash(bundle exec rake:*)",
7
+ "Bash(mkdir:*)"
7
8
  ]
8
9
  },
9
10
  "enableAllProjectMcpServers": false
data/.rubocop.yml CHANGED
@@ -66,6 +66,9 @@ RSpec/ExampleLength:
66
66
  RSpec/NestedGroups:
67
67
  Enabled: false
68
68
 
69
+ RSpec/MultipleMemoizedHelpers:
70
+ Enabled: false
71
+
69
72
  RSpec/MultipleExpectations:
70
73
  Enabled: false
71
74
 
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.4.4
1
+ ruby 3.4.5
data/CHANGELOG.md CHANGED
@@ -5,7 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.4.0] - 2025-07-18
9
+
10
+ ### Added
11
+ - JSONRPC::BatchRequest#process_each method for simplified batch processing
12
+
13
+ ## [0.3.0] - 2025-07-17
14
+
15
+ ### Added
16
+ - Rails routing support with method constraints for JSON-RPC procedures
17
+ - Rails single-file routing example application
18
+ - Method constraint helper for Rails routing integration
19
+
20
+ ### Changed
21
+ - Updated Ruby to v3.4.5 across all environments
22
+ - Updated development dependencies (bundler, overcommit, rubocop, rubocop-yard)
23
+ - Updated examples dependencies to use latest versions
24
+ - Improved documentation for Rails single-file applications
25
+
26
+ ### Fixed
27
+ - Standardized Ruby version usage across the project
9
28
 
10
29
  ## [0.2.0] - 2025-07-10
11
30
 
@@ -52,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52
71
  - Helper methods for request and response processing
53
72
  - Examples for basic and advanced usage scenarios
54
73
 
55
- [Unreleased]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.2.0...HEAD
74
+ [0.4.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.3.0...v0.4.0
75
+ [0.3.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.2.0...v0.3.0
56
76
  [0.2.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.1.0...v0.2.0
57
77
  [0.1.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/745b5a...v0.1.0