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.
- checksums.yaml +4 -4
- data/DEVELOPMENT.md +124 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +14 -0
- data/README.md +16 -1
- data/Rakefile +54 -0
- data/VERSION +1 -1
- data/bin/console +10 -0
- data/bin/setup +15 -0
- data/bin/test +5 -0
- data/bin/test-all +10 -0
- data/bin/test-rails +5 -0
- data/historiographer.gemspec +28 -3
- data/lib/historiographer/history.rb +72 -37
- data/spec/combustion_helper.rb +34 -0
- data/spec/db/migrate/20250826000000_create_test_users.rb +8 -0
- data/spec/db/migrate/20250826000001_create_test_user_histories.rb +18 -0
- data/spec/db/migrate/20250826000002_create_test_websites.rb +9 -0
- data/spec/db/migrate/20250826000003_create_test_website_histories.rb +19 -0
- data/spec/db/schema.rb +45 -1
- data/spec/historiographer_spec.rb +164 -0
- data/spec/integration/historiographer_safe_integration_spec.rb +154 -0
- data/spec/internal/app/models/application_record.rb +5 -0
- data/spec/internal/app/models/deploy.rb +5 -0
- data/spec/internal/app/models/user.rb +4 -0
- data/spec/internal/app/models/website.rb +5 -0
- data/spec/internal/app/models/website_history.rb +7 -0
- data/spec/internal/config/database.yml +9 -0
- data/spec/internal/config/routes.rb +2 -0
- data/spec/internal/db/schema.rb +48 -0
- data/spec/models/test_user.rb +4 -0
- data/spec/models/test_user_history.rb +3 -0
- data/spec/models/test_website.rb +4 -0
- data/spec/models/test_website_history.rb +3 -0
- data/spec/rails_integration/historiographer_rails_integration_spec.rb +106 -0
- metadata +32 -3
- data/spec/foreign_key_spec.rb +0 -189
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e539c6a610a6dd526ea49199d9d8f773dc8528c5e50960b3fc40615d87d81ffb
|
4
|
+
data.tar.gz: cc44f3a4a98e65763b27b869a36e4b8997e4bd9e3b1f988ee07a267b0b346dd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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-
|
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.
|
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
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
data/historiographer.gemspec
CHANGED
@@ -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.
|
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.
|
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
|
-
#
|
85
|
-
|
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
|
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
|
-
|
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
|
144
|
+
return if self.history_class.method_defined?(method_name)
|
124
145
|
|
125
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
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.
|
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.
|
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
|
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
|
-
|
305
|
-
|
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,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
|