acts_as_revisionable 1.0.6 → 1.1.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.
- data/README.rdoc +13 -5
- data/RELEASES.txt +7 -0
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/acts_as_revisionable.gemspec +13 -15
- data/lib/acts_as_revisionable.rb +101 -30
- data/lib/acts_as_revisionable/revision_record.rb +96 -66
- data/spec/acts_as_revisionable_spec.rb +682 -135
- data/spec/revision_record_spec.rb +291 -254
- data/spec/spec_helper.rb +18 -1
- data/spec/version_1_1_upgrade_spec.rb +41 -0
- metadata +33 -19
- data/spec/full_spec.rb +0 -449
@@ -1,213 +1,234 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'zlib'
|
3
3
|
|
4
4
|
describe ActsAsRevisionable::RevisionRecord do
|
5
|
-
|
5
|
+
|
6
6
|
before :all do
|
7
7
|
ActsAsRevisionable::Test.create_database
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
self
|
8
|
+
ActsAsRevisionable::RevisionRecord.create_table
|
9
|
+
|
10
|
+
class TestRevisionableAssociationLegacyRecord < ActiveRecord::Base
|
11
|
+
connection.create_table(table_name, :id => false) do |t|
|
12
|
+
t.column :legacy_id, :integer
|
13
|
+
t.column :name, :string
|
14
|
+
t.column :value, :integer
|
15
|
+
t.column :test_revisionable_record_id, :integer
|
16
|
+
end unless table_exists?
|
17
|
+
self.primary_key = :legacy_id
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize (attributes = {})
|
30
|
-
@attributes = attributes
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.reflections
|
34
|
-
@reflections || {}
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.reflections= (vals)
|
38
|
-
@reflections = vals
|
39
|
-
end
|
40
|
-
|
41
|
-
def id
|
42
|
-
attributes['id']
|
20
|
+
class TestRevisionableOneAssociationRecord < ActiveRecord::Base
|
21
|
+
connection.create_table(table_name, :id => false) do |t|
|
22
|
+
t.column :name, :string
|
23
|
+
t.column :value, :integer
|
24
|
+
t.column :test_revisionable_record_id, :integer
|
25
|
+
end unless table_exists?
|
43
26
|
end
|
44
27
|
|
45
|
-
|
46
|
-
|
28
|
+
class TestRevisionableAssociationComposite < ActiveRecord::Base
|
29
|
+
connection.create_table(table_name, :id => false) do |t|
|
30
|
+
t.column :first_id, :integer
|
31
|
+
t.column :second_id, :integer
|
32
|
+
t.column :name, :string
|
33
|
+
t.column :value, :integer
|
34
|
+
end unless table_exists?
|
35
|
+
set_primary_keys :first_id, :second_id
|
47
36
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
37
|
+
|
38
|
+
class TestRevisionableAssociationRecord < ActiveRecord::Base
|
39
|
+
connection.create_table(table_name) do |t|
|
40
|
+
t.column :name, :string
|
41
|
+
t.column :value, :integer
|
42
|
+
t.column :test_revisionable_record_id, :integer
|
43
|
+
end unless table_exists?
|
44
|
+
|
45
|
+
has_one :sub_association, :class_name => 'TestRevisionableSubAssociationRecord'
|
51
46
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
|
48
|
+
class OtherRevisionableRecordsTestRevisionableRecords < ActiveRecord::Base
|
49
|
+
connection.create_table(table_name, :id => false) do |t|
|
50
|
+
t.column :test_revisionable_record_id, :integer
|
51
|
+
t.column :other_revisionable_record_id, :integer
|
52
|
+
end unless table_exists?
|
55
53
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
|
55
|
+
class TestRevisionableSubAssociationRecord < ActiveRecord::Base
|
56
|
+
connection.create_table(table_name) do |t|
|
57
|
+
t.column :name, :string
|
58
|
+
t.column :value, :integer
|
59
|
+
t.column :test_revisionable_association_record_id, :integer
|
60
|
+
end unless table_exists?
|
59
61
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
|
63
|
+
module ActsAsRevisionable
|
64
|
+
class TestModuleRecord < ActiveRecord::Base
|
65
|
+
connection.create_table(table_name) do |t|
|
66
|
+
t.column :name, :string
|
67
|
+
t.column :value, :integer
|
68
|
+
end unless table_exists?
|
69
|
+
end
|
63
70
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
71
|
+
|
72
|
+
class TestRevisionableRecord < ActiveRecord::Base
|
73
|
+
connection.create_table(table_name) do |t|
|
74
|
+
t.column :name, :string
|
75
|
+
t.column :value, :integer
|
76
|
+
t.column :test_revisionable_one_association_record_id, :integer
|
77
|
+
end unless table_exists?
|
78
|
+
|
79
|
+
has_many :associations, :class_name => 'TestRevisionableAssociationRecord'
|
80
|
+
has_many :legacy_associations, :class_name => 'TestRevisionableAssociationLegacyRecord'
|
81
|
+
has_many :composit_associations, :class_name => 'TestRevisionableAssociationComposite', :foreign_key => :first_id
|
82
|
+
has_and_belongs_to_many :other_revisionable_records
|
83
|
+
has_one :one_association, :class_name => 'TestRevisionableOneAssociationRecord'
|
84
|
+
|
85
|
+
acts_as_revisionable :associations => [{:associations => :sub_association}, :one_association, :other_revisionable_records]
|
69
86
|
end
|
70
87
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
def self.reflections
|
78
|
-
@reflections || {}
|
88
|
+
class OtherRevisionableRecord < ActiveRecord::Base
|
89
|
+
connection.create_table(table_name) do |t|
|
90
|
+
t.column :name, :string
|
91
|
+
t.column :value, :integer
|
92
|
+
t.column :type, :string
|
93
|
+
end unless table_exists?
|
79
94
|
end
|
80
95
|
|
81
|
-
|
82
|
-
|
96
|
+
class TestInheritanceRecord < OtherRevisionableRecord
|
97
|
+
def self.base_class
|
98
|
+
OtherRevisionableRecord
|
99
|
+
end
|
83
100
|
end
|
84
101
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class TestInheritanceRecord < TestRevisionableRecord
|
92
|
-
def self.base_class
|
93
|
-
TestRevisionableRecord
|
94
|
-
end
|
95
|
-
|
96
|
-
def initialize (attributes = {})
|
97
|
-
super({'type' => 'TestInheritanceRecord'}.merge(attributes))
|
98
|
-
end
|
99
|
-
|
100
|
-
def type= (val)
|
101
|
-
attributes['type'] = val
|
102
|
-
end
|
102
|
+
|
103
|
+
after :all do
|
104
|
+
ActsAsRevisionable::Test.delete_database
|
103
105
|
end
|
104
|
-
|
105
|
-
before
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
|
107
|
+
before :each do
|
108
|
+
ActsAsRevisionable::RevisionRecord.delete_all
|
109
|
+
TestRevisionableRecord.delete_all
|
110
|
+
TestRevisionableAssociationLegacyRecord.delete_all
|
111
|
+
TestRevisionableAssociationRecord.delete_all
|
112
|
+
TestRevisionableSubAssociationRecord.delete_all
|
113
|
+
ActsAsRevisionable::TestModuleRecord.delete_all
|
114
|
+
OtherRevisionableRecord.delete_all
|
115
|
+
TestInheritanceRecord.delete_all
|
116
|
+
OtherRevisionableRecordsTestRevisionableRecords.delete_all
|
117
|
+
TestRevisionableOneAssociationRecord.delete_all
|
109
118
|
end
|
110
|
-
|
119
|
+
|
111
120
|
it "should set the revision number before it creates the record" do
|
112
|
-
|
113
|
-
revision1 = ActsAsRevisionable::RevisionRecord.new(
|
121
|
+
record = TestRevisionableRecord.create(:name => "test")
|
122
|
+
revision1 = ActsAsRevisionable::RevisionRecord.new(record)
|
114
123
|
revision1.save!
|
115
|
-
revision2 = ActsAsRevisionable::RevisionRecord.new(
|
124
|
+
revision2 = ActsAsRevisionable::RevisionRecord.new(record)
|
116
125
|
revision2.save!
|
117
126
|
revision1.revision.should == 1
|
118
127
|
revision2.revision.should == 2
|
119
128
|
revision2.revision = 20
|
120
129
|
revision2.save!
|
121
|
-
revision3 = ActsAsRevisionable::RevisionRecord.new(
|
130
|
+
revision3 = ActsAsRevisionable::RevisionRecord.new(record)
|
122
131
|
revision3.save!
|
123
132
|
revision3.revision.should == 21
|
124
|
-
ActsAsRevisionable::RevisionRecord.delete_all
|
125
133
|
end
|
126
|
-
|
134
|
+
|
127
135
|
it "should serialize all the attributes of the original model" do
|
128
|
-
|
129
|
-
original =
|
136
|
+
original = TestRevisionableRecord.new('name' => 'revision', 'value' => 5)
|
137
|
+
original.id = 1
|
130
138
|
revision = ActsAsRevisionable::RevisionRecord.new(original)
|
131
139
|
revision.revisionable_id.should == 1
|
132
140
|
revision.revisionable_type.should == "TestRevisionableRecord"
|
133
|
-
revision.revision_attributes.should ==
|
141
|
+
revision.revision_attributes['name'].should == 'revision'
|
142
|
+
revision.revision_attributes['value'].should == 5
|
134
143
|
end
|
135
|
-
|
144
|
+
|
136
145
|
it "should serialize all the attributes of revisionable has_many associations" do
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
original
|
141
|
-
revisionable_associations = [TestRevisionableAssociationRecord.new(association_attributes_1), TestRevisionableAssociationRecord.new(association_attributes_2)]
|
142
|
-
revisionable_associations_reflection = stub(:association, :name => :revisionable_associations, :macro => :has_many, :options => {:dependent => :destroy})
|
143
|
-
non_revisionable_associations_reflection = stub(:association, :name => :non_revisionable_associations, :macro => :has_many, :options => {})
|
144
|
-
|
145
|
-
TestRevisionableRecord.should_receive(:revisionable_associations).and_return(:revisionable_associations => true)
|
146
|
-
TestRevisionableRecord.reflections = {:revisionable_associations => revisionable_associations_reflection, :non_revisionable_associations => non_revisionable_associations_reflection}
|
147
|
-
original.should_not_receive(:non_revisionable_associations)
|
148
|
-
original.should_receive(:revisionable_associations).and_return(revisionable_associations)
|
149
|
-
|
146
|
+
original = TestRevisionableRecord.new(:name => 'revision', :value => 1)
|
147
|
+
association_1 = original.associations.build(:name => 'association 1', :value => 2)
|
148
|
+
association_2 = original.associations.build(:name => 'association 2', :value => 3)
|
149
|
+
original.save!
|
150
150
|
revision = ActsAsRevisionable::RevisionRecord.new(original)
|
151
|
-
revision.revision_attributes.should ==
|
151
|
+
revision.revision_attributes['associations'].should == [
|
152
|
+
{
|
153
|
+
"id" => association_1.id,
|
154
|
+
"name" => "association 1",
|
155
|
+
"value" => 2,
|
156
|
+
"test_revisionable_record_id" => original.id,
|
157
|
+
"sub_association" => nil
|
158
|
+
},
|
159
|
+
{
|
160
|
+
"id" => association_2.id,
|
161
|
+
"name" => "association 2",
|
162
|
+
"value" => 3,
|
163
|
+
"test_revisionable_record_id" => original.id,
|
164
|
+
"sub_association" => nil
|
165
|
+
}
|
166
|
+
]
|
152
167
|
end
|
153
|
-
|
168
|
+
|
154
169
|
it "should serialize all the attributes of revisionable has_one associations" do
|
155
|
-
|
156
|
-
|
157
|
-
original
|
158
|
-
revisionable_association = TestRevisionableAssociationRecord.new(association_attributes)
|
159
|
-
revisionable_association_reflection = stub(:association, :name => :revisionable_association, :macro => :has_one, :options => {:dependent => :destroy})
|
160
|
-
non_revisionable_association_reflection = stub(:association, :name => :non_revisionable_association, :macro => :has_one, :options => {})
|
161
|
-
|
162
|
-
TestRevisionableRecord.should_receive(:revisionable_associations).and_return(:revisionable_association => true)
|
163
|
-
TestRevisionableRecord.reflections = {:revisionable_association => revisionable_association_reflection, :non_revisionable_association => non_revisionable_association_reflection}
|
164
|
-
original.should_not_receive(:non_revisionable_association)
|
165
|
-
original.should_receive(:revisionable_association).and_return(revisionable_association)
|
166
|
-
|
170
|
+
original = TestRevisionableRecord.new(:name => 'revision', :value => 1)
|
171
|
+
one = original.build_one_association(:name => 'one', :value => 2)
|
172
|
+
original.save!
|
167
173
|
revision = ActsAsRevisionable::RevisionRecord.new(original)
|
168
|
-
revision.revision_attributes.should ==
|
174
|
+
revision.revision_attributes['one_association'].should == {
|
175
|
+
"id"=>one.id, "name"=>"one", "value"=>2, "test_revisionable_record_id"=>original.id
|
176
|
+
}
|
169
177
|
end
|
170
|
-
|
178
|
+
|
171
179
|
it "should serialize all revisionable has_many_and_belongs_to_many associations" do
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
original.
|
180
|
-
|
180
|
+
original = TestRevisionableRecord.new(:name => 'revision', :value => 1)
|
181
|
+
other_1 = OtherRevisionableRecord.create(:name => "other 1")
|
182
|
+
other_2 = OtherRevisionableRecord.create(:name => "other 2")
|
183
|
+
other_3 = OtherRevisionableRecord.create(:name => "other 3")
|
184
|
+
original.other_revisionable_records << other_1
|
185
|
+
original.other_revisionable_records << other_2
|
186
|
+
original.other_revisionable_records << other_3
|
187
|
+
original.save!
|
181
188
|
revision = ActsAsRevisionable::RevisionRecord.new(original)
|
182
|
-
revision.revision_attributes.should ==
|
189
|
+
revision.revision_attributes['other_revisionable_records'].sort.should == [other_1.id, other_2.id, other_3.id]
|
183
190
|
end
|
184
|
-
|
185
|
-
it "should serialize revisionable associations of revisionable associations
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
association_2 = TestRevisionableAssociationRecord.new(association_attributes_2)
|
192
|
-
revisionable_associations = [association_1, association_2]
|
193
|
-
revisionable_associations_reflection = stub(:association, :name => :revisionable_associations, :macro => :has_many, :options => {:dependent => :destroy})
|
194
|
-
sub_association_attributes = {'id' => 4, 'name' => 'sub_association_1'}
|
195
|
-
sub_association = TestRevisionableSubAssociationRecord.new(sub_association_attributes)
|
196
|
-
sub_association_reflection = stub(:sub_association, :name => :sub_association, :macro => :has_one, :options => {:dependent => :destroy})
|
197
|
-
|
198
|
-
TestRevisionableRecord.should_receive(:revisionable_associations).and_return(:revisionable_associations => {:sub_association => true})
|
199
|
-
TestRevisionableRecord.reflections = {:revisionable_associations => revisionable_associations_reflection}
|
200
|
-
TestRevisionableAssociationRecord.reflections = {:sub_association => sub_association_reflection}
|
201
|
-
original.should_receive(:revisionable_associations).and_return(revisionable_associations)
|
202
|
-
association_1.should_receive(:sub_association).and_return(sub_association)
|
203
|
-
association_2.should_receive(:sub_association).and_return(nil)
|
204
|
-
|
191
|
+
|
192
|
+
it "should serialize revisionable associations of revisionable associations" do
|
193
|
+
original = TestRevisionableRecord.new(:name => 'revision', :value => 1)
|
194
|
+
association_1 = original.associations.build(:name => 'association 1', :value => 2)
|
195
|
+
association_2 = original.associations.build(:name => 'association 2', :value => 3)
|
196
|
+
sub_association = association_1.build_sub_association(:name => 'sub', :value => 4)
|
197
|
+
original.save!
|
205
198
|
revision = ActsAsRevisionable::RevisionRecord.new(original)
|
206
|
-
revision.revision_attributes.should ==
|
199
|
+
revision.revision_attributes.should == {
|
200
|
+
"id" => original.id,
|
201
|
+
"name" => "revision",
|
202
|
+
"value" => 1,
|
203
|
+
"associations" => [
|
204
|
+
{
|
205
|
+
"id" => association_1.id,
|
206
|
+
"name" => "association 1",
|
207
|
+
"value" => 2,
|
208
|
+
"test_revisionable_record_id" => original.id,
|
209
|
+
"sub_association" => {
|
210
|
+
"id" => sub_association.id,
|
211
|
+
"name" => "sub",
|
212
|
+
"value" => 4,
|
213
|
+
"test_revisionable_association_record_id" => association_1.id
|
214
|
+
}
|
215
|
+
},
|
216
|
+
{
|
217
|
+
"id" => association_2.id,
|
218
|
+
"name" => "association 2",
|
219
|
+
"value" => 3,
|
220
|
+
"test_revisionable_record_id" => original.id,
|
221
|
+
"sub_association" => nil
|
222
|
+
}
|
223
|
+
],
|
224
|
+
"test_revisionable_one_association_record_id" => nil,
|
225
|
+
"other_revisionable_records" => [],
|
226
|
+
"one_association" => nil
|
227
|
+
}
|
207
228
|
end
|
208
|
-
|
229
|
+
|
209
230
|
it "should be able to restore the original model using Ruby serialization" do
|
210
|
-
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
|
231
|
+
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5, 'test_revisionable_one_association_record_id' => nil}
|
211
232
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(attributes), :ruby)
|
212
233
|
revision.data = Zlib::Deflate.deflate(Marshal.dump(attributes))
|
213
234
|
restored = revision.restore
|
@@ -215,9 +236,9 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
215
236
|
restored.id.should == 1
|
216
237
|
restored.attributes.should == attributes
|
217
238
|
end
|
218
|
-
|
239
|
+
|
219
240
|
it "should be able to restore the original model using YAML serialization" do
|
220
|
-
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
|
241
|
+
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5, 'test_revisionable_one_association_record_id' => nil}
|
221
242
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(attributes), :yaml)
|
222
243
|
revision.data = Zlib::Deflate.deflate(YAML.dump(attributes))
|
223
244
|
restored = revision.restore
|
@@ -225,9 +246,9 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
225
246
|
restored.id.should == 1
|
226
247
|
restored.attributes.should == attributes
|
227
248
|
end
|
228
|
-
|
249
|
+
|
229
250
|
it "should be able to restore the original model using XML serialization" do
|
230
|
-
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
|
251
|
+
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5, 'test_revisionable_one_association_record_id' => nil}
|
231
252
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(attributes), :xml)
|
232
253
|
revision.data = Zlib::Deflate.deflate(YAML.dump(attributes))
|
233
254
|
restored = revision.restore
|
@@ -235,113 +256,105 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
235
256
|
restored.id.should == 1
|
236
257
|
restored.attributes.should == attributes
|
237
258
|
end
|
238
|
-
|
259
|
+
|
239
260
|
it "should be able to restore associations" do
|
240
261
|
restored = TestRevisionableRecord.new
|
241
262
|
attributes = {'id' => 1, 'name' => 'revision', 'value' => Time.now, :associations => {'id' => 2, 'value' => 'val'}}
|
242
263
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
243
264
|
revision.data = Zlib::Deflate.deflate(Marshal.dump(attributes))
|
244
|
-
associations_reflection = stub(:associations, :name => :associations, :macro => :has_many, :options => {:dependent => :destroy})
|
245
|
-
TestRevisionableRecord.reflections = {:associations => associations_reflection}
|
246
265
|
TestRevisionableRecord.should_receive(:new).and_return(restored)
|
247
266
|
revision.should_receive(:restore_association).with(restored, :associations, {'id' => 2, 'value' => 'val'})
|
248
267
|
restored = revision.restore
|
249
268
|
end
|
250
|
-
|
269
|
+
|
251
270
|
it "should be able to restore the has_many associations" do
|
252
271
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
253
272
|
record = TestRevisionableRecord.new
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
associations = mock(:associations)
|
258
|
-
record.should_receive(:associations).and_return(associations)
|
259
|
-
associated_record = TestRevisionableAssociationRecord.new
|
260
|
-
associations.should_receive(:build).and_return(associated_record)
|
261
|
-
|
262
|
-
revision.send(:restore_association, record, :associations, {'id' => 1, 'value' => 'val'})
|
273
|
+
revision.send(:restore_association, record, :associations, {'id' => 1, 'name' => 'assoc', 'value' => 10})
|
274
|
+
record.associations.size.should == 1
|
275
|
+
associated_record = record.associations.first
|
263
276
|
associated_record.id.should == 1
|
264
|
-
associated_record.
|
277
|
+
associated_record.name.should == 'assoc'
|
278
|
+
associated_record.value.should == 10
|
265
279
|
end
|
266
|
-
|
267
|
-
it "should be able to restore the
|
280
|
+
|
281
|
+
it "should be able to restore the has_many associations with a legacy primary key" do
|
268
282
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
269
283
|
record = TestRevisionableRecord.new
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
associated_record = TestRevisionableAssociationRecord.new
|
274
|
-
TestRevisionableAssociationRecord.should_receive(:new).and_return(associated_record)
|
275
|
-
record.should_receive(:association=).with(associated_record)
|
276
|
-
|
277
|
-
revision.send(:restore_association, record, :association, {'id' => 1, 'value' => 'val'})
|
284
|
+
revision.send(:restore_association, record, :legacy_associations, {'legacy_id' => 1, 'name' => 'legacy', 'value' => 10})
|
285
|
+
record.legacy_associations.size.should == 1
|
286
|
+
associated_record = record.legacy_associations.first
|
278
287
|
associated_record.id.should == 1
|
279
|
-
associated_record.
|
288
|
+
associated_record.name.should == 'legacy'
|
289
|
+
associated_record.value.should == 10
|
280
290
|
end
|
281
|
-
|
291
|
+
|
292
|
+
it "should be able to restore the has_many associations with composite primary keys" do
|
293
|
+
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
294
|
+
record = TestRevisionableRecord.new
|
295
|
+
revision.send(:restore_association, record, :composit_associations, {'first_id' => 1, 'second_id' => 2, 'name' => 'composit', 'value' => 10})
|
296
|
+
record.composit_associations.size.should == 1
|
297
|
+
associated_record = record.composit_associations.first
|
298
|
+
associated_record.first_id.should == 1
|
299
|
+
associated_record.second_id.should == 2
|
300
|
+
associated_record.name.should == 'composit'
|
301
|
+
associated_record.value.should == 10
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should be able to restore the has_one associations" do
|
305
|
+
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
306
|
+
record = TestRevisionableRecord.new
|
307
|
+
revision.send(:restore_association, record, :one_association, {'id' => 1, 'name' => 'one', 'value' => 1})
|
308
|
+
record.one_association.id.should == 1
|
309
|
+
record.one_association.name.should == 'one'
|
310
|
+
record.one_association.value.should == 1
|
311
|
+
end
|
312
|
+
|
282
313
|
it "should be able to restore the has_and_belongs_to_many associations" do
|
314
|
+
other_1 = OtherRevisionableRecord.create(:name => "other 1")
|
315
|
+
other_2 = OtherRevisionableRecord.create(:name => "other 2")
|
316
|
+
other_3 = OtherRevisionableRecord.create(:name => "other 3")
|
283
317
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
284
318
|
record = TestRevisionableRecord.new
|
285
|
-
|
286
|
-
|
287
|
-
TestRevisionableRecord.reflections = {:associations => associations_reflection}
|
288
|
-
record.should_receive(:association_ids=).with([2, 3, 4])
|
289
|
-
|
290
|
-
revision.send(:restore_association, record, :associations, [2, 3, 4])
|
319
|
+
revision.send(:restore_association, record, :other_revisionable_records, [other_1.id, other_2.id, other_3.id])
|
320
|
+
record.other_revisionable_records.collect{|r| r.id}.sort.should == [other_1.id, other_2.id, other_3.id]
|
291
321
|
end
|
292
|
-
|
322
|
+
|
293
323
|
it "should be able to restore associations of associations" do
|
294
324
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
295
325
|
record = TestRevisionableRecord.new
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
associations = mock(:associations)
|
300
|
-
record.should_receive(:associations).and_return(associations)
|
301
|
-
associated_record = TestRevisionableAssociationRecord.new
|
302
|
-
associations.should_receive(:build).and_return(associated_record)
|
303
|
-
|
304
|
-
sub_associated_record = TestRevisionableSubAssociationRecord.new
|
305
|
-
TestRevisionableAssociationRecord.should_receive(:new).and_return(sub_associated_record)
|
306
|
-
sub_association_reflection = stub(:sub_association, :name => :sub_association, :macro => :has_one, :klass => TestRevisionableAssociationRecord, :options => {:dependent => :destroy})
|
307
|
-
TestRevisionableAssociationRecord.reflections = {:sub_association => sub_association_reflection}
|
308
|
-
associated_record.should_receive(:sub_association=).with(sub_associated_record)
|
309
|
-
|
310
|
-
revision.send(:restore_association, record, :associations, {'id' => 1, 'value' => 'val', :sub_association => {'id' => 2, 'value' => 'sub'}})
|
326
|
+
revision.send(:restore_association, record, :associations, {'id' => 1, 'name' => 'assoc', 'value' => 10, :sub_association => {'id' => 2, 'name' => 'sub', 'value' => 1000}})
|
327
|
+
record.associations.size.should == 1
|
328
|
+
associated_record = record.associations.first
|
311
329
|
associated_record.id.should == 1
|
312
|
-
associated_record.
|
330
|
+
associated_record.name.should == 'assoc'
|
331
|
+
associated_record.value.should == 10
|
332
|
+
sub_associated_record = associated_record.sub_association
|
313
333
|
sub_associated_record.id.should == 2
|
314
|
-
sub_associated_record.
|
334
|
+
sub_associated_record.name.should == 'sub'
|
335
|
+
sub_associated_record.value.should == 1000
|
315
336
|
end
|
316
|
-
|
337
|
+
|
317
338
|
it "should be able to restore a record for a model that has changed and add errors to the restored record" do
|
318
339
|
restored = TestRevisionableRecord.new
|
319
340
|
attributes = {'id' => 1, 'name' => 'revision', 'value' => Time.now, 'deleted_attribute' => 'abc', :bad_association => {'id' => 3, 'value' => :val}, :associations => {'id' => 2, 'value' => 'val', 'other' => 'val2'}}
|
320
341
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new)
|
321
342
|
revision.data = Zlib::Deflate.deflate(Marshal.dump(attributes))
|
322
343
|
TestRevisionableRecord.should_receive(:new).and_return(restored)
|
323
|
-
|
344
|
+
|
324
345
|
associations = mock(:associations)
|
325
346
|
restored.should_receive(:associations).and_return(associations)
|
326
347
|
associated_record = TestRevisionableAssociationRecord.new
|
327
348
|
associations.should_receive(:build).and_return(associated_record)
|
328
|
-
|
329
|
-
mock_record_errors = {}
|
330
|
-
restored.stub!(:errors).and_return(mock_record_errors)
|
331
|
-
mock_record_errors.should_receive(:add).with(:bad_association, "could not be restored to {\"id\"=>3, \"value\"=>:val}")
|
332
|
-
mock_record_errors.should_receive(:add).with(:deleted_attribute, 'could not be restored to "abc"')
|
333
|
-
mock_record_errors.should_receive(:add).with(:associations, 'could not be restored from the revision')
|
334
|
-
|
335
|
-
mock_association_errors = mock(:errors)
|
336
|
-
associated_record.stub!(:errors).and_return(mock_association_errors)
|
337
|
-
mock_association_errors.should_receive(:add).with(:other, 'could not be restored to "val2"')
|
338
|
-
|
339
|
-
associations_reflection = stub(:associations, :name => :associations, :macro => :has_many, :options => {:dependent => :destroy})
|
340
|
-
TestRevisionableRecord.reflections = {:associations => associations_reflection}
|
341
|
-
|
349
|
+
|
342
350
|
restored = revision.restore
|
351
|
+
|
352
|
+
restored.errors[:deleted_attribute].should include("could not be restored to \"abc\"")
|
353
|
+
restored.errors[:bad_association].should include("could not be restored to {\"id\"=>3, \"value\"=>:val}")
|
354
|
+
restored.errors[:associations].should include("could not be restored from the revision")
|
355
|
+
associated_record.errors[:other].should include("could not be restored to \"val2\"")
|
343
356
|
end
|
344
|
-
|
357
|
+
|
345
358
|
it "should be able to truncate the revisions for a record" do
|
346
359
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
|
347
360
|
revision.revision = 20
|
@@ -349,7 +362,7 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
349
362
|
ActsAsRevisionable::RevisionRecord.should_receive(:delete_all).with(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', 'TestRevisionableRecord', 1, 20])
|
350
363
|
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => 15)
|
351
364
|
end
|
352
|
-
|
365
|
+
|
353
366
|
it "should be able to truncate the revisions for a record by age" do
|
354
367
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
|
355
368
|
revision.revision = 20
|
@@ -360,25 +373,31 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
360
373
|
ActsAsRevisionable::RevisionRecord.should_receive(:delete_all).with(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', 'TestRevisionableRecord', 1, 20])
|
361
374
|
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :minimum_age => minimum_age)
|
362
375
|
end
|
363
|
-
|
376
|
+
|
364
377
|
it "should not truncate the revisions for a record if it doesn't have enough" do
|
365
378
|
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => ['revisionable_type = ? AND revisionable_id = ?', 'TestRevisionableRecord', 1], :offset => 15, :order => 'revision DESC').and_return(nil)
|
366
379
|
ActsAsRevisionable::RevisionRecord.should_not_receive(:delete_all)
|
367
380
|
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => 15)
|
368
381
|
end
|
369
|
-
|
382
|
+
|
370
383
|
it "should not truncate the revisions for a record if no limit or minimum_age is set" do
|
371
384
|
ActsAsRevisionable::RevisionRecord.should_not_receive(:find)
|
372
385
|
ActsAsRevisionable::RevisionRecord.should_not_receive(:delete_all)
|
373
386
|
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => nil, :minimum_age => nil)
|
374
387
|
end
|
375
|
-
|
388
|
+
|
376
389
|
it "should be able to find a record by revisioned type and id" do
|
377
390
|
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
|
378
391
|
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => {:revisionable_type => 'TestRevisionableRecord', :revisionable_id => 1, :revision => 2}).and_return(revision)
|
379
392
|
ActsAsRevisionable::RevisionRecord.find_revision(TestRevisionableRecord, 1, 2).should == revision
|
380
393
|
end
|
381
394
|
|
395
|
+
it "should find the last revision" do
|
396
|
+
revision = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new(:name => 'name'))
|
397
|
+
ActsAsRevisionable::RevisionRecord.should_receive(:find).with(:first, :conditions => {:revisionable_type => 'TestRevisionableRecord', :revisionable_id => 1}, :order => "revision DESC").and_return(revision)
|
398
|
+
ActsAsRevisionable::RevisionRecord.last_revision(TestRevisionableRecord, 1).should == revision
|
399
|
+
end
|
400
|
+
|
382
401
|
it "should handle module namespaces" do
|
383
402
|
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
|
384
403
|
revision = ActsAsRevisionable::RevisionRecord.new(ActsAsRevisionable::TestModuleRecord.new(attributes))
|
@@ -386,7 +405,7 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
386
405
|
restored = revision.restore
|
387
406
|
restored.class.should == ActsAsRevisionable::TestModuleRecord
|
388
407
|
end
|
389
|
-
|
408
|
+
|
390
409
|
it "should handle single table inheritance" do
|
391
410
|
attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
|
392
411
|
record = TestInheritanceRecord.new(attributes)
|
@@ -395,34 +414,52 @@ describe ActsAsRevisionable::RevisionRecord do
|
|
395
414
|
restored = revision.restore
|
396
415
|
restored.class.should == TestInheritanceRecord
|
397
416
|
end
|
398
|
-
|
417
|
+
|
399
418
|
it "should really save the revision records to the database and restore without any mocking" do
|
400
|
-
ActsAsRevisionable::RevisionRecord.delete_all
|
401
419
|
ActsAsRevisionable::RevisionRecord.count.should == 0
|
402
|
-
|
403
|
-
|
404
|
-
original = TestRevisionableRecord.new(attributes)
|
405
|
-
original.attributes['name'] = 'revision 1'
|
420
|
+
|
421
|
+
original = TestRevisionableRecord.create(:name => 'revision 1', :value => 100)
|
406
422
|
ActsAsRevisionable::RevisionRecord.new(original).save!
|
407
|
-
first_revision = ActsAsRevisionable::RevisionRecord.
|
408
|
-
original.
|
423
|
+
first_revision = ActsAsRevisionable::RevisionRecord.first
|
424
|
+
original.name = 'revision 2'
|
409
425
|
ActsAsRevisionable::RevisionRecord.new(original).save!
|
410
|
-
original.
|
426
|
+
original.name = 'revision 3'
|
411
427
|
ActsAsRevisionable::RevisionRecord.new(original).save!
|
412
428
|
ActsAsRevisionable::RevisionRecord.count.should == 3
|
413
|
-
|
414
|
-
record = ActsAsRevisionable::RevisionRecord.find_revision(TestRevisionableRecord,
|
429
|
+
|
430
|
+
record = ActsAsRevisionable::RevisionRecord.find_revision(TestRevisionableRecord, original.id, 1).restore
|
415
431
|
record.class.should == TestRevisionableRecord
|
416
|
-
record.id.should ==
|
417
|
-
record.
|
418
|
-
|
419
|
-
|
432
|
+
record.id.should == original.id
|
433
|
+
record.name.should == 'revision 1'
|
434
|
+
record.value.should == 100
|
435
|
+
|
436
|
+
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, original.id, :limit => 2)
|
420
437
|
ActsAsRevisionable::RevisionRecord.count.should == 2
|
421
438
|
ActsAsRevisionable::RevisionRecord.find_by_id(first_revision.id).should == nil
|
422
|
-
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord,
|
439
|
+
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, original.id, :limit => 0, :minimum_age => 1.week)
|
423
440
|
ActsAsRevisionable::RevisionRecord.count.should == 2
|
424
|
-
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord,
|
441
|
+
ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, original.id, :limit => 0)
|
425
442
|
ActsAsRevisionable::RevisionRecord.count.should == 0
|
426
443
|
end
|
427
444
|
|
445
|
+
it "should delete revisions for models in a class that no longer exist if they are older than a specified number of seconds" do
|
446
|
+
record_1 = TestRevisionableRecord.create(:name => 'record_1')
|
447
|
+
record_2 = TestRevisionableAssociationLegacyRecord.new(:name => 'record_2')
|
448
|
+
record_2.id = record_1.id
|
449
|
+
record_2.save!
|
450
|
+
revision_0 = ActsAsRevisionable::RevisionRecord.create(record_1)
|
451
|
+
revision_1 = ActsAsRevisionable::RevisionRecord.create(record_1)
|
452
|
+
revision_1.trash!
|
453
|
+
revision_2 = ActsAsRevisionable::RevisionRecord.create(record_2)
|
454
|
+
revision_2.trash!
|
455
|
+
revision_3 = ActsAsRevisionable::RevisionRecord.create(TestRevisionableRecord.create(:name => 'record_3'))
|
456
|
+
now = Time.now
|
457
|
+
Time.stub(:now => now + 60)
|
458
|
+
revision_4 = ActsAsRevisionable::RevisionRecord.create(TestRevisionableRecord.create(:name => 'record_4'))
|
459
|
+
revision_4.trash!
|
460
|
+
ActsAsRevisionable::RevisionRecord.count.should == 5
|
461
|
+
ActsAsRevisionable::RevisionRecord.empty_trash(TestRevisionableRecord, 30)
|
462
|
+
ActsAsRevisionable::RevisionRecord.count.should == 3
|
463
|
+
ActsAsRevisionable::RevisionRecord.all.collect{|r| r.id}.sort.should == [revision_2.id, revision_3.id, revision_4.id]
|
464
|
+
end
|
428
465
|
end
|