chrono_model 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|