chrono_model 1.2.0 → 1.2.1
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/Rakefile +2 -1
- data/lib/chrono_model/time_machine.rb +1 -1
- data/lib/chrono_model/time_machine/history_model.rb +4 -0
- data/lib/chrono_model/version.rb +1 -1
- data/spec/chrono_model/adapter/base_spec.rb +157 -0
- data/spec/chrono_model/adapter/ddl_spec.rb +243 -0
- data/spec/chrono_model/adapter/indexes_spec.rb +72 -0
- data/spec/chrono_model/adapter/migrations_spec.rb +312 -0
- data/spec/chrono_model/history_models_spec.rb +32 -0
- data/spec/chrono_model/time_machine/as_of_spec.rb +188 -0
- data/spec/chrono_model/time_machine/changes_spec.rb +50 -0
- data/spec/chrono_model/{adapter → time_machine}/counter_cache_race_spec.rb +2 -2
- data/spec/chrono_model/time_machine/default_scope_spec.rb +37 -0
- data/spec/chrono_model/time_machine/history_spec.rb +104 -0
- data/spec/chrono_model/time_machine/keep_cool_spec.rb +27 -0
- data/spec/chrono_model/time_machine/manipulations_spec.rb +84 -0
- data/spec/chrono_model/time_machine/model_identification_spec.rb +46 -0
- data/spec/chrono_model/time_machine/sequence_spec.rb +74 -0
- data/spec/chrono_model/time_machine/sti_spec.rb +100 -0
- data/spec/chrono_model/{time_query_spec.rb → time_machine/time_query_spec.rb} +22 -5
- data/spec/chrono_model/time_machine/timeline_spec.rb +63 -0
- data/spec/chrono_model/time_machine/timestamps_spec.rb +43 -0
- data/spec/chrono_model/time_machine/transactions_spec.rb +69 -0
- data/spec/support/adapter/helpers.rb +53 -0
- data/spec/support/adapter/structure.rb +44 -0
- data/spec/support/time_machine/helpers.rb +47 -0
- data/spec/support/time_machine/structure.rb +111 -0
- metadata +48 -14
- data/spec/chrono_model/adapter/sti_bug_spec.rb +0 -49
- data/spec/chrono_model/adapter_spec.rb +0 -788
- data/spec/chrono_model/time_machine_spec.rb +0 -749
- data/spec/support/helpers.rb +0 -198
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
describe '#last_changes' do
|
8
|
+
context 'on plain records' do
|
9
|
+
context 'having history' do
|
10
|
+
subject { $t.bar.last_changes }
|
11
|
+
it { is_expected.to eq('name' => ['bar bar', 'new bar']) }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'without history' do
|
15
|
+
let(:record) { Bar.create!(:name => 'foreveralone') }
|
16
|
+
subject { record.last_changes }
|
17
|
+
it { is_expected.to be_nil }
|
18
|
+
after { record.destroy.history.delete_all } # UGLY
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'on history records' do
|
23
|
+
context 'at the beginning of the timeline' do
|
24
|
+
subject { $t.bar.history.first.last_changes }
|
25
|
+
it { is_expected.to be_nil }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'in the middle of the timeline' do
|
29
|
+
subject { $t.bar.history.second.last_changes }
|
30
|
+
it { is_expected.to eq('name' => ['bar', 'foo bar']) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#changes_against' do
|
36
|
+
context 'can compare records against history' do
|
37
|
+
it { expect($t.bar.changes_against($t.bar.history.first)).to eq('name' => ['bar', 'new bar']) }
|
38
|
+
it { expect($t.bar.changes_against($t.bar.history.second)).to eq('name' => ['foo bar', 'new bar']) }
|
39
|
+
it { expect($t.bar.changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'new bar']) }
|
40
|
+
it { expect($t.bar.changes_against($t.bar.history.last)).to eq({}) }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'can compare history against history' do
|
44
|
+
it { expect($t.bar.history.first. changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'bar']) }
|
45
|
+
it { expect($t.bar.history.second.changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'foo bar']) }
|
46
|
+
it { expect($t.bar.history.third. changes_against($t.bar.history.third)).to eq({}) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'support/
|
2
|
+
require 'support/time_machine/structure'
|
3
3
|
|
4
4
|
describe 'models with counter cache' do
|
5
|
-
include ChronoTest::Helpers
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
6
|
|
7
7
|
adapter.create_table 'sections', temporal: true, no_journal: %w( articles_count ) do |t|
|
8
8
|
t.string :name
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
# Set up database structure
|
8
|
+
#
|
9
|
+
adapter.create_table 'defoos', :temporal => true do |t|
|
10
|
+
t.string :name
|
11
|
+
t.boolean :active
|
12
|
+
end
|
13
|
+
|
14
|
+
class ::Defoo < ActiveRecord::Base
|
15
|
+
include ChronoModel::TimeMachine
|
16
|
+
|
17
|
+
default_scope proc { where(:active => true) }
|
18
|
+
end
|
19
|
+
|
20
|
+
active = ts_eval { Defoo.create! :name => 'active 1', :active => true }
|
21
|
+
ts_eval(active) { update_attributes! :name => 'active 2' }
|
22
|
+
|
23
|
+
hidden = ts_eval { Defoo.create! :name => 'hidden 1', :active => false }
|
24
|
+
ts_eval(hidden) { update_attributes! :name => 'hidden 2' }
|
25
|
+
|
26
|
+
describe 'it honors default_scopes' do
|
27
|
+
it { expect(Defoo.as_of(active.ts[0]).map(&:name)).to eq ['active 1'] }
|
28
|
+
it { expect(Defoo.as_of(active.ts[1]).map(&:name)).to eq ['active 2'] }
|
29
|
+
it { expect(Defoo.as_of(hidden.ts[0]).map(&:name)).to eq ['active 2'] }
|
30
|
+
it { expect(Defoo.as_of(hidden.ts[1]).map(&:name)).to eq ['active 2'] }
|
31
|
+
|
32
|
+
it { expect(Defoo.unscoped.as_of(active.ts[0]).map(&:name)).to eq ['active 1'] }
|
33
|
+
it { expect(Defoo.unscoped.as_of(active.ts[1]).map(&:name)).to eq ['active 2'] }
|
34
|
+
it { expect(Defoo.unscoped.as_of(hidden.ts[0]).map(&:name)).to eq ['active 2', 'hidden 1'] }
|
35
|
+
it { expect(Defoo.unscoped.as_of(hidden.ts[1]).map(&:name)).to eq ['active 2', 'hidden 2'] }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
describe '.history' do
|
8
|
+
let(:foo_history) {
|
9
|
+
['foo', 'foo bar', 'new foo', 'foo 0', 'foo 1']
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:bar_history) {
|
13
|
+
['bar', 'foo bar', 'bar bar', 'new bar', 'bar 0', 'bar 1']
|
14
|
+
}
|
15
|
+
|
16
|
+
it { expect(Foo.history.all.map(&:name)).to eq foo_history }
|
17
|
+
it { expect(Bar.history.all.map(&:name)).to eq bar_history }
|
18
|
+
|
19
|
+
it { expect(Foo.history.first).to be_a(Foo::History) }
|
20
|
+
it { expect(Bar.history.first).to be_a(Bar::History) }
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe '#history' do
|
25
|
+
describe 'returns historical instances' do
|
26
|
+
it { expect($t.foo.history.size).to eq(3) }
|
27
|
+
it { expect($t.foo.history.map(&:name)).to eq ['foo', 'foo bar', 'new foo'] }
|
28
|
+
|
29
|
+
it { expect($t.bar.history.size).to eq(4) }
|
30
|
+
it { expect($t.bar.history.map(&:name)).to eq ['bar', 'foo bar', 'bar bar', 'new bar'] }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'does not return read only records' do
|
34
|
+
it { expect($t.foo.history.all?(&:readonly?)).to be(false) }
|
35
|
+
it { expect($t.bar.history.all?(&:readonly?)).to be(false) }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'takes care of associated records' do
|
39
|
+
subject { $t.foo.history.map {|f| f.bars.first.try(:name)} }
|
40
|
+
it { is_expected.to eq [nil, 'foo bar', 'new bar'] }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'does not return read only associated records' do
|
44
|
+
it { expect($t.foo.history[2].bars.all?(&:readonly?)).to_not be(true) }
|
45
|
+
it { expect($t.bar.history.all? {|b| b.foo.readonly?}).to_not be(true) }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'allows a custom select list' do
|
49
|
+
it { expect($t.foo.history.select(:id).first.attributes.keys).to eq %w( id ) }
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'does not add as_of_time when there are aggregates' do
|
53
|
+
it { expect($t.foo.history.select('max(id)').to_sql).to_not match(/as_of_time/) }
|
54
|
+
|
55
|
+
it { expect($t.foo.history.except(:order).select('max(id) as foo, min(id) as bar').group('id').first.attributes.keys).to eq %w( id foo bar ) }
|
56
|
+
end
|
57
|
+
|
58
|
+
context '.sorted' do
|
59
|
+
describe 'orders by recorded_at, hid' do
|
60
|
+
it { expect($t.foo.history.sorted.to_sql).to match(/order by .+"recorded_at" ASC, .+"hid" ASC/i) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
describe '#current_version' do
|
67
|
+
describe 'on plain records' do
|
68
|
+
subject { $t.foo.current_version }
|
69
|
+
it { is_expected.to eq $t.foo }
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'from #as_of' do
|
73
|
+
subject { $t.foo.as_of(Time.now) }
|
74
|
+
it { is_expected.to eq $t.foo }
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'on historical records' do
|
78
|
+
subject { $t.foo.history.sample.current_version }
|
79
|
+
it { is_expected.to eq $t.foo }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
describe '#historical?' do
|
85
|
+
subject { record.historical? }
|
86
|
+
|
87
|
+
describe 'on plain records' do
|
88
|
+
let(:record) { $t.foo }
|
89
|
+
it { is_expected.to be(false) }
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'on historical records' do
|
93
|
+
describe 'from #history' do
|
94
|
+
let(:record) { $t.foo.history.first }
|
95
|
+
it { is_expected.to be(true) }
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'from #as_of' do
|
99
|
+
let(:record) { $t.foo.as_of(Time.now) }
|
100
|
+
it { is_expected.to be(true) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
describe 'does not interfere with AR standard behaviour' do
|
8
|
+
let(:all_foos) { [ $t.foo ] + $t.foos }
|
9
|
+
let(:all_bars) { [ $t.bar ] + $t.bars }
|
10
|
+
|
11
|
+
it { expect(Foo.count).to eq all_foos.size }
|
12
|
+
it { expect(Bar.count).to eq all_bars.size }
|
13
|
+
|
14
|
+
it { expect(Foo.includes(bars: :sub_bars)).to eq all_foos }
|
15
|
+
it { expect(Foo.includes(:bars).preload(bars: :sub_bars)).to eq all_foos }
|
16
|
+
|
17
|
+
it { expect(Foo.includes(:bars).first.name).to eq 'new foo' }
|
18
|
+
it { expect(Foo.includes(:bars).as_of($t.foo.ts[0]).first.name).to eq 'foo' }
|
19
|
+
|
20
|
+
it { expect(Foo.joins(:bars).map(&:bars).flatten).to eq all_bars }
|
21
|
+
it { expect(Foo.joins(:bars).first.bars.joins(:sub_bars).first.name).to eq 'new bar' }
|
22
|
+
|
23
|
+
it { expect(Foo.joins(bars: :sub_bars).first.bars.joins(:sub_bars).first.sub_bars.first.name).to eq 'new sub-bar' }
|
24
|
+
|
25
|
+
it { expect(Foo.first.bars.includes(:sub_bars)).to eq [ $t.bar ] }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
describe '#save' do
|
8
|
+
subject { $t.bar.history.first }
|
9
|
+
|
10
|
+
it do
|
11
|
+
with_revert do
|
12
|
+
subject.name = 'modified bar history'
|
13
|
+
subject.save
|
14
|
+
subject.reload
|
15
|
+
|
16
|
+
is_expected.to be_a(Bar::History)
|
17
|
+
expect(subject.name).to eq 'modified bar history'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#save!' do
|
23
|
+
subject { $t.bar.history.second }
|
24
|
+
|
25
|
+
it do
|
26
|
+
with_revert do
|
27
|
+
subject.name = 'another modified bar history'
|
28
|
+
subject.save
|
29
|
+
subject.reload
|
30
|
+
|
31
|
+
is_expected.to be_a(Bar::History)
|
32
|
+
expect(subject.name).to eq 'another modified bar history'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#destroy' do
|
38
|
+
describe 'on historical records' do
|
39
|
+
subject { $t.foo.history.first.destroy }
|
40
|
+
it { expect { subject }.to raise_error(ActiveRecord::ReadOnlyRecord) }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'on current records' do
|
44
|
+
rec = nil
|
45
|
+
before(:all) do
|
46
|
+
rec = ts_eval { Foo.create!(:name => 'alive foo', :fooity => 42) }
|
47
|
+
ts_eval(rec) { update_attributes!(:name => 'dying foo') }
|
48
|
+
end
|
49
|
+
after(:all) do
|
50
|
+
rec.history.delete_all
|
51
|
+
end
|
52
|
+
|
53
|
+
subject { rec.destroy }
|
54
|
+
|
55
|
+
it { expect { subject }.to_not raise_error }
|
56
|
+
it { expect { rec.reload }.to raise_error(ActiveRecord::RecordNotFound) }
|
57
|
+
|
58
|
+
describe 'does not delete its history' do
|
59
|
+
subject { record.name }
|
60
|
+
|
61
|
+
context do
|
62
|
+
let(:record) { rec.as_of(rec.ts.first) }
|
63
|
+
it { is_expected.to eq 'alive foo' }
|
64
|
+
end
|
65
|
+
|
66
|
+
context do
|
67
|
+
let(:record) { rec.as_of(rec.ts.last) }
|
68
|
+
it { is_expected.to eq 'dying foo' }
|
69
|
+
end
|
70
|
+
|
71
|
+
context do
|
72
|
+
let(:record) { Foo.as_of(rec.ts.first).where(:fooity => 42).first }
|
73
|
+
it { is_expected.to eq 'alive foo' }
|
74
|
+
end
|
75
|
+
|
76
|
+
context do
|
77
|
+
subject { Foo.history.where(:fooity => 42).map(&:name) }
|
78
|
+
it { is_expected.to eq ['alive foo', 'dying foo'] }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
adapter.create_table 'plains' do |t|
|
8
|
+
t.string :foo
|
9
|
+
end
|
10
|
+
|
11
|
+
class ::Plain < ActiveRecord::Base
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.chrono?' do
|
15
|
+
subject { model.chrono? }
|
16
|
+
|
17
|
+
context 'on a temporal model' do
|
18
|
+
let(:model) { Foo }
|
19
|
+
it { is_expected.to be(true) }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'on a plain model' do
|
23
|
+
let(:model) { Plain }
|
24
|
+
it { is_expected.to be(false) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.history?' do
|
29
|
+
subject { model.history? }
|
30
|
+
|
31
|
+
context 'on a temporal parent model' do
|
32
|
+
let(:model) { Foo }
|
33
|
+
it { is_expected.to be(false) }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'on a temporal history model' do
|
37
|
+
let(:model) { Foo::History }
|
38
|
+
it { is_expected.to be(true) }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'on a plain model' do
|
42
|
+
let(:model) { Plain }
|
43
|
+
it { expect { subject }.to raise_error(NoMethodError) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
describe ChronoModel::TimeMachine do
|
5
|
+
include ChronoTest::TimeMachine::Helpers
|
6
|
+
|
7
|
+
describe '#pred' do
|
8
|
+
context 'on the first history entry' do
|
9
|
+
subject { $t.foo.history.first.pred }
|
10
|
+
it { is_expected.to be(nil) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'on the second history entry' do
|
14
|
+
subject { $t.foo.history.second.pred }
|
15
|
+
it { is_expected.to eq $t.foo.history.first }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'on the last history entry' do
|
19
|
+
subject { $t.foo.history.last.pred }
|
20
|
+
it { is_expected.to eq $t.foo.history[$t.foo.history.size - 2] }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'on records having history' do
|
24
|
+
subject { $t.bar.pred }
|
25
|
+
it { expect(subject.name).to eq 'bar bar' }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when there is enough history' do
|
29
|
+
subject { $t.bar.pred.pred.pred.pred }
|
30
|
+
it { expect(subject.name).to eq 'bar' }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when no history is recorded' do
|
34
|
+
let(:record) { Bar.create!(:name => 'quuuux') }
|
35
|
+
|
36
|
+
subject { record.pred }
|
37
|
+
|
38
|
+
it { is_expected.to be(nil) }
|
39
|
+
|
40
|
+
after { record.destroy.history.delete_all }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#succ' do
|
45
|
+
context 'on the first history entry' do
|
46
|
+
subject { $t.foo.history.first.succ }
|
47
|
+
|
48
|
+
it { is_expected.to eq $t.foo.history.second }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'on the second history entry' do
|
52
|
+
subject { $t.foo.history.second.succ }
|
53
|
+
|
54
|
+
it { is_expected.to eq $t.foo.history.third }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'on the last history entry' do
|
58
|
+
subject { $t.foo.history.last.succ }
|
59
|
+
|
60
|
+
it { is_expected.to be(nil) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#first' do
|
65
|
+
subject { $t.foo.history.sample.first }
|
66
|
+
it { is_expected.to eq $t.foo.history.first }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#last' do
|
70
|
+
subject { $t.foo.history.sample.last }
|
71
|
+
it { is_expected.to eq $t.foo.history.last }
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/time_machine/structure'
|
3
|
+
|
4
|
+
# STI cases
|
5
|
+
#
|
6
|
+
# - https://github.com/ifad/chronomodel/issues/5
|
7
|
+
# - https://github.com/ifad/chronomodel/issues/47
|
8
|
+
#
|
9
|
+
describe 'models with STI' do
|
10
|
+
include ChronoTest::TimeMachine::Helpers
|
11
|
+
|
12
|
+
adapter.create_table 'elements', :temporal => true do |t|
|
13
|
+
t.string :title
|
14
|
+
t.string :type
|
15
|
+
end
|
16
|
+
|
17
|
+
class ::Element < ActiveRecord::Base
|
18
|
+
include ChronoModel::TimeMachine
|
19
|
+
end
|
20
|
+
|
21
|
+
class ::Publication < Element
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.descendants' do
|
25
|
+
subject { Element.descendants }
|
26
|
+
|
27
|
+
it { is_expected.to_not include(Element::History) }
|
28
|
+
it { is_expected.to include(Publication) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.descendants_with_history' do
|
32
|
+
subject { Element.descendants_with_history }
|
33
|
+
|
34
|
+
it { is_expected.to include(Element::History) }
|
35
|
+
it { is_expected.to include(Publication) }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'timeline' do
|
39
|
+
let(:publication) do
|
40
|
+
pub = ts_eval { Publication.create! :title => 'wrong title' }
|
41
|
+
ts_eval(pub) { update_attributes! :title => 'correct title' }
|
42
|
+
|
43
|
+
pub
|
44
|
+
end
|
45
|
+
|
46
|
+
it { expect(publication.history.map(&:title)).to eq ['wrong title', 'correct title'] }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'identity' do
|
50
|
+
adapter.create_table 'animals', temporal: true do |t|
|
51
|
+
t.string :type
|
52
|
+
end
|
53
|
+
|
54
|
+
class ::Animal < ActiveRecord::Base
|
55
|
+
include ChronoModel::TimeMachine
|
56
|
+
end
|
57
|
+
|
58
|
+
class ::Dog < Animal
|
59
|
+
end
|
60
|
+
|
61
|
+
class ::Goat < Animal
|
62
|
+
end
|
63
|
+
|
64
|
+
before do
|
65
|
+
Dog.create!
|
66
|
+
@later = Time.new
|
67
|
+
Goat.create!
|
68
|
+
end
|
69
|
+
|
70
|
+
after do
|
71
|
+
tables = ['temporal.animals', 'history.animals']
|
72
|
+
ActiveRecord::Base.connection.execute "truncate #{tables.join(', ')} cascade"
|
73
|
+
end
|
74
|
+
|
75
|
+
specify "select" do
|
76
|
+
expect(Animal.first).to be_a(Animal)
|
77
|
+
expect(Animal.as_of(@later).first).to be_a(Animal)
|
78
|
+
|
79
|
+
expect(Animal.where(type: 'Dog').first).to be_a(Dog)
|
80
|
+
expect(Dog.first).to be_a(Dog)
|
81
|
+
expect(Dog.as_of(@later).first).to be_a(Dog)
|
82
|
+
|
83
|
+
expect(Animal.where(type: 'Goat').first).to be_a(Goat)
|
84
|
+
expect(Goat.first).to be_a(Goat)
|
85
|
+
expect(Goat.as_of(@later).first).to be(nil)
|
86
|
+
expect(Goat.as_of(Time.now).first).to be_a(Goat)
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "count" do
|
90
|
+
expect(Animal.count).to eq(2)
|
91
|
+
expect(Animal.as_of(@later).count).to eq(1)
|
92
|
+
|
93
|
+
expect(Dog.count).to eq(1)
|
94
|
+
expect(Dog.as_of(@later).count).to eq(1)
|
95
|
+
|
96
|
+
expect(Goat.count).to eq(1)
|
97
|
+
expect(Goat.as_of(@later).count).to eq(0)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|