fixture_kit 0.19.0 → 0.20.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: 2addefdcd1d783d80888f69edb6953f8d6432471864bd1dafa915705a67ce317
4
- data.tar.gz: fded508a51904f238bb5befffd3e3f9132e91010cb3ba74cdf159e0e40cefc67
3
+ metadata.gz: 90cf1c283943f0ec2cf0c82b5f5b6b65de810a1e4e5ed8d15fd12c4b2ba64879
4
+ data.tar.gz: 2eb935b512b373c399220ed60a6250b3d32349aa9dc5e4c31801d3db45a94b94
5
5
  SHA512:
6
- metadata.gz: 2f1b4dc03ceb9edf697972849a4da270a8d6760c1885ffd3ddd579b96fd7896c29d0989bf044deb824d95c5bce598fe3fd5a45753dad674696ad312674963dc9
7
- data.tar.gz: 6d11e943aac3c481c047579c4537782a35003603690e46d8ea23a172e5dbf93a3ae736f38738611a519c6f73d6e3b38516e8172b4c2397345d2ae4e441ddd0cc
6
+ metadata.gz: 8a4f49bf2e6422be97e8a316acfd623c9e27c861bc63c29465dc6d8fb331ddbff85a84aeede77d654e4e544096061b62c8aa9daa3bfac23ec5ae7d6dfc8db957
7
+ data.tar.gz: a799c0b665d6396837fb82ee50d6e193e3adde8114f0edb63d3f3ed5540205b486587a5ff1065ea5811c2b61fa0ed9cb10f0c43fba18da9b15eb0a913b76f660
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "digest"
4
+
3
5
  module FixtureKit
4
6
  class Cache
5
7
  ANONYMOUS_DIRECTORY = "_anonymous"
8
+ DIGEST_LENGTH = 12
6
9
 
7
10
  include ConfigurationHelper
8
11
 
@@ -22,7 +25,8 @@ module FixtureKit
22
25
  if raw_identifier.is_a?(String)
23
26
  raw_identifier
24
27
  else
25
- File.join(ANONYMOUS_DIRECTORY, FixtureKit.runner.adapter.identifier_for(raw_identifier))
28
+ normalized_scope = FixtureKit.runner.adapter.identifier_for(raw_identifier)
29
+ File.join(ANONYMOUS_DIRECTORY, "#{normalized_scope}.#{definition_digest}")
26
30
  end
27
31
  end
28
32
  end
@@ -62,6 +66,26 @@ module FixtureKit
62
66
 
63
67
  private
64
68
 
69
+ # The scope name alone does not identify a declaration site. Test
70
+ # frameworks derive it from the group description, which is lossy -- RSpec
71
+ # strips every non-alphanumeric character, so `Foo::Bar` and `"Foo Bar"`
72
+ # both become `FooBar` -- and, worse, not even unique within a process:
73
+ # test runners that load specs in batches call RSpec::Core::World#reset
74
+ # between them, which drops the constants RSpec disambiguates against, so
75
+ # the first group of a given description in *every* batch takes the
76
+ # unsuffixed name. The cache directory is cleared once per process, so
77
+ # without the digest two spec files sharing a top-level description share
78
+ # this entry, and the second fixture to generate silently mounts the
79
+ # first's records.
80
+ #
81
+ # Joined to the scope with a dot rather than an underscore: scope names are
82
+ # snake_case, so an underscore leaves no boundary between the two, while a
83
+ # dot cannot appear in one -- RSpec strips non-alphanumerics from the group
84
+ # description before it is underscored.
85
+ def definition_digest
86
+ Digest::SHA256.hexdigest(fixture.definition.fingerprint)[0, DIGEST_LENGTH]
87
+ end
88
+
65
89
  def evaluate(coders, context, data = {}, &block)
66
90
  if coders.empty?
67
91
  fixture.definition.evaluate(context, parent: fixture.parent&.mount)
@@ -14,6 +14,17 @@ module FixtureKit
14
14
  @definition.source_location.first
15
15
  end
16
16
 
17
+ def location
18
+ file, line = @definition.source_location
19
+ "#{file}:#{line}"
20
+ end
21
+
22
+ # Two definitions sharing a fingerprint evaluate the same block against the
23
+ # same parent, so they are interchangeable and may share a cache entry.
24
+ def fingerprint
25
+ "#{location}:#{extends}"
26
+ end
27
+
17
28
  def evaluate(context, parent: nil)
18
29
  context.singleton_class.prepend(mixin(parent))
19
30
  context.instance_exec(&@definition)
@@ -19,6 +19,8 @@ module FixtureKit
19
19
  end
20
20
 
21
21
  def generate(force: false)
22
+ FixtureKit.runner.registry.claim_cache_identifier(self)
23
+
22
24
  return if @cache.exists? && !force
23
25
 
24
26
  parent&.generate
@@ -8,6 +8,7 @@ module FixtureKit
8
8
  @declarations = {}
9
9
  @fixtures = {}
10
10
  @resolving = []
11
+ @cache_owners = {}
11
12
  end
12
13
 
13
14
  def add(name_or_definition, scope = nil)
@@ -34,6 +35,23 @@ module FixtureKit
34
35
  @fixtures.values
35
36
  end
36
37
 
38
+ # Fixtures sharing a cache identifier share a cache file: the second one to
39
+ # generate finds the file already written, skips generation, and mounts the
40
+ # other's records. That is only safe when both declarations are the same, so
41
+ # anything else has to fail here -- otherwise it surfaces as a confusing
42
+ # NoMethodError on Repository, or not at all when both expose the same names.
43
+ # Identifiers are derived from the declaration site, so this is a backstop
44
+ # against a digest collision or a regression in that derivation.
45
+ def claim_cache_identifier(fixture)
46
+ owner = (@cache_owners[fixture.cache.identifier] ||= fixture)
47
+ return if owner.equal?(fixture)
48
+ return if owner.definition.fingerprint == fixture.definition.fingerprint
49
+
50
+ raise FixtureKit::CacheIdentifierCollision,
51
+ "fixtures declared at #{owner.definition.location} and #{fixture.definition.location} " \
52
+ "both resolve to the cache identifier '#{fixture.cache.identifier}'"
53
+ end
54
+
37
55
  private
38
56
 
39
57
  def fetch_named_fixture(name)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FixtureKit
4
- VERSION = "0.19.0"
4
+ VERSION = "0.20.0"
5
5
  end
data/lib/fixture_kit.rb CHANGED
@@ -6,6 +6,7 @@ module FixtureKit
6
6
  class InvalidFixtureDeclaration < Error; end
7
7
  class MultipleFixtures < Error; end
8
8
  class CacheMissingError < Error; end
9
+ class CacheIdentifierCollision < Error; end
9
10
  class FixtureDefinitionNotFound < Error; end
10
11
  class RunnerAlreadyStartedError < Error; end
11
12
  class CircularFixtureInheritance < Error; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixture_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ngan Pham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-04 00:00:00.000000000 Z
11
+ date: 2026-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport