activerecord-null 0.1.3 → 0.1.4

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: 61c6b213e7c4c8f5cbeab18700a2eebc46803592fb34b3677ed93da0a6c49c92
4
- data.tar.gz: 698f74dbdf03af5dbdc7e35111f20a478363c832028258c77cc14fdf0061b8b0
3
+ metadata.gz: 7367e3edc8eb1a5d3a539c08cb3ac8bc18a7334d36725edd397ca121adcd143c
4
+ data.tar.gz: 8e86346fcf08e650daeaacc39a70c213dce6cf63119317742fb0ecd5ffbdc142
5
5
  SHA512:
6
- metadata.gz: 3e8fd9f3d1c8c130a997e3c66c6560b82e18b2ff782a2772832de7e4344abb59f0a6ef0efcaae67cb2a47dd221076b1113be2a0f520bc0c267a6ecb137c3b7eb
7
- data.tar.gz: 9848bd89df1f1cb2ded9dcecd27f478aacbac6ed1586f56ecd7b17446b3755bceec6fbef8b0858b58be7996da11617c1d2f056517c0a55a03aff26a79ee483e5
6
+ metadata.gz: 2ec01aaa257aebaf1e5f9773e9d9d54431a0ffedf51adba02426b18552dc1bd20e1f64f3a1613a797fe69330c510526383e295650db1720e50b66b67715d6d25
7
+ data.tar.gz: eaf8a7c1df7a95fb2a48f44f519a81f5961d5ab681daa93933e47f80f88a3bfe335061642da59ad14456245363af478e5f62bbc9ecb616a610273b54ec88f287
data/CHANGELOG.md CHANGED
@@ -5,24 +5,14 @@ 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
- ## [0.1.3] - 2025-11-18
8
+ ## [0.1.4] - 2025-11-19
9
9
 
10
10
  ### Changed
11
11
 
12
- - Use git trailers to track changelog entries. (d296851)
13
- - Implement [] method to access attributes with string/symbol keys (1dc4f95)
12
+ - Only initialize attributes when table exists (dba2ace)
14
13
 
15
- ### Removed
16
-
17
- - CodeClimate access on CI runs. (2dbf5bc)
18
-
19
- ## [0.1.3] - 2025-11-18
14
+ ## [0.1.4] - 2025-11-19
20
15
 
21
16
  ### Changed
22
17
 
23
- - Use git trailers to track changelog entries. (d296851)
24
- - Implement [] method to access attributes with string/symbol keys (1dc4f95)
25
-
26
- ### Removed
27
-
28
- - CodeClimate access on CI runs. (2dbf5bc)
18
+ - Only initialize attributes when table exists (dba2ace)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Null
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.4"
6
6
  end
7
7
  end
@@ -45,7 +45,12 @@ module ActiveRecord
45
45
 
46
46
  include Singleton
47
47
 
48
+ # Store assignments for lazy initialization
49
+ @_null_assignments = assignments
50
+
48
51
  class << self
52
+ attr_reader :_null_assignments
53
+
49
54
  def method_missing(method, ...)
50
55
  mimic_model_class.respond_to?(method) ? mimic_model_class.send(method, ...) : super
51
56
  end
@@ -53,18 +58,42 @@ module ActiveRecord
53
58
  def respond_to_missing?(method, include_private = false)
54
59
  mimic_model_class.respond_to?(method, include_private) || super
55
60
  end
61
+
62
+ # Override instance to initialize attributes lazily
63
+ def instance
64
+ initialize_attribute_methods unless @_attributes_initialized
65
+ super
66
+ end
67
+
68
+ private
69
+
70
+ def initialize_attribute_methods
71
+ # Only initialize if table exists
72
+ return unless mimic_model_class.table_exists?
73
+
74
+ # Define custom assignment methods first
75
+ if _null_assignments.any?
76
+ _null_assignments.each do |attributes, value|
77
+ define_attribute_methods(attributes, value:)
78
+ end
79
+ end
80
+
81
+ # Then define database attributes
82
+ nil_assignments = mimic_model_class.attribute_names
83
+ # Remove custom assignments from database attributes
84
+ if _null_assignments.any?
85
+ _null_assignments.each do |attributes, _|
86
+ nil_assignments -= attributes
87
+ end
88
+ end
89
+ define_attribute_methods(nil_assignments) if nil_assignments.any?
90
+
91
+ @_attributes_initialized = true
92
+ end
56
93
  end
57
94
  end
58
95
  null_class.class_eval(&) if block_given?
59
96
 
60
- nil_assignments = inherit.attribute_names
61
- if assignments.any?
62
- assignments.each do |attributes, value|
63
- nil_assignments -= attributes
64
- null_class.define_attribute_methods(attributes, value:)
65
- end
66
- end
67
- null_class.define_attribute_methods(nil_assignments)
68
97
  inherit.const_set(:Null, null_class)
69
98
 
70
99
  inherit.define_singleton_method(:null) { null_class.instance }
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ # Test that Null classes can be defined without database access
6
+ class LazyLoadingTest < Minitest::Spec
7
+ describe "Lazy attribute loading" do
8
+ it "allows Null class definition without database access" do
9
+ # Create a new model class with a fake table_exists? check
10
+ table_available = false
11
+
12
+ test_class = Class.new(ApplicationRecord) do
13
+ def self.name
14
+ "TestModel"
15
+ end
16
+
17
+ define_singleton_method(:table_exists?) do
18
+ table_available
19
+ end
20
+
21
+ define_singleton_method(:attribute_names) do
22
+ ["id", "name", "email"]
23
+ end
24
+ end
25
+
26
+ # Extend with Null - this should NOT require database
27
+ test_class.extend ActiveRecord::Null
28
+
29
+ # Define the Null class - this should also NOT require database
30
+ # This is the key fix - Null() doesn't call attribute_names
31
+ assert_silent do
32
+ test_class.Null([:name] => "Unknown")
33
+ end
34
+
35
+ # The Null class constant should exist
36
+ assert test_class.const_defined?(:Null)
37
+
38
+ # Now make table available
39
+ table_available = true
40
+
41
+ # When we call .null with table available, attributes get loaded
42
+ null_instance = test_class.null
43
+ assert_instance_of test_class::Null, null_instance
44
+ assert_equal "Unknown", null_instance.name
45
+ assert_nil null_instance.email
46
+ end
47
+
48
+ it "initializes attributes only once" do
49
+ call_count = 0
50
+ test_class = Class.new(ApplicationRecord) do
51
+ def self.name
52
+ "CountingModel"
53
+ end
54
+
55
+ define_singleton_method(:table_exists?) do
56
+ true
57
+ end
58
+
59
+ define_singleton_method(:attribute_names) do
60
+ call_count += 1
61
+ ["id", "value"]
62
+ end
63
+ end
64
+
65
+ test_class.extend ActiveRecord::Null
66
+ test_class.Null
67
+
68
+ # Calling .null multiple times should only initialize once
69
+ assert_equal 0, call_count
70
+ test_class.null
71
+ assert_equal 1, call_count
72
+ test_class.null
73
+ assert_equal 1, call_count
74
+ test_class.null
75
+ assert_equal 1, call_count
76
+ end
77
+
78
+ it "handles missing database gracefully" do
79
+ # Create a model that simulates a missing database
80
+ test_class = Class.new(ApplicationRecord) do
81
+ def self.name
82
+ "MissingDbModel"
83
+ end
84
+
85
+ define_singleton_method(:table_exists?) do
86
+ false
87
+ end
88
+
89
+ define_singleton_method(:attribute_names) do
90
+ raise ActiveRecord::NoDatabaseError, "Database 'test' does not exist"
91
+ end
92
+ end
93
+
94
+ test_class.extend ActiveRecord::Null
95
+
96
+ # Should be able to define Null class without database
97
+ assert_silent do
98
+ test_class.Null([:name] => "Unknown")
99
+ end
100
+
101
+ # Accessing .null returns the instance (but no attributes are defined yet)
102
+ null_instance = test_class.null
103
+ assert_instance_of test_class::Null, null_instance
104
+
105
+ # Without a table, no attributes work (not even custom ones)
106
+ # This is fine - in CI, you wouldn't call .null before running migrations
107
+ assert_raises(NoMethodError) { null_instance.name }
108
+ end
109
+
110
+ it "handles missing table gracefully" do
111
+ # Create a model that simulates a missing table
112
+ test_class = Class.new(ApplicationRecord) do
113
+ def self.name
114
+ "MissingTableModel"
115
+ end
116
+
117
+ define_singleton_method(:table_exists?) do
118
+ false
119
+ end
120
+
121
+ define_singleton_method(:attribute_names) do
122
+ raise ActiveRecord::StatementInvalid, "Table 'missing_table_models' doesn't exist"
123
+ end
124
+ end
125
+
126
+ test_class.extend ActiveRecord::Null
127
+
128
+ # Should be able to define Null class without table
129
+ assert_silent do
130
+ test_class.Null([:status] => "inactive")
131
+ end
132
+
133
+ # Accessing .null returns the instance
134
+ null_instance = test_class.null
135
+ assert_instance_of test_class::Null, null_instance
136
+
137
+ # Without a table, attributes don't work
138
+ assert_raises(NoMethodError) { null_instance.status }
139
+ end
140
+ end
141
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-null
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Gay
@@ -39,6 +39,7 @@ files:
39
39
  - lib/activerecord/null.rb
40
40
  - lib/activerecord/null/mimic.rb
41
41
  - lib/activerecord/null/version.rb
42
+ - test/activerecord/test_lazy_loading.rb
42
43
  - test/activerecord/test_null.rb
43
44
  - test/support/schema.rb
44
45
  - test/test_helper.rb