chrono_model 1.1.0 → 1.2.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/lib/chrono_model/patches/join_node.rb +1 -1
- data/lib/chrono_model/time_machine.rb +14 -41
- data/lib/chrono_model/time_machine/history_model.rb +38 -21
- data/lib/chrono_model/version.rb +1 -1
- data/spec/chrono_model/adapter/counter_cache_race_spec.rb +8 -8
- data/spec/chrono_model/adapter/sti_bug_spec.rb +49 -0
- data/spec/chrono_model/time_machine_spec.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15a81b8b75b7b77c03e9e750db3122cc0107e238696882bd67f2fe8fc7647274
|
4
|
+
data.tar.gz: 599dd55436b014a5db0ce7c1b003aa34ce15e3e12f08dac3e5cc0c5549823a8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ba2dc61cc6a62e573dea411005abf071e8010bcd249b05f274afefc756b1ac85b47e6469a6c9988f3b97c3f340891606bbe32d97cd75dfc4fcf0c1f99148805
|
7
|
+
data.tar.gz: 2cce42ff59e611beb10929f8484b8a0ec9eb106f2284770566d51d979eb6b8bb0c0627f88a4e8d1ef0c83cd7dfbd0733b44e09b339994a0424e7114181a74bf6
|
@@ -29,64 +29,37 @@ module ChronoModel
|
|
29
29
|
descendants_with_history.reject(&:history?)
|
30
30
|
end
|
31
31
|
|
32
|
-
# STI support.
|
32
|
+
# STI support.
|
33
33
|
#
|
34
34
|
def inherited(subclass)
|
35
35
|
super
|
36
36
|
|
37
|
-
# Do not smash stack
|
38
|
-
# new
|
39
|
-
#
|
37
|
+
# Do not smash stack. The below +define_history_model_for+ method
|
38
|
+
# defines a new inherited class via Class.new(), thus +inherited+
|
39
|
+
# is going to be called again. By that time the subclass is still
|
40
|
+
# an anonymous one, so its +name+ method returns nil. We use this
|
41
|
+
# condition to avoid infinite recursion.
|
42
|
+
#
|
43
|
+
# Sadly, we can't avoid it by calling +.history?+, because in the
|
44
|
+
# subclass the HistoryModel hasn't been included yet.
|
45
|
+
#
|
40
46
|
unless subclass.name.nil?
|
41
|
-
ChronoModel::TimeMachine.
|
47
|
+
ChronoModel::TimeMachine.define_history_model_for(subclass)
|
42
48
|
end
|
43
49
|
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
53
|
def self.define_history_model_for(model)
|
48
|
-
history = Class.new(model)
|
54
|
+
history = Class.new(model) do
|
55
|
+
include ChronoModel::TimeMachine::HistoryModel
|
56
|
+
end
|
49
57
|
|
50
58
|
model.singleton_class.instance_eval do
|
51
59
|
define_method(:history) { history }
|
52
60
|
end
|
53
61
|
|
54
|
-
history.singleton_class.instance_eval do
|
55
|
-
define_method(:sti_name) { model.sti_name }
|
56
|
-
end
|
57
|
-
|
58
62
|
model.const_set :History, history
|
59
|
-
|
60
|
-
return history
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.define_inherited_history_model_for(subclass)
|
64
|
-
# Define history model for the subclass
|
65
|
-
history = Class.new(subclass.superclass.history)
|
66
|
-
history.table_name = subclass.superclass.history.table_name
|
67
|
-
|
68
|
-
# Override the STI name on the history subclass
|
69
|
-
history.singleton_class.instance_eval do
|
70
|
-
define_method(:sti_name) { subclass.sti_name }
|
71
|
-
end
|
72
|
-
|
73
|
-
# Return the subclass history via the .history method
|
74
|
-
subclass.singleton_class.instance_eval do
|
75
|
-
define_method(:history) { history }
|
76
|
-
end
|
77
|
-
|
78
|
-
# Define the History constant inside the subclass
|
79
|
-
subclass.const_set :History, history
|
80
|
-
|
81
|
-
history.instance_eval do
|
82
|
-
# Monkey patch of ActiveRecord::Inheritance.
|
83
|
-
# STI fails when a Foo::History record has Foo as type in the
|
84
|
-
# inheritance column; AR expects the type to be an instance of the
|
85
|
-
# current class or a descendant (or self).
|
86
|
-
def find_sti_class(type_name)
|
87
|
-
super(type_name + "::History")
|
88
|
-
end
|
89
|
-
end
|
90
63
|
end
|
91
64
|
|
92
65
|
module ClassMethods
|
@@ -50,21 +50,6 @@ module ChronoModel
|
|
50
50
|
true
|
51
51
|
end
|
52
52
|
|
53
|
-
# Getting the correct quoted_table_name can be tricky when
|
54
|
-
# STI is involved. If Orange < Fruit, then Orange::History < Fruit::History
|
55
|
-
# (see define_inherited_history_model_for).
|
56
|
-
# This means that the superclass method returns Fruit::History, which
|
57
|
-
# will give us the wrong table name. What we actually want is the
|
58
|
-
# superclass of Fruit::History, which is Fruit. So, we use
|
59
|
-
# non_history_superclass instead. -npj
|
60
|
-
def non_history_superclass(klass = self)
|
61
|
-
if klass.superclass.history?
|
62
|
-
non_history_superclass(klass.superclass)
|
63
|
-
else
|
64
|
-
klass.superclass
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
53
|
def relation
|
69
54
|
super.as_of_time!(Time.now)
|
70
55
|
end
|
@@ -72,14 +57,15 @@ module ChronoModel
|
|
72
57
|
# Fetches as of +time+ records.
|
73
58
|
#
|
74
59
|
def as_of(time)
|
75
|
-
|
60
|
+
superclass.from(virtual_table_at(time)).as_of_time!(time)
|
76
61
|
end
|
77
62
|
|
78
|
-
def virtual_table_at(time,
|
79
|
-
|
80
|
-
|
63
|
+
def virtual_table_at(time, table_name: nil)
|
64
|
+
virtual_name = table_name ?
|
65
|
+
connection.quote_table_name(table_name) :
|
66
|
+
superclass.quoted_table_name
|
81
67
|
|
82
|
-
"(#{at(time).to_sql}) #{
|
68
|
+
"(#{at(time).to_sql}) #{virtual_name}"
|
83
69
|
end
|
84
70
|
|
85
71
|
# Fetches history record at the given time
|
@@ -102,6 +88,37 @@ module ChronoModel
|
|
102
88
|
def of(object)
|
103
89
|
where(id: object)
|
104
90
|
end
|
91
|
+
|
92
|
+
# The `sti_name` method returns the contents of the inheritance
|
93
|
+
# column, and it is usually the class name. The inherited class
|
94
|
+
# name has the "::History" suffix but that is never going to be
|
95
|
+
# present in the data.
|
96
|
+
#
|
97
|
+
# As such it is overriden here to return the same contents that
|
98
|
+
# the parent would have returned.
|
99
|
+
def sti_name
|
100
|
+
superclass.sti_name
|
101
|
+
end
|
102
|
+
|
103
|
+
# For STI to work, the history model needs to have the exact same
|
104
|
+
# semantics as the model it inherits from. However given it is
|
105
|
+
# actually inherited, the original AR implementation would return
|
106
|
+
# false here. But for STI sake, the history model is located in the
|
107
|
+
# same exact hierarchy location as its parent, thus this is defined in
|
108
|
+
# this override.
|
109
|
+
#
|
110
|
+
def descends_from_active_record?
|
111
|
+
superclass.descends_from_active_record?
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
# STI fails when a Foo::History record has Foo as type in the
|
116
|
+
# inheritance column; AR expects the type to be an instance of the
|
117
|
+
# current class or a descendant (or self).
|
118
|
+
#
|
119
|
+
def find_sti_class(type_name)
|
120
|
+
super(type_name + "::History")
|
121
|
+
end
|
105
122
|
end
|
106
123
|
|
107
124
|
# The history id is `hid`, but this cannot set as primary key
|
@@ -170,7 +187,7 @@ module ChronoModel
|
|
170
187
|
# Returns this history entry's current record
|
171
188
|
#
|
172
189
|
def current_version
|
173
|
-
self.class.
|
190
|
+
self.class.superclass.find(rid)
|
174
191
|
end
|
175
192
|
|
176
193
|
def record #:nodoc:
|
data/lib/chrono_model/version.rb
CHANGED
@@ -4,15 +4,15 @@ require 'support/helpers'
|
|
4
4
|
describe 'models with counter cache' do
|
5
5
|
include ChronoTest::Helpers::TimeMachine
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
adapter.create_table 'sections', temporal: true, no_journal: %w( articles_count ) do |t|
|
8
|
+
t.string :name
|
9
|
+
t.integer :articles_count, default: 0
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
adapter.create_table 'articles', temporal: true do |t|
|
13
|
+
t.string :title
|
14
|
+
t.references :section
|
15
|
+
end
|
16
16
|
|
17
17
|
class ::Section < ActiveRecord::Base
|
18
18
|
include ChronoModel::TimeMachine
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/helpers'
|
3
|
+
|
4
|
+
describe 'models with STI' do
|
5
|
+
include ChronoTest::Helpers::TimeMachine
|
6
|
+
|
7
|
+
adapter.create_table 'animals', temporal: true do |t|
|
8
|
+
t.string :type
|
9
|
+
end
|
10
|
+
|
11
|
+
class ::Animal < ActiveRecord::Base
|
12
|
+
include ChronoModel::TimeMachine
|
13
|
+
end
|
14
|
+
|
15
|
+
class ::Dog < Animal
|
16
|
+
end
|
17
|
+
|
18
|
+
class ::Goat < Animal
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'it generates the right queries' do
|
22
|
+
before do
|
23
|
+
Dog.create!
|
24
|
+
@later = Time.new
|
25
|
+
Goat.create!
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
tables = ['temporal.animals', 'history.animals']
|
30
|
+
ActiveRecord::Base.connection.execute "truncate #{tables.join(', ')} cascade"
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "select" do
|
34
|
+
expect(Animal.first).to_not be_nil
|
35
|
+
expect(Animal.as_of(@later).first).to_not be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "count" do
|
39
|
+
expect(Animal.count).to eq(2)
|
40
|
+
expect(Animal.as_of(@later).count).to eq(1)
|
41
|
+
|
42
|
+
expect(Dog.count).to eq(1)
|
43
|
+
expect(Dog.as_of(@later).count).to eq(1)
|
44
|
+
|
45
|
+
expect(Goat.count).to eq(1)
|
46
|
+
expect(Goat.as_of(@later).count).to eq(0)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chrono_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcello Barnaba
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-04-
|
12
|
+
date: 2019-04-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -253,6 +253,7 @@ files:
|
|
253
253
|
- spec/aruba/migrations_spec.rb
|
254
254
|
- spec/aruba/rake_task_spec.rb
|
255
255
|
- spec/chrono_model/adapter/counter_cache_race_spec.rb
|
256
|
+
- spec/chrono_model/adapter/sti_bug_spec.rb
|
256
257
|
- spec/chrono_model/adapter_spec.rb
|
257
258
|
- spec/chrono_model/conversions_spec.rb
|
258
259
|
- spec/chrono_model/json_ops_spec.rb
|
@@ -308,6 +309,7 @@ test_files:
|
|
308
309
|
- spec/aruba/migrations_spec.rb
|
309
310
|
- spec/aruba/rake_task_spec.rb
|
310
311
|
- spec/chrono_model/adapter/counter_cache_race_spec.rb
|
312
|
+
- spec/chrono_model/adapter/sti_bug_spec.rb
|
311
313
|
- spec/chrono_model/adapter_spec.rb
|
312
314
|
- spec/chrono_model/conversions_spec.rb
|
313
315
|
- spec/chrono_model/json_ops_spec.rb
|