historiographer 4.4.2 → 4.4.3

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/DEVELOPMENT.md +124 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +14 -0
  5. data/README.md +16 -1
  6. data/Rakefile +54 -0
  7. data/VERSION +1 -1
  8. data/bin/console +10 -0
  9. data/bin/setup +15 -0
  10. data/bin/test +5 -0
  11. data/bin/test-all +10 -0
  12. data/bin/test-rails +5 -0
  13. data/historiographer.gemspec +28 -3
  14. data/lib/historiographer/history.rb +72 -37
  15. data/spec/combustion_helper.rb +34 -0
  16. data/spec/db/migrate/20250826000000_create_test_users.rb +8 -0
  17. data/spec/db/migrate/20250826000001_create_test_user_histories.rb +18 -0
  18. data/spec/db/migrate/20250826000002_create_test_websites.rb +9 -0
  19. data/spec/db/migrate/20250826000003_create_test_website_histories.rb +19 -0
  20. data/spec/db/schema.rb +45 -1
  21. data/spec/historiographer_spec.rb +164 -0
  22. data/spec/integration/historiographer_safe_integration_spec.rb +154 -0
  23. data/spec/internal/app/models/application_record.rb +5 -0
  24. data/spec/internal/app/models/deploy.rb +5 -0
  25. data/spec/internal/app/models/user.rb +4 -0
  26. data/spec/internal/app/models/website.rb +5 -0
  27. data/spec/internal/app/models/website_history.rb +7 -0
  28. data/spec/internal/config/database.yml +9 -0
  29. data/spec/internal/config/routes.rb +2 -0
  30. data/spec/internal/db/schema.rb +48 -0
  31. data/spec/models/test_user.rb +4 -0
  32. data/spec/models/test_user_history.rb +3 -0
  33. data/spec/models/test_website.rb +4 -0
  34. data/spec/models/test_website_history.rb +3 -0
  35. data/spec/rails_integration/historiographer_rails_integration_spec.rb +106 -0
  36. metadata +32 -3
  37. data/spec/foreign_key_spec.rb +0 -189
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66fb9c7d713df84dd4ad5470c6ace5730e5a762d378d0d1fc523a00767b94a2b
4
- data.tar.gz: e9b1cfaa4911d4e596aaa8808d1e4cf69b7e57a40101bae4cf826ec48832d3c9
3
+ metadata.gz: e539c6a610a6dd526ea49199d9d8f773dc8528c5e50960b3fc40615d87d81ffb
4
+ data.tar.gz: cc44f3a4a98e65763b27b869a36e4b8997e4bd9e3b1f988ee07a267b0b346dd3
5
5
  SHA512:
6
- metadata.gz: a6291260e457cc89940cbdd622e1e2075e05ad7682005f4bd4dab9e63d302e2cd1dd3d55d494f336b442fe36827b5934251f3fb155480ab46e1bf7e84cb77414
7
- data.tar.gz: e1a2afe3202d4c3949424afda77a5c9b307baa50c1e43bde0c488ecff3bd85aff08b5ce57c6664dba058b188603e35c845e508487769848fa811003174b751e0
6
+ metadata.gz: fb5d80e2d90f8c5dc2172b1a81f75b646750c3eff34c2921fa3a454d7905d05e59420c86c0b24d08eec96ace5f067e1b91e30c0d944675a84a2c5f2ad63e0fb8
7
+ data.tar.gz: 2fedc59aa4c1fcb684e43f18a03a686ffdf4137fcef83e086583d064491a9cb16b7b17ca46f4893b1a3d83cdc4bc6a3977de4ae534454895fa0a53e0b7bdcf2d
data/DEVELOPMENT.md ADDED
@@ -0,0 +1,124 @@
1
+ # Historiographer Development Guide
2
+
3
+ ## Quick Start
4
+
5
+ ```bash
6
+ # Initial setup
7
+ bin/setup
8
+
9
+ # Run tests
10
+ bin/test # Regular test suite (fast)
11
+ bin/test-rails # Rails integration tests (slower)
12
+ bin/test-all # Both test suites
13
+
14
+ # Interactive console
15
+ bin/console
16
+ ```
17
+
18
+ ## Available Commands
19
+
20
+ ### Using Rake Tasks
21
+
22
+ ```bash
23
+ # Testing
24
+ rake spec # Run regular specs (default)
25
+ rake spec:regular # Run regular specs explicitly
26
+ rake spec:rails # Run Rails integration specs
27
+ rake spec:all # Run all specs
28
+
29
+ # Database
30
+ rake db:create # Create test database
31
+ rake db:migrate # Run migrations
32
+ rake db:rollback # Rollback migrations
33
+ rake test_setup # Setup test database
34
+ rake test_reset # Reset test database
35
+
36
+ # Other
37
+ rake console # Launch console with gem loaded
38
+ rake help # List all available tasks
39
+ ```
40
+
41
+ ### Using Bin Scripts
42
+
43
+ All scripts are in the `bin/` directory:
44
+
45
+ - `bin/setup` - Initial development setup
46
+ - `bin/test` - Run regular test suite
47
+ - `bin/test-rails` - Run Rails integration tests
48
+ - `bin/test-all` - Run all tests
49
+ - `bin/console` - Interactive console
50
+
51
+ ### Direct Commands
52
+
53
+ ```bash
54
+ # Run specific tests
55
+ bundle exec rspec spec/historiographer_spec.rb
56
+ bundle exec rspec spec/historiographer_spec.rb:100 # Run specific line
57
+
58
+ # Rails integration tests only
59
+ bundle exec rspec spec/rails_integration
60
+
61
+ # Database operations
62
+ bundle exec rake db:create
63
+ bundle exec rake db:migrate
64
+ bundle exec rake db:rollback
65
+ ```
66
+
67
+ ## Test Organization
68
+
69
+ The test suite is split into two parts:
70
+
71
+ 1. **Regular Tests** (`spec/*.rb`, `spec/models/*.rb`, etc.)
72
+ - Fast, lightweight Rails simulation
73
+ - Uses standalone_migrations
74
+ - Default when running `rake spec` or `bin/test`
75
+
76
+ 2. **Rails Integration Tests** (`spec/rails_integration/*.rb`)
77
+ - Uses Combustion to create a real Rails app
78
+ - Tests Rails-specific behaviors like autoloading
79
+ - Run separately with `rake spec:rails` or `bin/test-rails`
80
+
81
+ ## Database Setup
82
+
83
+ The gem uses PostgreSQL for testing. Two databases are used:
84
+
85
+ 1. `historiographer_test` - Main test database
86
+ 2. `historiographer_combustion_test` - Rails integration test database
87
+
88
+ Configure database connection in:
89
+ - `spec/db/database.yml` - Main test database
90
+ - `spec/internal/config/database.yml` - Combustion test database
91
+
92
+ ## Adding New Tests
93
+
94
+ ### Regular Tests
95
+ Place in `spec/` directory. Models go in `spec/models/`.
96
+
97
+ ### Rails Integration Tests
98
+ Place in `spec/rails_integration/`. These tests use Combustion and require:
99
+ ```ruby
100
+ require 'combustion_helper'
101
+ ```
102
+
103
+ ## Debugging
104
+
105
+ ```bash
106
+ # Launch console with all models loaded
107
+ bin/console
108
+
109
+ # Or with rake
110
+ rake console
111
+
112
+ # Or manually
113
+ bundle exec pry -r ./init.rb
114
+ ```
115
+
116
+ ## Gem Development
117
+
118
+ ```bash
119
+ # Build gem
120
+ rake build
121
+
122
+ # Release new version
123
+ rake release
124
+ ```
data/Gemfile CHANGED
@@ -25,9 +25,11 @@ group :development do
25
25
  end
26
26
 
27
27
  group :test do
28
+ gem 'combustion', '~> 1.3'
28
29
  gem 'database_cleaner'
29
30
  gem 'factory_bot_rails'
30
31
  gem 'guard'
31
32
  gem 'guard-rspec'
32
33
  gem 'rspec'
34
+ gem 'rspec-rails'
33
35
  end
data/Gemfile.lock CHANGED
@@ -103,6 +103,10 @@ GEM
103
103
  bigdecimal (3.1.8)
104
104
  builder (3.3.0)
105
105
  coderay (1.1.3)
106
+ combustion (1.5.0)
107
+ activesupport (>= 3.0.0)
108
+ railties (>= 3.0.0)
109
+ thor (>= 0.14.6)
106
110
  concurrent-ruby (1.3.4)
107
111
  connection_pool (2.4.1)
108
112
  crass (1.0.6)
@@ -281,6 +285,14 @@ GEM
281
285
  rspec-mocks (3.13.2)
282
286
  diff-lcs (>= 1.2.0, < 2.0)
283
287
  rspec-support (~> 3.13.0)
288
+ rspec-rails (7.1.1)
289
+ actionpack (>= 7.0)
290
+ activesupport (>= 7.0)
291
+ railties (>= 7.0)
292
+ rspec-core (~> 3.13)
293
+ rspec-expectations (~> 3.13)
294
+ rspec-mocks (~> 3.13)
295
+ rspec-support (~> 3.13)
284
296
  rspec-support (3.13.1)
285
297
  securerandom (0.3.1)
286
298
  semver2 (3.4.2)
@@ -317,6 +329,7 @@ DEPENDENCIES
317
329
  activerecord (>= 6)
318
330
  activerecord-import
319
331
  activesupport
332
+ combustion (~> 1.3)
320
333
  database_cleaner
321
334
  factory_bot_rails
322
335
  guard
@@ -330,6 +343,7 @@ DEPENDENCIES
330
343
  rdoc (~> 3.12)
331
344
  rollbar
332
345
  rspec
346
+ rspec-rails
333
347
  simplecov
334
348
  standalone_migrations
335
349
  timecop
data/README.md CHANGED
@@ -358,7 +358,22 @@ For contributors on OSX, you may have difficulty installing mysql:
358
358
  gem install mysql2 -v '0.4.10' --source 'https://rubygems.org/' -- --with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include
359
359
  ```
360
360
 
361
+ == Testing
362
+
363
+ ```bash
364
+ # Initial setup
365
+ bin/setup
366
+
367
+ # Run tests
368
+ bin/test # Regular test suite (fast)
369
+ bin/test-rails # Rails integration tests (slower)
370
+ bin/test-all # Both test suites
371
+
372
+ # Interactive console
373
+ bin/console
374
+ ```
375
+
361
376
  == Copyright
362
377
 
363
- Copyright (c) 2016-2020 brettshollenberger. See LICENSE.txt for
378
+ Copyright (c) 2016-2025 brettshollenberger. See LICENSE.txt for
364
379
  further details.
data/Rakefile CHANGED
@@ -52,3 +52,57 @@ end
52
52
 
53
53
  require 'standalone_migrations'
54
54
  StandaloneMigrations::Tasks.load_tasks
55
+
56
+ # Custom tasks for common operations
57
+ namespace :spec do
58
+ require 'rspec/core/rake_task'
59
+
60
+ desc "Run regular specs (excluding Rails integration)"
61
+ RSpec::Core::RakeTask.new(:regular) do |t|
62
+ t.rspec_opts = "--exclude-pattern spec/rails_integration/**/*_spec.rb"
63
+ end
64
+
65
+ desc "Run Rails integration specs with Combustion"
66
+ RSpec::Core::RakeTask.new(:rails) do |t|
67
+ t.pattern = "spec/rails_integration/**/*_spec.rb"
68
+ end
69
+
70
+ desc "Run all specs (regular and Rails integration separately)"
71
+ task :all do
72
+ puts "\n========== Running Regular Specs ==========\n"
73
+ Rake::Task['spec:regular'].invoke
74
+ puts "\n========== Running Rails Integration Specs ==========\n"
75
+ Rake::Task['spec:rails'].invoke
76
+ end
77
+ end
78
+
79
+ desc "Run regular test suite (default)"
80
+ task :spec => 'spec:regular'
81
+
82
+ desc "Setup test database"
83
+ task :test_setup do
84
+ sh "bundle exec rake db:create"
85
+ sh "bundle exec rake db:migrate"
86
+ end
87
+
88
+ desc "Reset test database"
89
+ task :test_reset do
90
+ sh "bundle exec rake db:drop"
91
+ sh "bundle exec rake db:create"
92
+ sh "bundle exec rake db:migrate"
93
+ end
94
+
95
+ desc "Run linting and type checking"
96
+ task :lint do
97
+ puts "No linting configured yet. Consider adding rubocop."
98
+ end
99
+
100
+ desc "Console with the gem loaded"
101
+ task :console do
102
+ sh "bundle exec pry -r ./init.rb"
103
+ end
104
+
105
+ desc "List all available tasks"
106
+ task :help do
107
+ sh "rake -T"
108
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.4.2
1
+ 4.4.3
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # Launch an interactive console with the gem loaded
3
+
4
+ require "bundler/setup"
5
+ require "pry"
6
+ require_relative "../init"
7
+
8
+ puts "Loading Historiographer console..."
9
+ puts "Models available: Post, Author, Comment, User, etc."
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # Setup script for development
3
+
4
+ puts "Setting up Historiographer development environment..."
5
+
6
+ system("bundle install")
7
+ system("bundle exec rake db:create")
8
+ system("bundle exec rake db:migrate")
9
+ system("createdb historiographer_combustion_test 2>/dev/null")
10
+
11
+ puts "\nSetup complete! You can now run:"
12
+ puts " bin/test - Run regular tests"
13
+ puts " bin/test-rails - Run Rails integration tests"
14
+ puts " bin/test-all - Run all tests"
15
+ puts " bin/console - Launch interactive console"
data/bin/test ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # Run the regular test suite (excluding Rails integration tests)
3
+
4
+ puts "Running regular test suite..."
5
+ exec "bundle exec rspec --exclude-pattern spec/rails_integration/**/*_spec.rb"
data/bin/test-all ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # Run all tests (regular and Rails integration)
3
+
4
+ puts "\n========== Running Regular Test Suite ==========\n"
5
+ system("bundle exec rspec --exclude-pattern spec/rails_integration/**/*_spec.rb")
6
+
7
+ puts "\n========== Running Rails Integration Tests ==========\n"
8
+ system("bundle exec rspec spec/rails_integration")
9
+
10
+ exit($?.exitstatus || 0)
data/bin/test-rails ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # Run Rails integration tests with Combustion
3
+
4
+ puts "Running Rails integration tests..."
5
+ exec "bundle exec rspec spec/rails_integration/historiographer_rails_integration_spec.rb"
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: historiographer 4.4.2 ruby lib
5
+ # stub: historiographer 4.4.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "historiographer".freeze
9
- s.version = "4.4.2"
9
+ s.version = "4.4.3"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.date = "2025-08-22"
15
15
  s.description = "Creates separate tables for each history table".freeze
16
16
  s.email = "brett.shollenberger@gmail.com".freeze
17
+ s.executables = ["console".freeze, "setup".freeze, "test".freeze, "test-all".freeze, "test-rails".freeze]
17
18
  s.extra_rdoc_files = [
18
19
  "LICENSE.txt",
19
20
  "README.md"
@@ -23,6 +24,7 @@ Gem::Specification.new do |s|
23
24
  ".rspec",
24
25
  ".ruby-version",
25
26
  ".standalone_migrations",
27
+ "DEVELOPMENT.md",
26
28
  "Gemfile",
27
29
  "Gemfile.lock",
28
30
  "Guardfile",
@@ -30,6 +32,11 @@ Gem::Specification.new do |s|
30
32
  "README.md",
31
33
  "Rakefile",
32
34
  "VERSION",
35
+ "bin/console",
36
+ "bin/setup",
37
+ "bin/test",
38
+ "bin/test-all",
39
+ "bin/test-rails",
33
40
  "historiographer-4.1.12.gem",
34
41
  "historiographer-4.1.13.gem",
35
42
  "historiographer-4.1.14.gem",
@@ -49,6 +56,7 @@ Gem::Specification.new do |s|
49
56
  "lib/historiographer/safe.rb",
50
57
  "lib/historiographer/silent.rb",
51
58
  "lib/historiographer/version.rb",
59
+ "spec/combustion_helper.rb",
52
60
  "spec/db/database.yml",
53
61
  "spec/db/migrate/20161121212228_create_posts.rb",
54
62
  "spec/db/migrate/20161121212229_create_post_histories.rb",
@@ -71,10 +79,22 @@ Gem::Specification.new do |s|
71
79
  "spec/db/migrate/20250824000000_create_test_articles.rb",
72
80
  "spec/db/migrate/20250824000001_create_test_categories.rb",
73
81
  "spec/db/migrate/20250825000000_create_bylines.rb",
82
+ "spec/db/migrate/20250826000000_create_test_users.rb",
83
+ "spec/db/migrate/20250826000001_create_test_user_histories.rb",
84
+ "spec/db/migrate/20250826000002_create_test_websites.rb",
85
+ "spec/db/migrate/20250826000003_create_test_website_histories.rb",
74
86
  "spec/db/schema.rb",
75
87
  "spec/factories/post.rb",
76
- "spec/foreign_key_spec.rb",
77
88
  "spec/historiographer_spec.rb",
89
+ "spec/integration/historiographer_safe_integration_spec.rb",
90
+ "spec/internal/app/models/application_record.rb",
91
+ "spec/internal/app/models/deploy.rb",
92
+ "spec/internal/app/models/user.rb",
93
+ "spec/internal/app/models/website.rb",
94
+ "spec/internal/app/models/website_history.rb",
95
+ "spec/internal/config/database.yml",
96
+ "spec/internal/config/routes.rb",
97
+ "spec/internal/db/schema.rb",
78
98
  "spec/models/application_record.rb",
79
99
  "spec/models/author.rb",
80
100
  "spec/models/author_history.rb",
@@ -97,10 +117,15 @@ Gem::Specification.new do |s|
97
117
  "spec/models/test_article_history.rb",
98
118
  "spec/models/test_category.rb",
99
119
  "spec/models/test_category_history.rb",
120
+ "spec/models/test_user.rb",
121
+ "spec/models/test_user_history.rb",
122
+ "spec/models/test_website.rb",
123
+ "spec/models/test_website_history.rb",
100
124
  "spec/models/thing_with_compound_index.rb",
101
125
  "spec/models/thing_with_compound_index_history.rb",
102
126
  "spec/models/thing_without_history.rb",
103
127
  "spec/models/user.rb",
128
+ "spec/rails_integration/historiographer_rails_integration_spec.rb",
104
129
  "spec/spec_helper.rb"
105
130
  ]
106
131
  s.homepage = "http://github.com/brettshollenberger/historiographer".freeze
@@ -78,11 +78,24 @@ module Historiographer
78
78
  # "RetailerProductHistory."
79
79
  #
80
80
  foreign_class_name = base.name.gsub(/History$/) {} # e.g. "RetailerProductHistory" => "RetailerProduct"
81
- foreign_class = foreign_class_name.constantize
82
81
  association_name = foreign_class_name.split("::").last.underscore.to_sym # e.g. "RetailerProduct" => :retailer_product
83
82
 
84
- # Store the original class for method delegation
85
- class_variable_set(:@@original_class, foreign_class)
83
+ # Defer foreign class resolution to avoid load order issues
84
+ base.define_singleton_method :foreign_class do
85
+ return class_variable_get(:@@foreign_class) if class_variable_defined?(:@@foreign_class)
86
+ begin
87
+ foreign_class = foreign_class_name.constantize
88
+ class_variable_set(:@@foreign_class, foreign_class)
89
+ foreign_class
90
+ rescue NameError => e
91
+ # If the class isn't loaded yet, return nil and it will be retried later
92
+ nil
93
+ end
94
+ end
95
+
96
+ # Store the foreign class name for later use
97
+ class_variable_set(:@@foreign_class_name, foreign_class_name)
98
+ class_variable_set(:@@association_name, association_name)
86
99
 
87
100
  #
88
101
  # A History class will be linked to the user
@@ -94,12 +107,20 @@ module Historiographer
94
107
  #
95
108
  # To use histories, a user class must be defined.
96
109
  #
97
- unless foreign_class.ancestors.include?(Historiographer::Silent)
110
+ # Set up user association unless Silent module is included
111
+ # Defer this check until foreign_class is available
112
+ unless base.foreign_class && base.foreign_class.ancestors.include?(Historiographer::Silent)
98
113
  belongs_to :user, foreign_key: :history_user_id
99
114
  end
100
115
 
101
- # Add method_added hook to the original class
102
- foreign_class.singleton_class.class_eval do
116
+ # Add method_added hook to the original class when it's available
117
+ # This needs to be deferred until the foreign class is loaded
118
+ base.define_singleton_method :setup_method_delegation do
119
+ return unless foreign_class
120
+ return if class_variable_defined?(:@@method_delegation_setup) && class_variable_get(:@@method_delegation_setup)
121
+ class_variable_set(:@@method_delegation_setup, true)
122
+
123
+ foreign_class.singleton_class.class_eval do
103
124
  # Keep track of original method_added if it exists
104
125
  if method_defined?(:method_added)
105
126
  alias_method :original_method_added, :method_added
@@ -120,9 +141,9 @@ module Historiographer
120
141
  return unless method_obj.owner == self
121
142
 
122
143
  # Skip if we've already defined this method in the history class
123
- return if foreign_class.history_class.method_defined?(method_name)
144
+ return if self.history_class.method_defined?(method_name)
124
145
 
125
- foreign_class.history_class.class_eval do
146
+ self.history_class.class_eval do
126
147
  define_method(method_name) do |*args, **kwargs, &block|
127
148
  forward_method(method_name, *args, **kwargs, &block)
128
149
  end
@@ -134,19 +155,29 @@ module Historiographer
134
155
  end
135
156
 
136
157
  begin
137
- (foreign_class.columns.map(&:name) - ["id"]).each do |method_name|
138
- define_method(method_name) do |*args, **kwargs, &block|
139
- forward_method(method_name, *args, **kwargs, &block)
158
+ end
159
+ end
160
+
161
+ # Try to set up method delegation if foreign class is available
162
+ base.setup_method_delegation if base.foreign_class
163
+
164
+ # Also delegate existing methods from the foreign class
165
+ if base.foreign_class
166
+ begin
167
+ (base.foreign_class.columns.map(&:name) - ["id"]).each do |method_name|
168
+ define_method(method_name) do |*args, **kwargs, &block|
169
+ forward_method(method_name, *args, **kwargs, &block)
170
+ end
140
171
  end
172
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished
173
+ # Table might not exist yet during setup
141
174
  end
142
- rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished
143
- # Table might not exist yet during setup
144
175
  end
145
176
 
146
177
  # Add method_missing for any methods we might have missed
147
178
  def method_missing(method_name, *args, **kwargs, &block)
148
- original_class = self.class.class_variable_get(:@@original_class)
149
- if original_class.method_defined?(method_name)
179
+ original_class = self.class.foreign_class
180
+ if original_class && original_class.method_defined?(method_name)
150
181
  forward_method(method_name, *args, **kwargs, &block)
151
182
  else
152
183
  super
@@ -154,8 +185,8 @@ module Historiographer
154
185
  end
155
186
 
156
187
  def respond_to_missing?(method_name, include_private = false)
157
- original_class = self.class.class_variable_get(:@@original_class)
158
- original_class.method_defined?(method_name) || super
188
+ original_class = self.class.foreign_class
189
+ (original_class && original_class.method_defined?(method_name)) || super
159
190
  end
160
191
 
161
192
  #
@@ -241,29 +272,36 @@ module Historiographer
241
272
  history_classes = Thread.current[:historiographer_history_classes] ||= []
242
273
  history_classes << base
243
274
 
275
+ # Always define the setup_history_associations method
276
+ base.define_singleton_method :setup_history_associations do |force = false|
277
+ return if !force && class_variable_defined?(:@@associations_set_up) && class_variable_get(:@@associations_set_up)
278
+ class_variable_set(:@@associations_set_up, true)
279
+
280
+ return unless foreign_class
281
+
282
+ # Also set up method delegation if not already done
283
+ setup_method_delegation if respond_to?(:setup_method_delegation)
284
+
285
+ foreign_class.reflect_on_all_associations.each do |association|
286
+ begin
287
+ define_history_association(association)
288
+ rescue => e
289
+ # Log but don't fail
290
+ puts "Warning: Could not define history association #{association.name}: #{e.message}" if ENV['DEBUG']
291
+ end
292
+ end
293
+ end
294
+
244
295
  # Set up the after_initialize hook if we're in a Rails app
245
296
  if defined?(Rails) && Rails.respond_to?(:application) && Rails.application && Rails.application.config.respond_to?(:after_initialize)
246
297
  Rails.application.config.after_initialize do
247
298
  history_classes.each do |history_class|
299
+ history_class.setup_method_delegation if history_class.respond_to?(:setup_method_delegation)
248
300
  history_class.setup_history_associations
249
301
  end
250
302
  end
251
303
  else
252
- # For non-Rails environments (like our test suite), set up associations immediately
253
- # but defer if models aren't loaded yet
254
- base.define_singleton_method :setup_history_associations do |force = false|
255
- return if !force && class_variable_defined?(:@@associations_set_up) && class_variable_get(:@@associations_set_up)
256
- class_variable_set(:@@associations_set_up, true)
257
-
258
- foreign_class.reflect_on_all_associations.each do |association|
259
- begin
260
- define_history_association(association)
261
- rescue => e
262
- # Log but don't fail
263
- puts "Warning: Could not define history association #{association.name}: #{e.message}" if ENV['DEBUG']
264
- end
265
- end
266
- end
304
+ # For non-Rails environments, try to set up associations immediately
267
305
 
268
306
  # Try to set up now if possible
269
307
  begin
@@ -301,11 +339,8 @@ module Historiographer
301
339
  end
302
340
 
303
341
  def original_class
304
- unless class_variable_defined?(:@@original_class)
305
- class_variable_set(:@@original_class, self.name.gsub(/History$/, '').constantize)
306
- end
307
-
308
- class_variable_get(:@@original_class)
342
+ # Use the foreign_class method we defined earlier
343
+ foreign_class
309
344
  end
310
345
 
311
346
  def define_history_association(association)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RAILS_ENV'] ||= 'test'
4
+
5
+ require 'bundler'
6
+ Bundler.require :default, :test
7
+
8
+ require 'historiographer'
9
+ require 'combustion'
10
+
11
+ Combustion.path = 'spec/internal'
12
+ Combustion.initialize! :active_record do
13
+ config.load_defaults Rails::VERSION::STRING.to_f
14
+ end
15
+
16
+ require 'rspec/rails'
17
+ require 'database_cleaner'
18
+
19
+ RSpec.configure do |config|
20
+ config.use_transactional_fixtures = false
21
+
22
+ config.before(:suite) do
23
+ DatabaseCleaner.strategy = :transaction
24
+ DatabaseCleaner.clean_with(:truncation)
25
+ end
26
+
27
+ config.before(:each) do
28
+ DatabaseCleaner.start
29
+ end
30
+
31
+ config.after(:each) do
32
+ DatabaseCleaner.clean
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ class CreateTestUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :test_users do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ class CreateTestUserHistories < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :test_user_histories do |t|
4
+ t.integer :test_user_id, null: false
5
+ t.string :name
6
+ t.timestamps
7
+ t.datetime :history_started_at, null: false
8
+ t.datetime :history_ended_at
9
+ t.integer :history_user_id
10
+ t.string :snapshot_id
11
+
12
+ t.index :test_user_id
13
+ t.index :history_started_at
14
+ t.index :history_ended_at
15
+ t.index :snapshot_id
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ class CreateTestWebsites < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :test_websites do |t|
4
+ t.string :name
5
+ t.integer :user_id
6
+ t.timestamps
7
+ end
8
+ end
9
+ end