declare_schema 4.0.0 → 4.0.1

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: 929f250672103aa42777cb918340e5281d100fdaf19cedb5f7aaa96504e66a8c
4
- data.tar.gz: 377a3f2aaae4fa4b6294a4c4a9edd672a8626a505be6a07e06b953a28cb78ffc
3
+ metadata.gz: 3bff0212d77096ebab36e041956c4c1bb9660cf64782e4a43d3ed259958e13f0
4
+ data.tar.gz: 0db353087c6b118cab88e86f959c1a41ad63a3915338a4fce227a98c65d99c40
5
5
  SHA512:
6
- metadata.gz: 8d05a979184d7e93041a462b1269b7d71a6a675f37b8914e27728db0f2d28878d0436cc10eba4bd107f1cb57355c1321147659842367e9231ebb32eec5d14b79
7
- data.tar.gz: 3c361cb45d57de4c4aac717155b354f34d0c654b4881c29742a9f611cf7ca8efd0c9297e70433dd7b2df4df70751bf3c1d0463824ccc8eb77f5f9a1d836e711d
6
+ metadata.gz: b80feb059afdf5f9582a0895aed29b3251f9a1d29b03b3c82364204ebff919e70166cfe44c243f75abb20bac9ac75d6ae33d98abfb3728759337f6c7ccd0b34f
7
+ data.tar.gz: 29bf01f645154515d89bb66ad54fc28cdd4401a39dca742d714b44c2d0b437e28b2ba61ae228a6ce7614aabb940e7682ac72873d617d8935ac5a80c8686d0fb7
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.2
1
+ 3.3.8
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
5
  Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [4.0.1] - 2026-05-07
8
+ ### Fixed
9
+ - `DeferredFieldSpec` (the lazy stand-in introduced in 4.0.0 for `belongs_to`
10
+ foreign-key field specs) now transparently delegates the `FieldSpec` read
11
+ API (`.options`, `.type`, `.limit`, `.null`, etc.) to its memoized resolved
12
+ spec. Application code that reads `Model.field_specs[name].options` at
13
+ runtime would raise `NoMethodError: undefined method 'options' for an
14
+ instance of DeclareSchema::Model::DeferredFieldSpec`.
15
+
7
16
  ## [4.0.0] - 2026-05-05
8
17
  ### Added
9
18
  - Generalized `belongs_to` foreign keys to always match the primary key they point at, including
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (4.0.0)
4
+ declare_schema (4.0.1)
5
5
  rails (>= 7.0)
6
6
 
7
7
  GEM
@@ -12,7 +12,7 @@ module DeclareSchema
12
12
  # The migrator calls {#resolve} on every value in `field_specs` at the start
13
13
  # of migration generation (see `Migrator#generate`); for plain {FieldSpec}s
14
14
  # `#resolve` is a no-op returning self, while for instances of this class it
15
- # invokes the block (memoized) with the default spec and returns the produced
15
+ # invokes the resolver block with the default spec and returns the produced
16
16
  # {FieldSpec}.
17
17
  class DeferredFieldSpec
18
18
  # @param default_spec [FieldSpec] the eager placeholder spec
@@ -24,14 +24,25 @@ module DeclareSchema
24
24
  @resolver = resolver
25
25
  end
26
26
 
27
- # Invoke the resolver block with the default spec and return its result.
28
- # The migrator's `transform_values!(&:resolve)` swaps this DeferredFieldSpec
29
- # out of `field_specs` for the produced FieldSpec, so resolve is called
30
- # exactly once per instance in production -- no memoization needed.
27
+ # Resolve and memoize the produced FieldSpec. Memoization matters because
28
+ # application code can hit several FieldSpec accessors per request; without
29
+ # it each one would re-run the resolver and re-touch `reflection.klass`.
31
30
  #
32
31
  # @return [FieldSpec]
33
32
  def resolve
34
- @resolver.call(@default_spec)
33
+ @resolved ||= @resolver.call(@default_spec)
34
+ end
35
+
36
+ def respond_to_missing?(name, include_private = false)
37
+ resolve.respond_to?(name, include_private) || super
38
+ end
39
+
40
+ def method_missing(name, *args, **kwargs, &)
41
+ if resolve.respond_to?(name)
42
+ resolve.public_send(name, *args, **kwargs, &)
43
+ else
44
+ super
45
+ end
35
46
  end
36
47
  end
37
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "4.0.0"
4
+ VERSION = "4.0.1"
5
5
  end
@@ -24,5 +24,59 @@ RSpec.describe DeclareSchema::Model::DeferredFieldSpec do
24
24
  expect(deferred.resolve).to equal(mirrored_spec)
25
25
  expect(captured).to equal(default_spec)
26
26
  end
27
+
28
+ it 'memoizes the resolver result so repeated calls invoke it only once' do
29
+ call_count = 0
30
+ deferred = described_class.new(default_spec) do |_|
31
+ call_count += 1
32
+ mirrored_spec
33
+ end
34
+
35
+ 3.times { deferred.resolve }
36
+
37
+ expect(call_count).to eq(1)
38
+ end
39
+
40
+ it 'memoizes the resolver result across many delegated reads' do
41
+ call_count = 0
42
+ deferred = described_class.new(default_spec) do |_|
43
+ call_count += 1
44
+ mirrored_spec
45
+ end
46
+
47
+ deferred.options
48
+ deferred.type
49
+ deferred.limit
50
+ deferred.null
51
+
52
+ expect(call_count).to eq(1)
53
+ end
54
+ end
55
+
56
+ # Application code can read from `field_specs` without first calling `.resolve`. The deferred spec must
57
+ # therefore quack like the resolved FieldSpec; otherwise we get NoMethodError at runtime
58
+ # in apps that read FieldSpec attributes.
59
+ describe 'FieldSpec API delegation' do
60
+ let(:deferred) { described_class.new(default_spec) { mirrored_spec } }
61
+
62
+ it 'delegates #options to the resolved spec (parent-mirrored, not default)' do
63
+ expect(deferred.options).to eq(mirrored_spec.options)
64
+ end
65
+
66
+ it 'delegates #type to the resolved spec' do
67
+ expect(deferred.type).to eq(:string)
68
+ end
69
+
70
+ it 'delegates #limit to the resolved spec' do
71
+ expect(deferred.limit).to eq(36)
72
+ end
73
+
74
+ it 'delegates #null to the resolved spec' do
75
+ expect(deferred.null).to eq(false)
76
+ end
77
+
78
+ it 'reports respond_to? for delegated methods' do
79
+ expect(deferred).to respond_to(:options, :type, :limit, :null, :name, :position)
80
+ end
27
81
  end
28
82
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: declare_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca Development adapted from hobo_fields by Tom Locke
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-05-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -144,7 +143,6 @@ homepage: https://github.com/Invoca/declare_schema
144
143
  licenses: []
145
144
  metadata:
146
145
  allowed_push_host: https://rubygems.org
147
- post_install_message:
148
146
  rdoc_options: []
149
147
  require_paths:
150
148
  - lib
@@ -159,8 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
157
  - !ruby/object:Gem::Version
160
158
  version: 1.3.6
161
159
  requirements: []
162
- rubygems_version: 3.4.10
163
- signing_key:
160
+ rubygems_version: 3.6.8
164
161
  specification_version: 4
165
162
  summary: Database schema declaration and migration generator for Rails
166
163
  test_files: []