rspock 2.1.0 → 2.2.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: 856b42367a5e7d9bfcffe5e725f24d33c3792176aa927516a517c4c9118d5be7
4
- data.tar.gz: '09aa051168f43190aba2b5ed346e141bb6f8597ff3e42a51681a2de366b96f16'
3
+ metadata.gz: 40f95078010f80a7874ef4cd38a286629ea2fa1c6355dd2f8b15f8b1b8f050f6
4
+ data.tar.gz: 7b78667195ce9c030bf0f32de406f65ae463d69be810838aff25b525b0f2e755
5
5
  SHA512:
6
- metadata.gz: 296fdd0f0318aa99b4a4269b99679c35932e7dfb80ae2aecc2f879f996fee63d1fa4f35d82b3e09efe4b8b6a168aa1d3e8dba1880334467c2a595318159e8c69
7
- data.tar.gz: 00a357b4915ccf4354e38dabc2f415370dbb6cb2cb9c485776f4252c300239f5285d20c6f262854f269f8d8abf2e8499e5570b7d36ad5036214d4c4259afb28b
6
+ metadata.gz: 301ca7b7cf35271b5b28d79acfdf135d11fd35979e4c5a3497e5d16c8bc314fa8dc9cac12053070577f93373b83ae3c5dc3321f38f57b508ca51ebf8c97f53bc
7
+ data.tar.gz: 98777999b2c5f4f028c66fe6e4fc109cbcce0bd024ea6729cd8d0fbe1420e512d706c5482d99c5f09f0a580887ed82ed5f19d2962d258628f7aee94c05f3d1e4
data/.claude/CLAUDE.md ADDED
@@ -0,0 +1,2 @@
1
+ See @../.cursor/rules/base.mdc for information on your desired behavior.
2
+ See @../.cursor/rules/best-practices.mdc for our best practices (snapshot we own and evolve).
@@ -0,0 +1,35 @@
1
+ ---
2
+ description: Principal Engineer persona and code guidelines (stack-agnostic)
3
+ globs:
4
+ alwaysApply: true
5
+ ---
6
+ As a Principal Engineer, the highest ranking engineer at our company, you are tasked with creating clear, readable code. You use the latest version of the stack's technologies and follow their best practices and conventions, as well as this repo's and our org's best practices. Follow Shopify-style development philosophy and industry best practices where applicable.
7
+
8
+ When responding to questions, follow the Chain of Thought method. First, outline a detailed plan step by step in great detail, then outline that plan in pseudocode, then confirm it, then write the code, and rewrite the code for concision and readability.
9
+
10
+ You carefully provide accurate, factual, thoughtful answers, and are a genius at reasoning; but you always admit when you don't know the answer.
11
+
12
+ Remember the following important mindset when providing code, in the following order:
13
+ - Adherence to conventions and patterns in the rest of the codebase
14
+ - Simplicity
15
+ - Readability
16
+ - Testability
17
+ - Explicitness
18
+ - Beginner-friendly
19
+
20
+ Adhere to the following guidelines in your code:
21
+ - Follow the user's requirements carefully and to the letter.
22
+ - Fully implement all requested functionality.
23
+ - Leave no TODOs, FIXMEs, placeholders or missing pieces.
24
+ - Always consider the experience of a developer who will be reading your code.
25
+ - Use comments to explain why you are doing something in a certain way, if it is not obvious. If unsure, leave a comment.
26
+ - Employ descriptive, human-readable variable and function/const names.
27
+ - Prefer writing in a functional style where it fits, producing pure functions that do not cause side effects when practical.
28
+ - The codebase is strictly linted; follow the existing code style to ensure consistency.
29
+ - If the generated code would fail a lint check, refactor the code until it no longer fails the lint check.
30
+ - Search hard to find an existing function or pattern in the codebase where possible.
31
+ - Be sure to reference file names when relevant.
32
+ - Be concise. Minimize any prose other than code.
33
+ - If you think there might not be a correct answer, say so. If you do not know the answer, say so instead of guessing.
34
+ - In tests, always avoid mocking the filesystem. Use real files and directories, in temporary directories if needed.
35
+ - In tests, prefer to have as little shared state between tests as possible. Avoid beforeAll and afterAll.
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Our best practices (snapshot we own and evolve; often org-wide across repos)
3
+ globs:
4
+ alwaysApply: true
5
+ ---
6
+ # Best practices (d3mlabs snapshot)
7
+
8
+ We own this snapshot and evolve it as the org diverges. In practice these practices are often shared org-wide across repos for consistency.
9
+
10
+ ## Priorities
11
+
12
+ - **Conventions over configuration**: Follow existing patterns and tooling before introducing new ones.
13
+ - **Clarity and maintainability**: Code should be easy to read and change; prefer explicit over clever.
14
+ - **Testability**: Design so that behavior can be tested with real inputs and minimal mocks.
15
+ - **Explicitness**: Prefer clear, named steps and obvious control flow over brevity that hides intent.
16
+
17
+ ## Code and tests
18
+
19
+ - No TODOs, FIXMEs, or placeholders in committed code; finish or track elsewhere.
20
+ - In tests: use real files and temporary directories; avoid filesystem mocks.
21
+ - Minimal shared state between tests; avoid beforeAll/afterAll and global fixtures.
22
+ - Strict lint and existing code style; refactor until lint passes.
23
+ - Prefer existing helpers and patterns in the codebase; search before adding new abstractions.
24
+
25
+ ## Further reading
26
+
27
+ - [Shopify Engineering](https://shopify.engineering) — blog and engineering culture.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspock (2.1.0)
4
+ rspock (2.2.0)
5
5
  ast_transform (~> 2.0)
6
6
  minitest (~> 5.0)
7
7
  mocha (>= 1.0)
@@ -13,13 +13,15 @@ GEM
13
13
  specs:
14
14
  ansi (1.5.0)
15
15
  ast (2.4.3)
16
- ast_transform (2.1.0)
16
+ ast_transform (2.1.4)
17
17
  parser (>= 3.0)
18
18
  prism (>= 1.5)
19
19
  unparser (>= 0.6)
20
20
  builder (3.3.0)
21
+ byebug (13.0.0)
22
+ reline (>= 0.6.0)
21
23
  coderay (1.1.3)
22
- diff-lcs (1.6.2)
24
+ diff-lcs (2.0.0)
23
25
  docile (1.4.1)
24
26
  io-console (0.8.2)
25
27
  method_source (1.1.0)
@@ -39,6 +41,9 @@ GEM
39
41
  coderay (~> 1.1)
40
42
  method_source (~> 1.0)
41
43
  reline (>= 0.6.0)
44
+ pry-byebug (3.12.0)
45
+ byebug (~> 13.0)
46
+ pry (>= 0.13, < 0.17)
42
47
  racc (1.8.1)
43
48
  rake (13.3.1)
44
49
  reline (0.6.3)
@@ -51,8 +56,8 @@ GEM
51
56
  simplecov_json_formatter (~> 0.1)
52
57
  simplecov-html (0.13.2)
53
58
  simplecov_json_formatter (0.1.4)
54
- unparser (0.8.1)
55
- diff-lcs (~> 1.6)
59
+ unparser (0.8.2)
60
+ diff-lcs (>= 1.6, < 3)
56
61
  parser (>= 3.3.0)
57
62
  prism (>= 1.5.1)
58
63
 
@@ -65,6 +70,7 @@ DEPENDENCIES
65
70
  minitest (~> 5.14)
66
71
  minitest-reporters (~> 1.4)
67
72
  pry (>= 0.14)
73
+ pry-byebug (~> 3.9)
68
74
  rake (~> 13.0)
69
75
  rspock!
70
76
  simplecov (~> 0.22)
data/README.md CHANGED
@@ -17,7 +17,7 @@ Note: RSpock is heavily inspired by Spock for the Groovy programming language.
17
17
  * BDD-style code blocks: Given, When, Then, Expect, Cleanup, Where
18
18
  * Data-driven testing with incredibly expressive table-based Where blocks
19
19
  * Expressive assertions: Use familiar comparison operators `==` and `!=` for assertions!
20
- * [Interaction-based testing](#mocking-with-interactions), i.e. `1 * object.receive("message")` in Then blocks
20
+ * [Interaction-based testing](#mocking-with-interactions), i.e. `1 * object.receive("message")` in Then blocks, with optional [return value stubbing](#stubbing-return-values) via `>>`
21
21
  * (Planned) BDD-style custom reporter that outputs information from Code Blocks
22
22
  * (Planned) Capture all Then block violations
23
23
 
@@ -306,11 +306,12 @@ test "#publish sends a message to all subscribers" do
306
306
  end
307
307
  ```
308
308
 
309
- The above ___Then___ block contains 2 interactions, each of which has 4 parts: the _cardinality_, the _receiver_, the _message_ and its _arguments_.
309
+ The above ___Then___ block contains 2 interactions, each of which has 4 parts: the _cardinality_, the _receiver_, the _message_ and its _arguments_. Optionally, a _return value_ can be specified using the `>>` operator.
310
310
 
311
311
  ```
312
- 1 * receiver.message('hello')
313
- | | | |
312
+ 1 * receiver.message('hello') >> "result"
313
+ | | | | |
314
+ | | | | return value (optional)
314
315
  | | | argument(s)
315
316
  | | message
316
317
  | receiver
@@ -357,6 +358,58 @@ The arguments of an interaction describe which arguments of the method call are
357
358
  1 * subscriber.receive('hello') # an argument that is equal to the String 'hello'
358
359
  ```
359
360
 
361
+ #### Stubbing Return Values
362
+
363
+ Interactions can be combined with return value stubbing using the `>>` operator. This is useful when the code under test relies on the return value of a collaborator method.
364
+
365
+ ```ruby
366
+ 1 * subscriber.receive("hello") >> "ok"
367
+ ```
368
+
369
+ The `>>` operator is placed at the end of the interaction and specifies the value that the method call will return. This means you can set up expectations _and_ stub return values in a single, expressive statement.
370
+
371
+ ```ruby
372
+ class Service
373
+ # ...
374
+
375
+ def initialize(repository)
376
+ @repository = repository
377
+ end
378
+
379
+ def foo(param)
380
+ # ...
381
+ result = @repository.bar(param)
382
+ # Do something with +result+ ...
383
+
384
+ result
385
+ end
386
+ end
387
+
388
+ test "Service#bar" do
389
+ Given
390
+ repository = Repository.new
391
+ service = Service.new(repository)
392
+
393
+ When
394
+ result = service.foo(42)
395
+
396
+ Then
397
+ 1 * repository.foo(42) >> { name: "item", status: "active" }
398
+ result == "active"
399
+ end
400
+ ```
401
+
402
+ The return value can be any expression — a literal, a variable, or a complex object:
403
+
404
+ ```ruby
405
+ 1 * repository.find(42) >> "result" # a String
406
+ 1 * repository.all >> [item1, item2] # an Array
407
+ 1 * service.call >> { status: :ok } # a Hash
408
+ _ * cache.fetch("key") >> expensive_result # a variable
409
+ ```
410
+
411
+ **Note**: Without `>>`, an interaction sets up an expectation only (the method will return `nil` by default). Use `>>` when the code under test depends on the return value.
412
+
360
413
  ## Debugging
361
414
 
362
415
  ### Pry
@@ -448,7 +501,7 @@ There are two ways to create a release. Both require that `version.rb` has alrea
448
501
 
449
502
  ### Via GitHub UI
450
503
 
451
- 1. Update `VERSION` in `lib/rspock/version.rb`, commit, open a PR, and merge to main
504
+ 1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock`, commit, open a PR, and merge to main
452
505
  2. Go to the repo on GitHub → **Releases** → **Draft a new release**
453
506
  3. Enter a new tag (e.g. `v2.0.0`), select `main` as the target branch
454
507
  4. Add a title and release notes (GitHub can auto-generate these from merged PRs)
@@ -456,7 +509,7 @@ There are two ways to create a release. Both require that `version.rb` has alrea
456
509
 
457
510
  ### Via CLI
458
511
 
459
- 1. Update `VERSION` in `lib/rspock/version.rb`, commit, open a PR, and merge to main
512
+ 1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock`, commit, open a PR, and merge to main
460
513
  2. Tag and push:
461
514
  ```
462
515
  git checkout main && git pull
@@ -15,8 +15,10 @@ module RSpock
15
15
 
16
16
  def interaction_node?(node)
17
17
  return false if node.nil?
18
+ return true if node.type == :send && node.children[1] == :*
19
+ return true if return_value_node?(node)
18
20
 
19
- node.type == :send && node.children[1] == :*
21
+ false
20
22
  end
21
23
 
22
24
  private
@@ -45,6 +47,8 @@ module RSpock
45
47
  raise ArgumentError, "Unrecognized times constraint in interaction: #{@times_node&.loc&.expression || "?"}"
46
48
  end
47
49
 
50
+ result = chain_call(result, :returns, @return_value_node) if @return_value_node
51
+
48
52
  result
49
53
  end
50
54
 
@@ -88,11 +92,24 @@ module RSpock
88
92
  result
89
93
  end
90
94
 
95
+ def return_value_node?(node)
96
+ node.type == :send && node.children[1] == :>> && interaction_node?(node.children[0])
97
+ end
98
+
91
99
  def any_matcher_node?(node)
92
100
  node.type == :send && node.children[0].nil? && node.children[1] == :_
93
101
  end
94
102
 
95
103
  def parse_node(node)
104
+ # Unwrap `interaction >> return_value` into the underlying interaction,
105
+ # assigned to @return_value_node for transform_node to chain .returns().
106
+ if return_value_node?(node)
107
+ @return_value_node = node.children[2]
108
+ node = node.children[0]
109
+ else
110
+ @return_value_node = nil
111
+ end
112
+
96
113
  parse_lhs(node.children[0])
97
114
  parse_rhs(node.children[2])
98
115
  end
@@ -33,7 +33,7 @@ module RSpock
33
33
  return location unless source_map
34
34
 
35
35
  line_number = source_map.line(lineno) || '?'
36
- location.gsub(/#{ASTTransform.output_path}\/([\S]+):(\d+)/, "\\1:#{line_number}")
36
+ location.sub("#{file_path}:#{lineno}", "#{source_map.source_file_path}:#{line_number}")
37
37
  end
38
38
 
39
39
  private
@@ -1,3 +1,3 @@
1
1
  module RSpock
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
data/rspock.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "minitest", "~> 5.14"
27
27
  spec.add_development_dependency "minitest-reporters", "~> 1.4"
28
28
  spec.add_development_dependency "pry", ">= 0.14"
29
+ spec.add_development_dependency "pry-byebug", "~> 3.9"
29
30
  spec.add_development_dependency "rake", "~> 13.0"
30
31
  spec.add_development_dependency "simplecov", "~> 0.22"
31
32
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean-Philippe Duchesne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-21 00:00:00.000000000 Z
11
+ date: 2026-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +185,9 @@ executables: []
171
185
  extensions: []
172
186
  extra_rdoc_files: []
173
187
  files:
188
+ - ".claude/CLAUDE.md"
189
+ - ".cursor/rules/base.mdc"
190
+ - ".cursor/rules/best-practices.mdc"
174
191
  - ".github/workflows/ci.yml"
175
192
  - ".github/workflows/release.yml"
176
193
  - ".gitignore"