historiographer 4.4.2 → 4.4.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/DEVELOPMENT.md +124 -0
  3. data/Gemfile +2 -0
  4. data/README.md +16 -1
  5. data/Rakefile +68 -0
  6. data/VERSION +1 -1
  7. data/bin/console +10 -0
  8. data/bin/setup +15 -0
  9. data/bin/test +5 -0
  10. data/bin/test-all +10 -0
  11. data/bin/test-rails +5 -0
  12. data/historiographer.gemspec +52 -14
  13. data/lib/historiographer/history.rb +72 -37
  14. data/lib/historiographer.rb +5 -0
  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/migrate/20250827000000_create_templates.rb +9 -0
  21. data/spec/db/migrate/20250827000001_create_template_histories.rb +9 -0
  22. data/spec/db/migrate/20250827000002_create_websites.rb +9 -0
  23. data/spec/db/migrate/20250827000003_create_website_histories.rb +9 -0
  24. data/spec/db/migrate/20250827000004_create_template_files.rb +15 -0
  25. data/spec/db/migrate/20250827000005_create_template_file_histories.rb +9 -0
  26. data/spec/db/migrate/20250827000006_create_website_files.rb +15 -0
  27. data/spec/db/migrate/20250827000007_create_website_file_histories.rb +9 -0
  28. data/spec/db/migrate/20250827000008_create_code_files_view.rb +62 -0
  29. data/spec/db/schema.rb +170 -1
  30. data/spec/examples.txt +71 -0
  31. data/spec/historiographer_spec.rb +164 -0
  32. data/spec/integration/historiographer_safe_integration_spec.rb +154 -0
  33. data/spec/internal/app/models/application_record.rb +5 -0
  34. data/spec/internal/app/models/deploy.rb +5 -0
  35. data/spec/internal/app/models/user.rb +4 -0
  36. data/spec/internal/app/models/website.rb +5 -0
  37. data/spec/internal/app/models/website_history.rb +7 -0
  38. data/spec/internal/config/database.yml +9 -0
  39. data/spec/internal/config/routes.rb +2 -0
  40. data/spec/internal/db/schema.rb +48 -0
  41. data/spec/internal/log/development.log +0 -0
  42. data/spec/internal/log/test.log +1479 -0
  43. data/spec/models/code_file.rb +16 -0
  44. data/spec/models/template.rb +6 -0
  45. data/spec/models/template_file.rb +5 -0
  46. data/spec/models/template_file_history.rb +3 -0
  47. data/spec/models/template_history.rb +3 -0
  48. data/spec/models/test_user.rb +4 -0
  49. data/spec/models/test_user_history.rb +3 -0
  50. data/spec/models/test_website.rb +4 -0
  51. data/spec/models/test_website_history.rb +3 -0
  52. data/spec/models/website.rb +7 -0
  53. data/spec/models/website_file.rb +5 -0
  54. data/spec/models/website_file_history.rb +3 -0
  55. data/spec/models/website_history.rb +3 -0
  56. data/spec/rails_integration/historiographer_rails_integration_spec.rb +106 -0
  57. data/spec/view_backed_model_spec.rb +166 -0
  58. metadata +55 -13
  59. data/.document +0 -5
  60. data/.rspec +0 -1
  61. data/.ruby-version +0 -1
  62. data/.standalone_migrations +0 -6
  63. data/Gemfile.lock +0 -341
  64. data/historiographer-4.1.12.gem +0 -0
  65. data/historiographer-4.1.13.gem +0 -0
  66. data/historiographer-4.1.14.gem +0 -0
  67. data/historiographer-4.3.0.gem +0 -0
  68. data/spec/foreign_key_spec.rb +0 -189
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Historiographer::Safe Integration' do
4
+ # This test reproduces the exact error from a real Rails app where:
5
+ # 1. WebsiteHistory is defined first and includes Historiographer::History
6
+ # 2. Website is defined later and includes Historiographer::Safe
7
+ # 3. The error occurs because foreign_class.constantize fails when Website isn't loaded yet
8
+
9
+ context 'when history class is loaded before the main model' do
10
+ before(:each) do
11
+ # Ensure clean state
12
+ Object.send(:remove_const, :RealAppWebsite) if defined?(RealAppWebsite)
13
+ Object.send(:remove_const, :RealAppWebsiteHistory) if defined?(RealAppWebsiteHistory)
14
+
15
+ # Create the tables
16
+ ActiveRecord::Base.connection.create_table :real_app_websites, force: true do |t|
17
+ t.string :name
18
+ t.integer :project_id
19
+ t.integer :user_id
20
+ t.integer :template_id
21
+ t.timestamps
22
+ end
23
+
24
+ ActiveRecord::Base.connection.create_table :real_app_website_histories, force: true do |t|
25
+ t.integer :real_app_website_id, null: false
26
+ t.string :name
27
+ t.integer :project_id
28
+ t.integer :user_id
29
+ t.integer :template_id
30
+ t.datetime :created_at, null: false
31
+ t.datetime :updated_at, null: false
32
+ t.datetime :history_started_at, null: false
33
+ t.datetime :history_ended_at
34
+ t.integer :history_user_id
35
+ t.string :snapshot_id
36
+ t.string :thread_id
37
+ end
38
+
39
+ ActiveRecord::Base.connection.add_index :real_app_website_histories, :real_app_website_id
40
+ ActiveRecord::Base.connection.add_index :real_app_website_histories, :history_started_at
41
+ ActiveRecord::Base.connection.add_index :real_app_website_histories, :history_ended_at
42
+ end
43
+
44
+ after(:each) do
45
+ # Clean up tables
46
+ ActiveRecord::Base.connection.drop_table :real_app_website_histories if ActiveRecord::Base.connection.table_exists?(:real_app_website_histories)
47
+ ActiveRecord::Base.connection.drop_table :real_app_websites if ActiveRecord::Base.connection.table_exists?(:real_app_websites)
48
+
49
+ # Clean up constants
50
+ Object.send(:remove_const, :RealAppWebsiteHistory) if defined?(RealAppWebsiteHistory)
51
+ Object.send(:remove_const, :RealAppWebsite) if defined?(RealAppWebsite)
52
+ end
53
+
54
+ it 'handles history class being defined before the main model exists' do
55
+ # This is the exact scenario from the error report:
56
+ # WebsiteHistory is loaded/required first (common in Rails autoloading)
57
+
58
+ expect {
59
+ class RealAppWebsiteHistory < ApplicationRecord
60
+ self.table_name = 'real_app_website_histories'
61
+ include Historiographer::History
62
+ end
63
+ }.not_to raise_error
64
+
65
+ # At this point, RealAppWebsite doesn't exist yet
66
+ # The history class should handle this gracefully
67
+ expect(RealAppWebsiteHistory.foreign_class).to be_nil
68
+
69
+ # Now define the main model (simulating Rails autoloading it later)
70
+ class RealAppWebsite < ApplicationRecord
71
+ self.table_name = 'real_app_websites'
72
+ include Historiographer::Safe
73
+ end
74
+
75
+ # After the main model is defined, foreign_class should resolve
76
+ expect(RealAppWebsiteHistory.foreign_class).to eq(RealAppWebsite)
77
+
78
+ # And all functionality should work
79
+ website = RealAppWebsite.create!(name: 'Test Site', history_user_id: 1)
80
+ expect(website.histories.count).to eq(1)
81
+
82
+ history = website.histories.first
83
+ expect(history).to be_a(RealAppWebsiteHistory)
84
+ expect(history.real_app_website_id).to eq(website.id)
85
+ expect(history.name).to eq('Test Site')
86
+ end
87
+
88
+ it 'allows history class to define associations even when parent model is not loaded' do
89
+ # Define a Deploy model for association testing
90
+ ActiveRecord::Base.connection.create_table :deploys, force: true do |t|
91
+ t.integer :real_app_website_history_id
92
+ t.string :status
93
+ t.timestamps
94
+ end
95
+
96
+ class Deploy < ApplicationRecord
97
+ self.table_name = 'deploys'
98
+ end
99
+
100
+ # Define history class with associations before main model exists
101
+ expect {
102
+ class RealAppWebsiteHistory < ApplicationRecord
103
+ self.table_name = 'real_app_website_histories'
104
+ include Historiographer::History
105
+
106
+ # This should work even though RealAppWebsite doesn't exist yet
107
+ has_many :deploys, foreign_key: :real_app_website_history_id, dependent: :destroy
108
+ end
109
+ }.not_to raise_error
110
+
111
+ # Now define the main model
112
+ class RealAppWebsite < ApplicationRecord
113
+ self.table_name = 'real_app_websites'
114
+ include Historiographer::Safe
115
+ end
116
+
117
+ # Test that associations work
118
+ website = RealAppWebsite.create!(name: 'Test Site', history_user_id: 1)
119
+ history = website.histories.first
120
+
121
+ deploy = Deploy.create!(real_app_website_history_id: history.id, status: 'pending')
122
+ expect(history.deploys).to include(deploy)
123
+
124
+ # Clean up
125
+ ActiveRecord::Base.connection.drop_table :deploys
126
+ Object.send(:remove_const, :Deploy)
127
+ end
128
+
129
+ it 'supports after_initialize Rails hook when available' do
130
+ # Simulate Rails being available with after_initialize
131
+ rails_app = double('Rails App')
132
+ config = double('Rails Config')
133
+ allow(config).to receive(:after_initialize).and_yield
134
+ allow(rails_app).to receive(:config).and_return(config)
135
+ allow(Rails).to receive(:application).and_return(rails_app)
136
+
137
+ # Define history class
138
+ class RealAppWebsiteHistory < ApplicationRecord
139
+ self.table_name = 'real_app_website_histories'
140
+ include Historiographer::History
141
+ end
142
+
143
+ # Define main model
144
+ class RealAppWebsite < ApplicationRecord
145
+ self.table_name = 'real_app_websites'
146
+ include Historiographer::Safe
147
+ end
148
+
149
+ # The after_initialize should have set up associations
150
+ expect(RealAppWebsiteHistory).to respond_to(:setup_history_associations)
151
+ expect { RealAppWebsiteHistory.setup_history_associations }.not_to raise_error
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Deploy < ApplicationRecord
4
+ belongs_to :website_history
5
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class User < ApplicationRecord
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Website < ApplicationRecord
4
+ include Historiographer::Safe
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class WebsiteHistory < ApplicationRecord
4
+ include Historiographer::History
5
+
6
+ has_many :deploys
7
+ end
@@ -0,0 +1,9 @@
1
+ test:
2
+ adapter: postgresql
3
+ database: historiographer_combustion_test
4
+ username: <%= ENV['DB_USERNAME'] || 'postgres' %>
5
+ password: <%= ENV['DB_PASSWORD'] || '' %>
6
+ host: <%= ENV['DB_HOST'] || 'localhost' %>
7
+ port: <%= ENV['DB_PORT'] || 5432 %>
8
+ pool: 5
9
+ timeout: 5000
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ ActiveRecord::Schema.define(version: 1) do
4
+ # Website table - the main model
5
+ create_table :websites, force: true do |t|
6
+ t.string :name
7
+ t.integer :project_id
8
+ t.integer :user_id
9
+ t.integer :template_id
10
+ t.timestamps
11
+ end
12
+
13
+ # Website history table
14
+ create_table :website_histories, force: true do |t|
15
+ t.integer :website_id, null: false
16
+ t.string :name
17
+ t.integer :project_id
18
+ t.integer :user_id
19
+ t.integer :template_id
20
+ t.datetime :created_at, null: false
21
+ t.datetime :updated_at, null: false
22
+ t.datetime :history_started_at, null: false
23
+ t.datetime :history_ended_at
24
+ t.integer :history_user_id
25
+ t.string :snapshot_id
26
+ t.string :thread_id
27
+ end
28
+
29
+ add_index :website_histories, :website_id
30
+ add_index :website_histories, :history_started_at
31
+ add_index :website_histories, :history_ended_at
32
+ add_index :website_histories, :history_user_id
33
+ add_index :website_histories, :snapshot_id
34
+ add_index :website_histories, [:thread_id], unique: true, name: 'index_website_histories_on_thread_id'
35
+
36
+ # Deploy table - to test associations on history models
37
+ create_table :deploys, force: true do |t|
38
+ t.integer :website_history_id
39
+ t.string :status
40
+ t.timestamps
41
+ end
42
+
43
+ # User table
44
+ create_table :users, force: true do |t|
45
+ t.string :name
46
+ t.timestamps
47
+ end
48
+ end
File without changes