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.
@@ -1,213 +1,234 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
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
- end
9
-
10
- after :all do
11
- ActsAsRevisionable::Test.delete_database
12
- end
13
-
14
- class TestRevisionableRecord
15
- attr_accessor :attributes
16
-
17
- def self.base_class
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
- def self.inheritance_column
22
- 'type'
23
- end
24
-
25
- def self.store_full_sti_class
26
- true
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
- def id= (val)
46
- attributes['id'] = val
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
- def name= (val)
50
- attributes['name'] = val
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
- def value= (val)
54
- attributes['value'] = val
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
- def self.revisionable_associations
58
- nil
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
- def self.type_name_with_module (type_name)
62
- type_name
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
- end
65
-
66
- class TestRevisionableAssociationRecord < TestRevisionableRecord
67
- def self.reflections
68
- @reflections || {}
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
- def self.reflections= (vals)
72
- @reflections = vals
73
- end
74
- end
75
-
76
- class TestRevisionableSubAssociationRecord < TestRevisionableRecord
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
- def self.reflections= (vals)
82
- @reflections = vals
96
+ class TestInheritanceRecord < OtherRevisionableRecord
97
+ def self.base_class
98
+ OtherRevisionableRecord
99
+ end
83
100
  end
84
101
  end
85
-
86
- module ActsAsRevisionable
87
- class TestModuleRecord < TestRevisionableRecord
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(:each) do
106
- TestRevisionableRecord.reflections = nil
107
- TestRevisionableAssociationRecord.reflections = nil
108
- TestRevisionableSubAssociationRecord.reflections = nil
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
- ActsAsRevisionable::RevisionRecord.delete_all
113
- revision1 = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new('id' => 1))
121
+ record = TestRevisionableRecord.create(:name => "test")
122
+ revision1 = ActsAsRevisionable::RevisionRecord.new(record)
114
123
  revision1.save!
115
- revision2 = ActsAsRevisionable::RevisionRecord.new(TestRevisionableRecord.new('id' => 1))
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(TestRevisionableRecord.new('id' => 1))
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
- attributes = {'id' => 1, 'name' => 'revision', 'value' => 5}
129
- original = TestRevisionableRecord.new(attributes)
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 == attributes
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
- attributes = {'id' => 1, 'name' => 'revision', 'value' => Time.now}
138
- association_attributes_1 = {'id' => 2, 'name' => 'association_1'}
139
- association_attributes_2 = {'id' => 3, 'name' => 'association_2'}
140
- original = TestRevisionableRecord.new(attributes)
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 == attributes.merge('revisionable_associations' => [association_attributes_1, association_attributes_2])
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
- attributes = {'id' => 1, 'name' => 'revision', 'value' => Date.today}
156
- association_attributes = {'id' => 2, 'name' => 'association_1'}
157
- original = TestRevisionableRecord.new(attributes)
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 == attributes.merge('revisionable_association' => association_attributes)
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
- attributes = {'id' => 1, 'name' => 'revision'}
173
- original = TestRevisionableRecord.new(attributes)
174
- revisionable_associations_reflection = stub(:association, :name => :revisionable_associations, :macro => :has_and_belongs_to_many, :options => {:dependent => :destroy})
175
- non_revisionable_associations_reflection = stub(:association, :name => :non_revisionable_associations, :macro => :has_and_belongs_to_many, :options => {})
176
-
177
- TestRevisionableRecord.should_receive(:revisionable_associations).and_return(:revisionable_associations => true)
178
- TestRevisionableRecord.reflections = {:revisionable_associations => revisionable_associations_reflection, :non_revisionable_associations => non_revisionable_associations_reflection}
179
- original.should_receive(:revisionable_association_ids).and_return([2, 3, 4])
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 == attributes.merge('revisionable_associations' => [2, 3, 4])
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 with :dependent => :destroy" do
186
- attributes = {'id' => 1, 'name' => 'revision', 'value' => Time.now}
187
- association_attributes_1 = {'id' => 2, 'name' => 'association_1'}
188
- association_attributes_2 = {'id' => 3, 'name' => 'association_2'}
189
- original = TestRevisionableRecord.new(attributes)
190
- association_1 = TestRevisionableAssociationRecord.new(association_attributes_1)
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 == attributes.merge('revisionable_associations' => [association_attributes_1.merge('sub_association' => sub_association_attributes), association_attributes_2.merge('sub_association' => nil)])
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
- associations_reflection = stub(:associations, :name => :associations, :macro => :has_many, :options => {:dependent => :destroy})
256
- TestRevisionableRecord.reflections = {:associations => associations_reflection}
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.attributes.should == {'id' => 1, 'value' => 'val'}
277
+ associated_record.name.should == 'assoc'
278
+ associated_record.value.should == 10
265
279
  end
266
-
267
- it "should be able to restore the has_one associations" do
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
- association_reflection = stub(:associations, :name => :association, :macro => :has_one, :klass => TestRevisionableAssociationRecord, :options => {:dependent => :destroy})
272
- TestRevisionableRecord.reflections = {:association => association_reflection}
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.attributes.should == {'id' => 1, 'value' => 'val'}
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
- associations_reflection = stub(:associations, :name => :associations, :macro => :has_and_belongs_to_many, :options => {})
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
- associations_reflection = stub(:associations, :name => :associations, :macro => :has_many, :options => {:dependent => :destroy})
298
- TestRevisionableRecord.reflections = {:associations => associations_reflection}
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.attributes.should == {'id' => 1, 'value' => 'val'}
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.attributes.should == {'id' => 2, 'value' => 'sub'}
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
- attributes = {'id' => 1, 'value' => rand(1000000)}
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.find(:first)
408
- original.attributes['name'] = 'revision 2'
423
+ first_revision = ActsAsRevisionable::RevisionRecord.first
424
+ original.name = 'revision 2'
409
425
  ActsAsRevisionable::RevisionRecord.new(original).save!
410
- original.attributes['name'] = 'revision 3'
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, 1, 1).restore
429
+
430
+ record = ActsAsRevisionable::RevisionRecord.find_revision(TestRevisionableRecord, original.id, 1).restore
415
431
  record.class.should == TestRevisionableRecord
416
- record.id.should == 1
417
- record.attributes.should == attributes.merge('name' => 'revision 1')
418
-
419
- ActsAsRevisionable::RevisionRecord.truncate_revisions(TestRevisionableRecord, 1, :limit => 2)
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, 1, :limit => 0, :minimum_age => 1.week)
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, 1, :limit => 0)
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