historiographer 4.4.3 → 4.4.5

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -3
  3. data/Rakefile +14 -0
  4. data/VERSION +1 -1
  5. data/historiographer.gemspec +26 -13
  6. data/lib/historiographer/history.rb +4 -1
  7. data/lib/historiographer.rb +12 -7
  8. data/spec/db/migrate/20250827000000_create_templates.rb +9 -0
  9. data/spec/db/migrate/20250827000001_create_template_histories.rb +9 -0
  10. data/spec/db/migrate/20250827000002_create_websites.rb +9 -0
  11. data/spec/db/migrate/20250827000003_create_website_histories.rb +9 -0
  12. data/spec/db/migrate/20250827000004_create_template_files.rb +15 -0
  13. data/spec/db/migrate/20250827000005_create_template_file_histories.rb +9 -0
  14. data/spec/db/migrate/20250827000006_create_website_files.rb +15 -0
  15. data/spec/db/migrate/20250827000007_create_website_file_histories.rb +9 -0
  16. data/spec/db/migrate/20250827000008_create_code_files_view.rb +62 -0
  17. data/spec/db/schema.rb +126 -1
  18. data/spec/examples.txt +72 -0
  19. data/spec/historiographer_spec.rb +30 -0
  20. data/spec/internal/log/development.log +0 -0
  21. data/spec/internal/log/test.log +1479 -0
  22. data/spec/models/code_file.rb +16 -0
  23. data/spec/models/template.rb +6 -0
  24. data/spec/models/template_file.rb +5 -0
  25. data/spec/models/template_file_history.rb +3 -0
  26. data/spec/models/template_history.rb +3 -0
  27. data/spec/models/website.rb +7 -0
  28. data/spec/models/website_file.rb +5 -0
  29. data/spec/models/website_file_history.rb +3 -0
  30. data/spec/models/website_history.rb +3 -0
  31. data/spec/view_backed_model_spec.rb +166 -0
  32. metadata +24 -11
  33. data/.document +0 -5
  34. data/.rspec +0 -1
  35. data/.ruby-version +0 -1
  36. data/.standalone_migrations +0 -6
  37. data/Gemfile.lock +0 -355
  38. data/historiographer-4.1.12.gem +0 -0
  39. data/historiographer-4.1.13.gem +0 -0
  40. data/historiographer-4.1.14.gem +0 -0
  41. data/historiographer-4.3.0.gem +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e539c6a610a6dd526ea49199d9d8f773dc8528c5e50960b3fc40615d87d81ffb
4
- data.tar.gz: cc44f3a4a98e65763b27b869a36e4b8997e4bd9e3b1f988ee07a267b0b346dd3
3
+ metadata.gz: 99a33a798a97f440f2d4c0d2d1973303a013e99bb5f8d3f566c51a0d9375961f
4
+ data.tar.gz: 56b51522b7f4b254b9ba1e4e1813aa449dec80579e9e79c8e4f446e629ce74c7
5
5
  SHA512:
6
- metadata.gz: fb5d80e2d90f8c5dc2172b1a81f75b646750c3eff34c2921fa3a454d7905d05e59420c86c0b24d08eec96ace5f067e1b91e30c0d944675a84a2c5f2ad63e0fb8
7
- data.tar.gz: 2fedc59aa4c1fcb684e43f18a03a686ffdf4137fcef83e086583d064491a9cb16b7b17ca46f4893b1a3d83cdc4bc6a3977de4ae534454895fa0a53e0b7bdcf2d
6
+ metadata.gz: d27ffa3d2aaca0a91d9283c5f18701e7c576475d2619c569dd7edc21cdc8983c3376e823067ffb233a1f14ec4123823564b486c906da6b7860c285eb555ecc67
7
+ data.tar.gz: 7b1608e2c572ab02da5e2371679fe5facc463ac4d788e1e9e499a2078be074e3345758e9a9aed97931ade4c15f59fd220d2f8957e7d7101f44290217647a9ac2
data/README.md CHANGED
@@ -12,7 +12,7 @@ The Audited gem has some serious flaws.
12
12
 
13
13
  2. It doesn't provide the indexes you need from your primary tables
14
14
 
15
- 3. It doesn't provdie out-of-the-box snapshots
15
+ 3. It doesn't provide out-of-the-box snapshots
16
16
 
17
17
  ## How does Historiographer solve these problems?
18
18
 
@@ -86,14 +86,17 @@ You can take a snapshot of a record and all its associated records:
86
86
 
87
87
  ```ruby
88
88
  post = Post.find(1)
89
- post.snapshot(history_user_id: current_user.id)
89
+ post.snapshot
90
90
  ```
91
91
 
92
92
  This will:
93
93
 
94
94
  1. Create a history record for the post
95
- 2. Create history records for all associated records (comments, author, etc.)
95
+ 2. Recursively create history records for all associated records that also include Historiographer (comments, author, etc.)
96
96
  3. Link these history records together with a shared `snapshot_id`
97
+ 4. Skip any view-backed models (models without a primary key)
98
+
99
+ **Note:** The snapshot cascades to ALL associations where the associated model includes Historiographer. There is currently no built-in way to limit which associations get snapshotted. If you need finer control, you can either not include Historiographer on models you don't want snapshotted, or override the `snapshot` method.
97
100
 
98
101
  You can retrieve the latest snapshot using:
99
102
 
@@ -130,6 +133,38 @@ This can be useful when:
130
133
  - You're versioning training data for machine learning models
131
134
  - You need to maintain immutable audit trails at specific checkpoints
132
135
 
136
+ ## Safe and Silent Modes
137
+
138
+ Historiographer provides two additional modes for migrating existing models:
139
+
140
+ ### Historiographer::Safe
141
+
142
+ Use `Historiographer::Safe` when migrating an existing model to Historiographer. Instead of raising an error when `history_user_id` is missing, it logs to Rollbar, allowing you to find all locations that need to be updated:
143
+
144
+ ```ruby
145
+ class Post < ActiveRecord::Base
146
+ include Historiographer::Safe
147
+ end
148
+
149
+ # This will create a history record and log to Rollbar (instead of raising an error)
150
+ Post.create(title: "My Post")
151
+ ```
152
+
153
+ ### Historiographer::Silent
154
+
155
+ Use `Historiographer::Silent` when you want to allow missing `history_user_id` without any errors or logging:
156
+
157
+ ```ruby
158
+ class Post < ActiveRecord::Base
159
+ include Historiographer::Silent
160
+ end
161
+
162
+ # This will create a history record silently without history_user_id
163
+ Post.create(title: "My Post")
164
+ ```
165
+
166
+ **Note:** Both Safe and Silent modes are intended for migration purposes, not as long-term solutions.
167
+
133
168
  ## Namespaced Models
134
169
 
135
170
  When using namespaced models, Rails handles foreign key naming differently than with non-namespaced models. For example, if you have a model namespaced like this:
@@ -296,6 +331,7 @@ You should also make a `PostHistory` class if you're going to query `PostHistory
296
331
  ```ruby
297
332
  class PostHistory < ActiveRecord::Base
298
333
  self.table_name = "post_histories"
334
+ include Historiographer::History
299
335
  end
300
336
  ```
301
337
 
@@ -323,6 +359,21 @@ Post.last.destroy!(history_user_id: current_user.id)
323
359
  Post.destroy_all(history_user_id: current_user.id)
324
360
  ```
325
361
 
362
+ ### Skipping History
363
+
364
+ If you need to save a record without creating a history entry:
365
+
366
+ ```ruby
367
+ post = Post.new(title: "My Post")
368
+ post.save_without_history # No history_user_id required
369
+ post.save_without_history! # Bang version
370
+
371
+ # For bulk operations:
372
+ Post.all.update_all_without_history(title: "New Title")
373
+ Post.all.delete_all_without_history
374
+ Post.all.destroy_all_without_history
375
+ ```
376
+
326
377
  The `histories` classes have a `current` method, which only finds current history records. These records will also be the same as the data in the primary table.
327
378
 
328
379
  ```ruby
data/Rakefile CHANGED
@@ -22,6 +22,20 @@ Jeweler::Tasks.new do |gem|
22
22
  gem.description = %Q{Creates separate tables for each history table}
23
23
  gem.email = "brett.shollenberger@gmail.com"
24
24
  gem.authors = ["brettshollenberger"]
25
+
26
+ # Use glob patterns to automatically include all relevant files
27
+ gem.files = `git ls-files`.split("\n").reject { |f|
28
+ f.match(/^(test|spec|features)\//) ||
29
+ f.match(/\.gem$/) ||
30
+ f.match(/^\./) ||
31
+ f == 'Gemfile.lock'
32
+ }
33
+
34
+ # Include spec files for development/testing
35
+ gem.files += Dir.glob("spec/**/*")
36
+
37
+ # Ensure we have all the executables
38
+ gem.executables = Dir.glob("bin/*").map { |f| File.basename(f) }
25
39
  end
26
40
  Jeweler::RubygemsDotOrgTasks.new
27
41
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.4.3
1
+ 4.4.5
@@ -2,16 +2,16 @@
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.3 ruby lib
5
+ # stub: historiographer 4.4.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "historiographer".freeze
9
- s.version = "4.4.3"
9
+ s.version = "4.4.5"
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]
13
13
  s.authors = ["brettshollenberger".freeze]
14
- s.date = "2025-08-22"
14
+ s.date = "2026-01-14"
15
15
  s.description = "Creates separate tables for each history table".freeze
16
16
  s.email = "brett.shollenberger@gmail.com".freeze
17
17
  s.executables = ["console".freeze, "setup".freeze, "test".freeze, "test-all".freeze, "test-rails".freeze]
@@ -20,13 +20,8 @@ Gem::Specification.new do |s|
20
20
  "README.md"
21
21
  ]
22
22
  s.files = [
23
- ".document",
24
- ".rspec",
25
- ".ruby-version",
26
- ".standalone_migrations",
27
23
  "DEVELOPMENT.md",
28
24
  "Gemfile",
29
- "Gemfile.lock",
30
25
  "Guardfile",
31
26
  "LICENSE.txt",
32
27
  "README.md",
@@ -37,10 +32,6 @@ Gem::Specification.new do |s|
37
32
  "bin/test",
38
33
  "bin/test-all",
39
34
  "bin/test-rails",
40
- "historiographer-4.1.12.gem",
41
- "historiographer-4.1.13.gem",
42
- "historiographer-4.1.14.gem",
43
- "historiographer-4.3.0.gem",
44
35
  "historiographer.gemspec",
45
36
  "init.rb",
46
37
  "instructions/implementation.md",
@@ -83,7 +74,17 @@ Gem::Specification.new do |s|
83
74
  "spec/db/migrate/20250826000001_create_test_user_histories.rb",
84
75
  "spec/db/migrate/20250826000002_create_test_websites.rb",
85
76
  "spec/db/migrate/20250826000003_create_test_website_histories.rb",
77
+ "spec/db/migrate/20250827000000_create_templates.rb",
78
+ "spec/db/migrate/20250827000001_create_template_histories.rb",
79
+ "spec/db/migrate/20250827000002_create_websites.rb",
80
+ "spec/db/migrate/20250827000003_create_website_histories.rb",
81
+ "spec/db/migrate/20250827000004_create_template_files.rb",
82
+ "spec/db/migrate/20250827000005_create_template_file_histories.rb",
83
+ "spec/db/migrate/20250827000006_create_website_files.rb",
84
+ "spec/db/migrate/20250827000007_create_website_file_histories.rb",
85
+ "spec/db/migrate/20250827000008_create_code_files_view.rb",
86
86
  "spec/db/schema.rb",
87
+ "spec/examples.txt",
87
88
  "spec/factories/post.rb",
88
89
  "spec/historiographer_spec.rb",
89
90
  "spec/integration/historiographer_safe_integration_spec.rb",
@@ -95,10 +96,13 @@ Gem::Specification.new do |s|
95
96
  "spec/internal/config/database.yml",
96
97
  "spec/internal/config/routes.rb",
97
98
  "spec/internal/db/schema.rb",
99
+ "spec/internal/log/development.log",
100
+ "spec/internal/log/test.log",
98
101
  "spec/models/application_record.rb",
99
102
  "spec/models/author.rb",
100
103
  "spec/models/author_history.rb",
101
104
  "spec/models/byline.rb",
105
+ "spec/models/code_file.rb",
102
106
  "spec/models/comment.rb",
103
107
  "spec/models/comment_history.rb",
104
108
  "spec/models/easy_ml/column.rb",
@@ -113,6 +117,10 @@ Gem::Specification.new do |s|
113
117
  "spec/models/safe_post_history.rb",
114
118
  "spec/models/silent_post.rb",
115
119
  "spec/models/silent_post_history.rb",
120
+ "spec/models/template.rb",
121
+ "spec/models/template_file.rb",
122
+ "spec/models/template_file_history.rb",
123
+ "spec/models/template_history.rb",
116
124
  "spec/models/test_article.rb",
117
125
  "spec/models/test_article_history.rb",
118
126
  "spec/models/test_category.rb",
@@ -125,8 +133,13 @@ Gem::Specification.new do |s|
125
133
  "spec/models/thing_with_compound_index_history.rb",
126
134
  "spec/models/thing_without_history.rb",
127
135
  "spec/models/user.rb",
136
+ "spec/models/website.rb",
137
+ "spec/models/website_file.rb",
138
+ "spec/models/website_file_history.rb",
139
+ "spec/models/website_history.rb",
128
140
  "spec/rails_integration/historiographer_rails_integration_spec.rb",
129
- "spec/spec_helper.rb"
141
+ "spec/spec_helper.rb",
142
+ "spec/view_backed_model_spec.rb"
130
143
  ]
131
144
  s.homepage = "http://github.com/brettshollenberger/historiographer".freeze
132
145
  s.licenses = ["MIT".freeze]
@@ -109,8 +109,11 @@ module Historiographer
109
109
  #
110
110
  # Set up user association unless Silent module is included
111
111
  # Defer this check until foreign_class is available
112
+ # Use optional: true because history_user_id validation is handled separately
113
+ # by Historiographer's validate_history_user_id_present method, and we need
114
+ # to allow updates without a user when using without_history_user_id blocks
112
115
  unless base.foreign_class && base.foreign_class.ancestors.include?(Historiographer::Silent)
113
- belongs_to :user, foreign_key: :history_user_id
116
+ belongs_to :user, foreign_key: :history_user_id, optional: true
114
117
  end
115
118
 
116
119
  # Add method_added hook to the original class when it's available
@@ -188,7 +188,7 @@ module Historiographer
188
188
  rescue NameError
189
189
  # Get the base table name without _histories suffix
190
190
  base_table = base.table_name.singularize.sub(/_histories$/, '')
191
-
191
+
192
192
  history_class_initializer = Class.new(ActiveRecord::Base) do
193
193
  self.table_name = "#{base_table}_histories"
194
194
  end
@@ -204,7 +204,7 @@ module Historiographer
204
204
 
205
205
  # Set the constant in the correct module
206
206
  history_class = target_module.const_set(final_class_name, history_class_initializer)
207
-
207
+
208
208
  # Now that the class is named, include the History module and extend class methods
209
209
  history_class.send(:include, Historiographer::History)
210
210
  end
@@ -279,7 +279,7 @@ module Historiographer
279
279
  save!(*args, &block)
280
280
  @no_history = false
281
281
  end
282
-
282
+
283
283
  def snapshot(tree = {}, snapshot_id = nil)
284
284
  return if is_history_class?
285
285
 
@@ -296,7 +296,7 @@ module Historiographer
296
296
  null_snapshot = history_class.where(foreign_key => attrs[primary_key], snapshot_id: nil).first
297
297
  snapshot = nil
298
298
  if null_snapshot.present?
299
- null_snapshot.update(snapshot_id: snapshot_id)
299
+ null_snapshot.update!(snapshot_id: snapshot_id)
300
300
  snapshot = null_snapshot
301
301
  else
302
302
  snapshot = record_history(snapshot_id: snapshot_id)
@@ -304,6 +304,11 @@ module Historiographer
304
304
 
305
305
  # Recursively snapshot associations, avoiding infinite loops
306
306
  self.class.reflect_on_all_associations.each do |association|
307
+ # Skip associations to models without primary keys (e.g., database views)
308
+ association_class = association.klass rescue nil
309
+ next if association_class.nil?
310
+ next if association_class.primary_key.nil?
311
+
307
312
  associated_records = send(association.name)&.reload
308
313
  if associated_records.respond_to?(:order)
309
314
  associated_records = associated_records.order(id: :asc)
@@ -378,7 +383,7 @@ module Historiographer
378
383
 
379
384
  if history_class.history_foreign_key.present? && history_class.present?
380
385
  result = history_class.insert_all([attrs])
381
-
386
+
382
387
  # Check if the insertion was successful
383
388
  if result.rows.empty?
384
389
  # insert_all returned empty rows, likely due to a duplicate/conflict
@@ -388,7 +393,7 @@ module Historiographer
388
393
  foreign_key => attrs[foreign_key],
389
394
  history_started_at: attrs['history_started_at']
390
395
  ).first
391
-
396
+
392
397
  if existing_history
393
398
  # A duplicate history already exists (race condition or retry)
394
399
  # This is acceptable - return the existing history
@@ -399,7 +404,7 @@ module Historiographer
399
404
  history_class.create!(attrs) # This will raise the correct error since it will fail the unique constraint
400
405
  end
401
406
  end
402
-
407
+
403
408
  inserted_id = result.rows.first.first if history_class.primary_key == 'id'
404
409
  instance = history_class.find(inserted_id)
405
410
  current_history.update_columns(history_ended_at: now) if current_history.present?
@@ -0,0 +1,9 @@
1
+ class CreateTemplates < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :templates do |t|
4
+ t.string :name, null: false
5
+ t.text :description
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'historiographer/history_migration'
2
+
3
+ class CreateTemplateHistories < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :template_histories do |t|
6
+ t.histories
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class CreateWebsites < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :websites do |t|
4
+ t.string :domain, null: false
5
+ t.references :template, foreign_key: true
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'historiographer/history_migration'
2
+
3
+ class CreateWebsiteHistories < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :website_histories do |t|
6
+ t.histories
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class CreateTemplateFiles < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :template_files do |t|
4
+ t.references :template, foreign_key: true, null: false
5
+ t.string :path, null: false
6
+ t.text :content
7
+ t.tsvector :content_tsv
8
+ t.string :shasum
9
+ t.integer :file_specification_id
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :template_files, [:template_id, :path], unique: true
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'historiographer/history_migration'
2
+
3
+ class CreateTemplateFileHistories < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :template_file_histories do |t|
6
+ t.histories
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class CreateWebsiteFiles < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :website_files do |t|
4
+ t.references :website, foreign_key: true, null: false
5
+ t.string :path, null: false
6
+ t.text :content
7
+ t.tsvector :content_tsv
8
+ t.string :shasum
9
+ t.integer :file_specification_id
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :website_files, [:website_id, :path], unique: true
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'historiographer/history_migration'
2
+
3
+ class CreateWebsiteFileHistories < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :website_file_histories do |t|
6
+ t.histories
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ class CreateCodeFilesView < ActiveRecord::Migration[7.0]
2
+ def up
3
+ execute <<-SQL
4
+ CREATE OR REPLACE VIEW code_files AS
5
+ WITH merged_files AS (
6
+ -- Get all website files
7
+ SELECT
8
+ wf.website_id,
9
+ wf.path,
10
+ wf.content,
11
+ wf.content_tsv,
12
+ wf.shasum,
13
+ wf.file_specification_id,
14
+ wf.created_at,
15
+ wf.updated_at,
16
+ 'WebsiteFile' AS source_type,
17
+ wf.id AS source_id
18
+ FROM website_files wf
19
+
20
+ UNION ALL
21
+
22
+ -- Get template files that don't have a matching website file
23
+ SELECT
24
+ w.id AS website_id,
25
+ tf.path,
26
+ tf.content,
27
+ tf.content_tsv,
28
+ tf.shasum,
29
+ tf.file_specification_id,
30
+ tf.created_at,
31
+ tf.updated_at,
32
+ 'TemplateFile' AS source_type,
33
+ tf.id AS source_id
34
+ FROM template_files tf
35
+ INNER JOIN websites w ON w.template_id = tf.template_id
36
+ WHERE NOT EXISTS (
37
+ SELECT 1
38
+ FROM website_files wf2
39
+ WHERE wf2.website_id = w.id
40
+ AND wf2.path = tf.path
41
+ )
42
+ )
43
+ SELECT
44
+ website_id,
45
+ path,
46
+ content,
47
+ content_tsv,
48
+ shasum,
49
+ file_specification_id,
50
+ source_type,
51
+ source_id,
52
+ created_at,
53
+ updated_at
54
+ FROM merged_files
55
+ ORDER BY website_id, path;
56
+ SQL
57
+ end
58
+
59
+ def down
60
+ execute "DROP VIEW IF EXISTS code_files;"
61
+ end
62
+ end
data/spec/db/schema.rb CHANGED
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema[7.1].define(version: 2025_08_26_000003) do
13
+ ActiveRecord::Schema[7.1].define(version: 2025_08_27_000008) do
14
14
  # These are extensions that must be enabled in order to support this database
15
15
  enable_extension "plpgsql"
16
16
 
@@ -299,6 +299,66 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_26_000003) do
299
299
  t.index ["live_at"], name: "index_silent_posts_on_live_at"
300
300
  end
301
301
 
302
+ create_table "template_file_histories", force: :cascade do |t|
303
+ t.integer "template_file_id", null: false
304
+ t.integer "template_id", null: false
305
+ t.string "path", null: false
306
+ t.text "content"
307
+ t.tsvector "content_tsv"
308
+ t.string "shasum"
309
+ t.integer "file_specification_id"
310
+ t.datetime "created_at", null: false
311
+ t.datetime "updated_at", null: false
312
+ t.datetime "history_started_at", null: false
313
+ t.datetime "history_ended_at"
314
+ t.integer "history_user_id"
315
+ t.string "snapshot_id"
316
+ t.index ["history_ended_at"], name: "index_template_file_histories_on_history_ended_at"
317
+ t.index ["history_started_at"], name: "index_template_file_histories_on_history_started_at"
318
+ t.index ["history_user_id"], name: "index_template_file_histories_on_history_user_id"
319
+ t.index ["snapshot_id"], name: "index_template_file_histories_on_snapshot_id"
320
+ t.index ["template_file_id"], name: "index_template_file_histories_on_template_file_id"
321
+ t.index ["template_id", "path"], name: "index_template_file_histories_on_template_id_and_path"
322
+ t.index ["template_id"], name: "index_template_file_histories_on_template_id"
323
+ end
324
+
325
+ create_table "template_files", force: :cascade do |t|
326
+ t.bigint "template_id", null: false
327
+ t.string "path", null: false
328
+ t.text "content"
329
+ t.tsvector "content_tsv"
330
+ t.string "shasum"
331
+ t.integer "file_specification_id"
332
+ t.datetime "created_at", null: false
333
+ t.datetime "updated_at", null: false
334
+ t.index ["template_id", "path"], name: "index_template_files_on_template_id_and_path", unique: true
335
+ t.index ["template_id"], name: "index_template_files_on_template_id"
336
+ end
337
+
338
+ create_table "template_histories", force: :cascade do |t|
339
+ t.integer "template_id", null: false
340
+ t.string "name", null: false
341
+ t.text "description"
342
+ t.datetime "created_at", null: false
343
+ t.datetime "updated_at", null: false
344
+ t.datetime "history_started_at", null: false
345
+ t.datetime "history_ended_at"
346
+ t.integer "history_user_id"
347
+ t.string "snapshot_id"
348
+ t.index ["history_ended_at"], name: "index_template_histories_on_history_ended_at"
349
+ t.index ["history_started_at"], name: "index_template_histories_on_history_started_at"
350
+ t.index ["history_user_id"], name: "index_template_histories_on_history_user_id"
351
+ t.index ["snapshot_id"], name: "index_template_histories_on_snapshot_id"
352
+ t.index ["template_id"], name: "index_template_histories_on_template_id"
353
+ end
354
+
355
+ create_table "templates", force: :cascade do |t|
356
+ t.string "name", null: false
357
+ t.text "description"
358
+ t.datetime "created_at", null: false
359
+ t.datetime "updated_at", null: false
360
+ end
361
+
302
362
  create_table "test_article_histories", force: :cascade do |t|
303
363
  t.integer "test_article_id", null: false
304
364
  t.string "title"
@@ -419,4 +479,69 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_26_000003) do
419
479
  t.string "name"
420
480
  end
421
481
 
482
+ create_table "website_file_histories", force: :cascade do |t|
483
+ t.integer "website_file_id", null: false
484
+ t.integer "website_id", null: false
485
+ t.string "path", null: false
486
+ t.text "content"
487
+ t.tsvector "content_tsv"
488
+ t.string "shasum"
489
+ t.integer "file_specification_id"
490
+ t.datetime "created_at", null: false
491
+ t.datetime "updated_at", null: false
492
+ t.datetime "history_started_at", null: false
493
+ t.datetime "history_ended_at"
494
+ t.integer "history_user_id"
495
+ t.string "snapshot_id"
496
+ t.index ["history_ended_at"], name: "index_website_file_histories_on_history_ended_at"
497
+ t.index ["history_started_at"], name: "index_website_file_histories_on_history_started_at"
498
+ t.index ["history_user_id"], name: "index_website_file_histories_on_history_user_id"
499
+ t.index ["snapshot_id"], name: "index_website_file_histories_on_snapshot_id"
500
+ t.index ["website_file_id"], name: "index_website_file_histories_on_website_file_id"
501
+ t.index ["website_id", "path"], name: "index_website_file_histories_on_website_id_and_path"
502
+ t.index ["website_id"], name: "index_website_file_histories_on_website_id"
503
+ end
504
+
505
+ create_table "website_files", force: :cascade do |t|
506
+ t.bigint "website_id", null: false
507
+ t.string "path", null: false
508
+ t.text "content"
509
+ t.tsvector "content_tsv"
510
+ t.string "shasum"
511
+ t.integer "file_specification_id"
512
+ t.datetime "created_at", null: false
513
+ t.datetime "updated_at", null: false
514
+ t.index ["website_id", "path"], name: "index_website_files_on_website_id_and_path", unique: true
515
+ t.index ["website_id"], name: "index_website_files_on_website_id"
516
+ end
517
+
518
+ create_table "website_histories", force: :cascade do |t|
519
+ t.integer "website_id", null: false
520
+ t.string "domain", null: false
521
+ t.integer "template_id"
522
+ t.datetime "created_at", null: false
523
+ t.datetime "updated_at", null: false
524
+ t.datetime "history_started_at", null: false
525
+ t.datetime "history_ended_at"
526
+ t.integer "history_user_id"
527
+ t.string "snapshot_id"
528
+ t.index ["history_ended_at"], name: "index_website_histories_on_history_ended_at"
529
+ t.index ["history_started_at"], name: "index_website_histories_on_history_started_at"
530
+ t.index ["history_user_id"], name: "index_website_histories_on_history_user_id"
531
+ t.index ["snapshot_id"], name: "index_website_histories_on_snapshot_id"
532
+ t.index ["template_id"], name: "index_website_histories_on_template_id"
533
+ t.index ["website_id"], name: "index_website_histories_on_website_id"
534
+ end
535
+
536
+ create_table "websites", force: :cascade do |t|
537
+ t.string "domain", null: false
538
+ t.bigint "template_id"
539
+ t.datetime "created_at", null: false
540
+ t.datetime "updated_at", null: false
541
+ t.index ["template_id"], name: "index_websites_on_template_id"
542
+ end
543
+
544
+ add_foreign_key "template_files", "templates"
545
+ add_foreign_key "website_files", "websites"
546
+ add_foreign_key "websites", "templates"
422
547
  end