historiographer 4.1.14 → 4.3.0
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/.document +5 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.standalone_migrations +6 -0
- data/Gemfile +33 -0
- data/Gemfile.lock +341 -0
- data/Guardfile +4 -0
- data/README.md +0 -168
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/historiographer-4.1.12.gem +0 -0
- data/historiographer-4.1.13.gem +0 -0
- data/historiographer-4.1.14.gem +0 -0
- data/historiographer.gemspec +136 -0
- data/init.rb +18 -0
- data/instructions/implementation.md +282 -0
- data/instructions/todo.md +96 -0
- data/lib/historiographer/history.rb +1 -20
- data/lib/historiographer/version.rb +1 -1
- data/lib/historiographer.rb +27 -14
- data/spec/db/database.yml +27 -0
- data/spec/db/migrate/20161121212228_create_posts.rb +19 -0
- data/spec/db/migrate/20161121212229_create_post_histories.rb +10 -0
- data/spec/db/migrate/20161121212230_create_authors.rb +13 -0
- data/spec/db/migrate/20161121212231_create_author_histories.rb +10 -0
- data/spec/db/migrate/20161121212232_create_users.rb +9 -0
- data/spec/db/migrate/20171011194624_create_safe_posts.rb +19 -0
- data/spec/db/migrate/20171011194715_create_safe_post_histories.rb +9 -0
- data/spec/db/migrate/20191024142304_create_thing_with_compound_index.rb +10 -0
- data/spec/db/migrate/20191024142352_create_thing_with_compound_index_history.rb +11 -0
- data/spec/db/migrate/20191024203106_create_thing_without_history.rb +7 -0
- data/spec/db/migrate/20221018204220_create_silent_posts.rb +21 -0
- data/spec/db/migrate/20221018204255_create_silent_post_histories.rb +9 -0
- data/spec/db/migrate/20241109182017_create_comments.rb +13 -0
- data/spec/db/migrate/20241109182020_create_comment_histories.rb +9 -0
- data/spec/db/migrate/20241119000000_create_datasets.rb +17 -0
- data/spec/db/migrate/2025082100000_create_projects.rb +14 -0
- data/spec/db/migrate/2025082100001_create_project_files.rb +18 -0
- data/spec/db/schema.rb +352 -0
- data/spec/factories/post.rb +7 -0
- data/spec/historiographer_spec.rb +920 -0
- data/spec/models/application_record.rb +3 -0
- data/spec/models/author.rb +5 -0
- data/spec/models/author_history.rb +4 -0
- data/spec/models/comment.rb +5 -0
- data/spec/models/comment_history.rb +5 -0
- data/spec/models/easy_ml/column.rb +6 -0
- data/spec/models/easy_ml/column_history.rb +6 -0
- data/spec/models/post.rb +45 -0
- data/spec/models/post_history.rb +8 -0
- data/spec/models/project.rb +4 -0
- data/spec/models/project_file.rb +5 -0
- data/spec/models/project_file_history.rb +4 -0
- data/spec/models/project_history.rb +4 -0
- data/spec/models/safe_post.rb +5 -0
- data/spec/models/safe_post_history.rb +5 -0
- data/spec/models/silent_post.rb +3 -0
- data/spec/models/silent_post_history.rb +4 -0
- data/spec/models/thing_with_compound_index.rb +3 -0
- data/spec/models/thing_with_compound_index_history.rb +4 -0
- data/spec/models/thing_without_history.rb +2 -0
- data/spec/models/user.rb +2 -0
- data/spec/spec_helper.rb +105 -0
- metadata +62 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df0f832698c8177c8785d913caa4c26e2374e10ea813896190481b091a3176a6
|
4
|
+
data.tar.gz: 3bcf25861fed71c432c47e97489b2ffae7a42d70960e6d321ce8b4b24c8a5c89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df1430488c6120b9126aff4a526fb2aba8f84a6aad8690592b977a45b5031674ee3722818c8679881c96cd950b50a81089d4a363948cddcf65e253051792a524
|
7
|
+
data.tar.gz: 4bb03df9ecd8998fb1866bc2d5ada28b509d1122787e6ef7b47375d0a3dc18bd8bd14f8e846d1b6e5702c9a2f5a53366680d6855126c742c48526d9048d211d4
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.2
|
data/Gemfile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
ruby '3.0.2'
|
5
|
+
|
6
|
+
gem 'activerecord', '>= 6'
|
7
|
+
gem 'activerecord-import'
|
8
|
+
gem 'activesupport'
|
9
|
+
gem 'rails', '>= 6'
|
10
|
+
gem 'rollbar'
|
11
|
+
|
12
|
+
group :development, :test do
|
13
|
+
gem 'mysql2', '0.5'
|
14
|
+
gem 'paranoia'
|
15
|
+
gem 'pg'
|
16
|
+
gem 'pry'
|
17
|
+
gem 'standalone_migrations'
|
18
|
+
gem 'timecop'
|
19
|
+
end
|
20
|
+
|
21
|
+
group :development do
|
22
|
+
gem 'jeweler', git: 'https://github.com/technicalpickles/jeweler', branch: 'master'
|
23
|
+
gem 'rdoc', '~> 3.12'
|
24
|
+
gem 'simplecov', '>= 0'
|
25
|
+
end
|
26
|
+
|
27
|
+
group :test do
|
28
|
+
gem 'database_cleaner'
|
29
|
+
gem 'factory_bot_rails'
|
30
|
+
gem 'guard'
|
31
|
+
gem 'guard-rspec'
|
32
|
+
gem 'rspec'
|
33
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/technicalpickles/jeweler
|
3
|
+
revision: 2ab86309fc2494ba2a4e9c86c514742cd4f681c2
|
4
|
+
branch: master
|
5
|
+
specs:
|
6
|
+
jeweler (2.3.9)
|
7
|
+
builder
|
8
|
+
bundler
|
9
|
+
git (>= 1.2.5)
|
10
|
+
github_api (~> 0.16.0)
|
11
|
+
highline (>= 1.6.15)
|
12
|
+
nokogiri (>= 1.5.10)
|
13
|
+
psych
|
14
|
+
rake
|
15
|
+
rdoc
|
16
|
+
semver2
|
17
|
+
|
18
|
+
GEM
|
19
|
+
remote: https://rubygems.org/
|
20
|
+
specs:
|
21
|
+
actioncable (7.1.5)
|
22
|
+
actionpack (= 7.1.5)
|
23
|
+
activesupport (= 7.1.5)
|
24
|
+
nio4r (~> 2.0)
|
25
|
+
websocket-driver (>= 0.6.1)
|
26
|
+
zeitwerk (~> 2.6)
|
27
|
+
actionmailbox (7.1.5)
|
28
|
+
actionpack (= 7.1.5)
|
29
|
+
activejob (= 7.1.5)
|
30
|
+
activerecord (= 7.1.5)
|
31
|
+
activestorage (= 7.1.5)
|
32
|
+
activesupport (= 7.1.5)
|
33
|
+
mail (>= 2.7.1)
|
34
|
+
net-imap
|
35
|
+
net-pop
|
36
|
+
net-smtp
|
37
|
+
actionmailer (7.1.5)
|
38
|
+
actionpack (= 7.1.5)
|
39
|
+
actionview (= 7.1.5)
|
40
|
+
activejob (= 7.1.5)
|
41
|
+
activesupport (= 7.1.5)
|
42
|
+
mail (~> 2.5, >= 2.5.4)
|
43
|
+
net-imap
|
44
|
+
net-pop
|
45
|
+
net-smtp
|
46
|
+
rails-dom-testing (~> 2.2)
|
47
|
+
actionpack (7.1.5)
|
48
|
+
actionview (= 7.1.5)
|
49
|
+
activesupport (= 7.1.5)
|
50
|
+
nokogiri (>= 1.8.5)
|
51
|
+
racc
|
52
|
+
rack (>= 2.2.4)
|
53
|
+
rack-session (>= 1.0.1)
|
54
|
+
rack-test (>= 0.6.3)
|
55
|
+
rails-dom-testing (~> 2.2)
|
56
|
+
rails-html-sanitizer (~> 1.6)
|
57
|
+
actiontext (7.1.5)
|
58
|
+
actionpack (= 7.1.5)
|
59
|
+
activerecord (= 7.1.5)
|
60
|
+
activestorage (= 7.1.5)
|
61
|
+
activesupport (= 7.1.5)
|
62
|
+
globalid (>= 0.6.0)
|
63
|
+
nokogiri (>= 1.8.5)
|
64
|
+
actionview (7.1.5)
|
65
|
+
activesupport (= 7.1.5)
|
66
|
+
builder (~> 3.1)
|
67
|
+
erubi (~> 1.11)
|
68
|
+
rails-dom-testing (~> 2.2)
|
69
|
+
rails-html-sanitizer (~> 1.6)
|
70
|
+
activejob (7.1.5)
|
71
|
+
activesupport (= 7.1.5)
|
72
|
+
globalid (>= 0.3.6)
|
73
|
+
activemodel (7.1.5)
|
74
|
+
activesupport (= 7.1.5)
|
75
|
+
activerecord (7.1.5)
|
76
|
+
activemodel (= 7.1.5)
|
77
|
+
activesupport (= 7.1.5)
|
78
|
+
timeout (>= 0.4.0)
|
79
|
+
activerecord-import (1.8.1)
|
80
|
+
activerecord (>= 4.2)
|
81
|
+
activestorage (7.1.5)
|
82
|
+
actionpack (= 7.1.5)
|
83
|
+
activejob (= 7.1.5)
|
84
|
+
activerecord (= 7.1.5)
|
85
|
+
activesupport (= 7.1.5)
|
86
|
+
marcel (~> 1.0)
|
87
|
+
activesupport (7.1.5)
|
88
|
+
base64
|
89
|
+
benchmark (>= 0.3)
|
90
|
+
bigdecimal
|
91
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
92
|
+
connection_pool (>= 2.2.5)
|
93
|
+
drb
|
94
|
+
i18n (>= 1.6, < 2)
|
95
|
+
logger (>= 1.4.2)
|
96
|
+
minitest (>= 5.1)
|
97
|
+
mutex_m
|
98
|
+
securerandom (>= 0.3)
|
99
|
+
tzinfo (~> 2.0)
|
100
|
+
addressable (2.4.0)
|
101
|
+
base64 (0.2.0)
|
102
|
+
benchmark (0.4.0)
|
103
|
+
bigdecimal (3.1.8)
|
104
|
+
builder (3.3.0)
|
105
|
+
coderay (1.1.3)
|
106
|
+
concurrent-ruby (1.3.4)
|
107
|
+
connection_pool (2.4.1)
|
108
|
+
crass (1.0.6)
|
109
|
+
database_cleaner (2.1.0)
|
110
|
+
database_cleaner-active_record (>= 2, < 3)
|
111
|
+
database_cleaner-active_record (2.2.0)
|
112
|
+
activerecord (>= 5.a)
|
113
|
+
database_cleaner-core (~> 2.0.0)
|
114
|
+
database_cleaner-core (2.0.1)
|
115
|
+
date (3.4.0)
|
116
|
+
descendants_tracker (0.0.4)
|
117
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
118
|
+
diff-lcs (1.5.1)
|
119
|
+
docile (1.4.1)
|
120
|
+
drb (2.2.1)
|
121
|
+
erubi (1.13.0)
|
122
|
+
factory_bot (6.5.0)
|
123
|
+
activesupport (>= 5.0.0)
|
124
|
+
factory_bot_rails (6.4.4)
|
125
|
+
factory_bot (~> 6.5)
|
126
|
+
railties (>= 5.0.0)
|
127
|
+
faraday (0.9.2)
|
128
|
+
multipart-post (>= 1.2, < 3)
|
129
|
+
ffi (1.17.0-arm64-darwin)
|
130
|
+
ffi (1.17.0-x86_64-darwin)
|
131
|
+
formatador (1.1.0)
|
132
|
+
git (1.11.0)
|
133
|
+
rchardet (~> 1.8)
|
134
|
+
github_api (0.16.0)
|
135
|
+
addressable (~> 2.4.0)
|
136
|
+
descendants_tracker (~> 0.0.4)
|
137
|
+
faraday (~> 0.8, < 0.10)
|
138
|
+
hashie (>= 3.4)
|
139
|
+
mime-types (>= 1.16, < 3.0)
|
140
|
+
oauth2 (~> 1.0)
|
141
|
+
globalid (1.2.1)
|
142
|
+
activesupport (>= 6.1)
|
143
|
+
guard (2.19.0)
|
144
|
+
formatador (>= 0.2.4)
|
145
|
+
listen (>= 2.7, < 4.0)
|
146
|
+
lumberjack (>= 1.0.12, < 2.0)
|
147
|
+
nenv (~> 0.1)
|
148
|
+
notiffany (~> 0.0)
|
149
|
+
pry (>= 0.13.0)
|
150
|
+
shellany (~> 0.0)
|
151
|
+
thor (>= 0.18.1)
|
152
|
+
guard-compat (1.2.1)
|
153
|
+
guard-rspec (4.7.3)
|
154
|
+
guard (~> 2.1)
|
155
|
+
guard-compat (~> 1.1)
|
156
|
+
rspec (>= 2.99.0, < 4.0)
|
157
|
+
hashie (5.0.0)
|
158
|
+
highline (3.1.1)
|
159
|
+
reline
|
160
|
+
i18n (1.14.6)
|
161
|
+
concurrent-ruby (~> 1.0)
|
162
|
+
io-console (0.7.2)
|
163
|
+
irb (1.12.0)
|
164
|
+
rdoc
|
165
|
+
reline (>= 0.4.2)
|
166
|
+
json (1.8.6)
|
167
|
+
jwt (2.9.3)
|
168
|
+
base64
|
169
|
+
listen (3.9.0)
|
170
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
171
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
172
|
+
logger (1.6.1)
|
173
|
+
loofah (2.23.1)
|
174
|
+
crass (~> 1.0.2)
|
175
|
+
nokogiri (>= 1.12.0)
|
176
|
+
lumberjack (1.2.10)
|
177
|
+
mail (2.8.1)
|
178
|
+
mini_mime (>= 0.1.1)
|
179
|
+
net-imap
|
180
|
+
net-pop
|
181
|
+
net-smtp
|
182
|
+
marcel (1.0.4)
|
183
|
+
method_source (1.1.0)
|
184
|
+
mime-types (2.99.3)
|
185
|
+
mini_mime (1.1.5)
|
186
|
+
minitest (5.25.1)
|
187
|
+
multi_json (1.15.0)
|
188
|
+
multi_xml (0.6.0)
|
189
|
+
multipart-post (2.4.1)
|
190
|
+
mutex_m (0.2.0)
|
191
|
+
mysql2 (0.5.0)
|
192
|
+
nenv (0.3.0)
|
193
|
+
net-imap (0.4.18)
|
194
|
+
date
|
195
|
+
net-protocol
|
196
|
+
net-pop (0.1.2)
|
197
|
+
net-protocol
|
198
|
+
net-protocol (0.2.2)
|
199
|
+
timeout
|
200
|
+
net-smtp (0.5.0)
|
201
|
+
net-protocol
|
202
|
+
nio4r (2.7.4)
|
203
|
+
nokogiri (1.16.7-arm64-darwin)
|
204
|
+
racc (~> 1.4)
|
205
|
+
nokogiri (1.16.7-x86_64-darwin)
|
206
|
+
racc (~> 1.4)
|
207
|
+
notiffany (0.1.3)
|
208
|
+
nenv (~> 0.1)
|
209
|
+
shellany (~> 0.0)
|
210
|
+
oauth2 (1.4.8)
|
211
|
+
faraday (>= 0.8, < 3.0)
|
212
|
+
jwt (>= 1.0, < 3.0)
|
213
|
+
multi_json (~> 1.3)
|
214
|
+
multi_xml (~> 0.5)
|
215
|
+
rack (>= 1.2, < 3)
|
216
|
+
paranoia (3.0.0)
|
217
|
+
activerecord (>= 6, < 8.1)
|
218
|
+
pg (1.5.9)
|
219
|
+
pry (0.14.2)
|
220
|
+
coderay (~> 1.1)
|
221
|
+
method_source (~> 1.0)
|
222
|
+
psych (5.2.0)
|
223
|
+
stringio
|
224
|
+
racc (1.8.1)
|
225
|
+
rack (2.2.10)
|
226
|
+
rack-session (1.0.2)
|
227
|
+
rack (< 3)
|
228
|
+
rack-test (2.1.0)
|
229
|
+
rack (>= 1.3)
|
230
|
+
rackup (1.0.1)
|
231
|
+
rack (< 3)
|
232
|
+
webrick
|
233
|
+
rails (7.1.5)
|
234
|
+
actioncable (= 7.1.5)
|
235
|
+
actionmailbox (= 7.1.5)
|
236
|
+
actionmailer (= 7.1.5)
|
237
|
+
actionpack (= 7.1.5)
|
238
|
+
actiontext (= 7.1.5)
|
239
|
+
actionview (= 7.1.5)
|
240
|
+
activejob (= 7.1.5)
|
241
|
+
activemodel (= 7.1.5)
|
242
|
+
activerecord (= 7.1.5)
|
243
|
+
activestorage (= 7.1.5)
|
244
|
+
activesupport (= 7.1.5)
|
245
|
+
bundler (>= 1.15.0)
|
246
|
+
railties (= 7.1.5)
|
247
|
+
rails-dom-testing (2.2.0)
|
248
|
+
activesupport (>= 5.0.0)
|
249
|
+
minitest
|
250
|
+
nokogiri (>= 1.6)
|
251
|
+
rails-html-sanitizer (1.6.0)
|
252
|
+
loofah (~> 2.21)
|
253
|
+
nokogiri (~> 1.14)
|
254
|
+
railties (7.1.5)
|
255
|
+
actionpack (= 7.1.5)
|
256
|
+
activesupport (= 7.1.5)
|
257
|
+
irb
|
258
|
+
rackup (>= 1.0.0)
|
259
|
+
rake (>= 12.2)
|
260
|
+
thor (~> 1.0, >= 1.2.2)
|
261
|
+
zeitwerk (~> 2.6)
|
262
|
+
rake (13.2.1)
|
263
|
+
rb-fsevent (0.11.2)
|
264
|
+
rb-inotify (0.11.1)
|
265
|
+
ffi (~> 1.0)
|
266
|
+
rchardet (1.8.0)
|
267
|
+
rdoc (3.12.2)
|
268
|
+
json (~> 1.4)
|
269
|
+
reline (0.5.11)
|
270
|
+
io-console (~> 0.5)
|
271
|
+
rollbar (3.6.0)
|
272
|
+
rspec (3.13.0)
|
273
|
+
rspec-core (~> 3.13.0)
|
274
|
+
rspec-expectations (~> 3.13.0)
|
275
|
+
rspec-mocks (~> 3.13.0)
|
276
|
+
rspec-core (3.13.2)
|
277
|
+
rspec-support (~> 3.13.0)
|
278
|
+
rspec-expectations (3.13.3)
|
279
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
280
|
+
rspec-support (~> 3.13.0)
|
281
|
+
rspec-mocks (3.13.2)
|
282
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
283
|
+
rspec-support (~> 3.13.0)
|
284
|
+
rspec-support (3.13.1)
|
285
|
+
securerandom (0.3.1)
|
286
|
+
semver2 (3.4.2)
|
287
|
+
shellany (0.0.1)
|
288
|
+
simplecov (0.22.0)
|
289
|
+
docile (~> 1.1)
|
290
|
+
simplecov-html (~> 0.11)
|
291
|
+
simplecov_json_formatter (~> 0.1)
|
292
|
+
simplecov-html (0.13.1)
|
293
|
+
simplecov_json_formatter (0.1.4)
|
294
|
+
standalone_migrations (7.2.0)
|
295
|
+
activerecord (>= 6.0.0, < 8.0)
|
296
|
+
nokogiri (~> 1.14)
|
297
|
+
railties (>= 6.0.0, < 8.0)
|
298
|
+
rake (>= 10.0)
|
299
|
+
stringio (3.1.2)
|
300
|
+
thor (1.3.2)
|
301
|
+
thread_safe (0.3.6)
|
302
|
+
timecop (0.9.10)
|
303
|
+
timeout (0.4.2)
|
304
|
+
tzinfo (2.0.6)
|
305
|
+
concurrent-ruby (~> 1.0)
|
306
|
+
webrick (1.9.0)
|
307
|
+
websocket-driver (0.7.6)
|
308
|
+
websocket-extensions (>= 0.1.0)
|
309
|
+
websocket-extensions (0.1.5)
|
310
|
+
zeitwerk (2.6.18)
|
311
|
+
|
312
|
+
PLATFORMS
|
313
|
+
arm64-darwin
|
314
|
+
x86_64-darwin
|
315
|
+
|
316
|
+
DEPENDENCIES
|
317
|
+
activerecord (>= 6)
|
318
|
+
activerecord-import
|
319
|
+
activesupport
|
320
|
+
database_cleaner
|
321
|
+
factory_bot_rails
|
322
|
+
guard
|
323
|
+
guard-rspec
|
324
|
+
jeweler!
|
325
|
+
mysql2 (= 0.5)
|
326
|
+
paranoia
|
327
|
+
pg
|
328
|
+
pry
|
329
|
+
rails (>= 6)
|
330
|
+
rdoc (~> 3.12)
|
331
|
+
rollbar
|
332
|
+
rspec
|
333
|
+
simplecov
|
334
|
+
standalone_migrations
|
335
|
+
timecop
|
336
|
+
|
337
|
+
RUBY VERSION
|
338
|
+
ruby 3.0.2p107
|
339
|
+
|
340
|
+
BUNDLED WITH
|
341
|
+
2.5.23
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -130,174 +130,6 @@ This can be useful when:
|
|
130
130
|
- You're versioning training data for machine learning models
|
131
131
|
- You need to maintain immutable audit trails at specific checkpoints
|
132
132
|
|
133
|
-
## Single Table Inheritance (STI)
|
134
|
-
|
135
|
-
Historiographer fully supports Single Table Inheritance, both with the default `type` column and with custom inheritance columns.
|
136
|
-
|
137
|
-
### Default STI with `type` column
|
138
|
-
|
139
|
-
```ruby
|
140
|
-
class Post < ActiveRecord::Base
|
141
|
-
include Historiographer
|
142
|
-
end
|
143
|
-
|
144
|
-
class PrivatePost < Post
|
145
|
-
end
|
146
|
-
|
147
|
-
# The history classes follow the same inheritance pattern:
|
148
|
-
class PostHistory < ActiveRecord::Base
|
149
|
-
include Historiographer::History
|
150
|
-
end
|
151
|
-
|
152
|
-
class PrivatePostHistory < PostHistory
|
153
|
-
end
|
154
|
-
```
|
155
|
-
|
156
|
-
History records automatically maintain the correct STI type:
|
157
|
-
|
158
|
-
```ruby
|
159
|
-
private_post = PrivatePost.create(title: "Secret", history_user_id: current_user.id)
|
160
|
-
private_post.snapshot
|
161
|
-
|
162
|
-
# History records are the correct subclass
|
163
|
-
history = PostHistory.last
|
164
|
-
history.is_a?(PrivatePostHistory) #=> true
|
165
|
-
history.type #=> "PrivatePostHistory"
|
166
|
-
```
|
167
|
-
|
168
|
-
### Custom Inheritance Columns
|
169
|
-
|
170
|
-
You can also use a custom column for STI instead of the default `type`:
|
171
|
-
|
172
|
-
```ruby
|
173
|
-
class MLModel < ActiveRecord::Base
|
174
|
-
self.inheritance_column = :model_type
|
175
|
-
include Historiographer
|
176
|
-
end
|
177
|
-
|
178
|
-
class XGBoost < MLModel
|
179
|
-
self.table_name = "ml_models"
|
180
|
-
end
|
181
|
-
|
182
|
-
# History classes use the same custom column
|
183
|
-
class MLModelHistory < MLModel
|
184
|
-
self.inheritance_column = :model_type
|
185
|
-
self.table_name = "ml_model_histories"
|
186
|
-
end
|
187
|
-
|
188
|
-
class XGBoostHistory < MLModelHistory
|
189
|
-
end
|
190
|
-
```
|
191
|
-
|
192
|
-
Migration for custom inheritance column:
|
193
|
-
|
194
|
-
```ruby
|
195
|
-
create_table :ml_models do |t|
|
196
|
-
t.string :name
|
197
|
-
t.string :model_type # Custom inheritance column
|
198
|
-
t.jsonb :parameters
|
199
|
-
t.timestamps
|
200
|
-
|
201
|
-
t.index :model_type
|
202
|
-
end
|
203
|
-
|
204
|
-
create_table :ml_model_histories do |t|
|
205
|
-
t.histories # Includes all columns from parent table
|
206
|
-
end
|
207
|
-
```
|
208
|
-
|
209
|
-
The custom inheritance column works just like the default `type`:
|
210
|
-
|
211
|
-
```ruby
|
212
|
-
model = XGBoost.create(name: "My Model", history_user_id: current_user.id)
|
213
|
-
model.snapshot
|
214
|
-
|
215
|
-
# History records maintain the correct subclass
|
216
|
-
history = MLModelHistory.last
|
217
|
-
history.is_a?(XGBoostHistory) #=> true
|
218
|
-
history.model_type #=> "XGBoostHistory"
|
219
|
-
```
|
220
|
-
|
221
|
-
### STI and Snapshots: Perfect for Model Versioning
|
222
|
-
|
223
|
-
Single Table Inheritance combined with Historiographer's snapshot feature is particularly powerful for versioning machine learning models and other complex systems that need immutable historical records. Here's why:
|
224
|
-
|
225
|
-
1. **Type-Safe History**: When you snapshot an ML model, both the model and its parameters are preserved with their exact implementation type. This ensures that when you retrieve historical versions, you get back exactly the right subclass with its specific behavior:
|
226
|
-
|
227
|
-
```ruby
|
228
|
-
# Create and configure an XGBoost model
|
229
|
-
model = XGBoost.create(
|
230
|
-
name: "Customer Churn Predictor v1",
|
231
|
-
parameters: { max_depth: 3, eta: 0.1 },
|
232
|
-
history_user_id: current_user.id
|
233
|
-
)
|
234
|
-
|
235
|
-
# Take a snapshot before training
|
236
|
-
model.snapshot
|
237
|
-
|
238
|
-
# Update the model after training
|
239
|
-
model.update(
|
240
|
-
name: "Customer Churn Predictor v2",
|
241
|
-
parameters: { max_depth: 5, eta: 0.2 },
|
242
|
-
history_user_id: current_user.id
|
243
|
-
)
|
244
|
-
|
245
|
-
# Later, retrieve the exact pre-training version
|
246
|
-
historical_model = MLModel.latest_snapshot
|
247
|
-
historical_model.is_a?(XGBoostHistory) #=> true
|
248
|
-
historical_model.parameters #=> { max_depth: 3, eta: 0.1 }
|
249
|
-
```
|
250
|
-
|
251
|
-
2. **Implementation Versioning**: Different model types often have different parameters, preprocessing steps, or scoring methods. STI ensures these differences are preserved in history:
|
252
|
-
|
253
|
-
```ruby
|
254
|
-
class XGBoost < MLModel
|
255
|
-
def predict(data)
|
256
|
-
# XGBoost-specific prediction logic
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
class RandomForest < MLModel
|
261
|
-
def predict(data)
|
262
|
-
# RandomForest-specific prediction logic
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
# Your historical records maintain these implementation differences
|
267
|
-
old_model = MLModel.latest_snapshot
|
268
|
-
old_model.predict(data) # Uses the exact prediction logic from that point in time
|
269
|
-
```
|
270
|
-
|
271
|
-
3. **Reproducibility**: Essential for ML workflows where you need to reproduce results or audit model behavior:
|
272
|
-
|
273
|
-
```ruby
|
274
|
-
# Create model and snapshot at each significant stage
|
275
|
-
model = XGBoost.create(name: "Risk Scorer v1", history_user_id: current_user.id)
|
276
|
-
|
277
|
-
# Snapshot after initial configuration
|
278
|
-
model.snapshot(metadata: { stage: "configuration" })
|
279
|
-
|
280
|
-
# Snapshot after training
|
281
|
-
model.update(parameters: trained_parameters)
|
282
|
-
model.snapshot(metadata: { stage: "post_training" })
|
283
|
-
|
284
|
-
# Snapshot after validation
|
285
|
-
model.update(parameters: validated_parameters)
|
286
|
-
model.snapshot(metadata: { stage: "validated" })
|
287
|
-
|
288
|
-
# Later, you can retrieve any version to reproduce results
|
289
|
-
initial_version = model.histories.find_by(metadata: { stage: "configuration" })
|
290
|
-
trained_version = model.histories.find_by(metadata: { stage: "post_training" })
|
291
|
-
```
|
292
|
-
|
293
|
-
This combination of STI and snapshots is particularly valuable for:
|
294
|
-
|
295
|
-
- Model governance and compliance
|
296
|
-
- A/B testing different model types
|
297
|
-
- Debugging model behavior
|
298
|
-
- Reproducing historical predictions
|
299
|
-
- Maintaining audit trails for regulatory requirements
|
300
|
-
|
301
133
|
## Namespaced Models
|
302
134
|
|
303
135
|
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:
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'pry'
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
require 'rake'
|
14
|
+
require 'jeweler'
|
15
|
+
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
18
|
+
gem.name = "historiographer"
|
19
|
+
gem.homepage = "http://github.com/brettshollenberger/historiographer"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{Create histories of your ActiveRecord tables}
|
22
|
+
gem.description = %Q{Creates separate tables for each history table}
|
23
|
+
gem.email = "brett.shollenberger@gmail.com"
|
24
|
+
gem.authors = ["brettshollenberger"]
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'spec'
|
31
|
+
test.pattern = 'rspec/**/*_spec.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['test'].execute
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "historiographer #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
52
|
+
|
53
|
+
require 'standalone_migrations'
|
54
|
+
StandaloneMigrations::Tasks.load_tasks
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
4.3.0
|
Binary file
|
Binary file
|
Binary file
|