paper_trail 3.0.9 → 4.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +1 -2
- data/.travis.yml +5 -0
- data/CHANGELOG.md +37 -23
- data/README.md +170 -63
- data/gemfiles/3.0.gemfile +10 -4
- data/lib/generators/paper_trail/install_generator.rb +19 -3
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
- data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
- data/lib/paper_trail.rb +24 -4
- data/lib/paper_trail/cleaner.rb +3 -3
- data/lib/paper_trail/config.rb +17 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -0
- data/lib/paper_trail/frameworks/rspec.rb +5 -0
- data/lib/paper_trail/has_paper_trail.rb +112 -38
- data/lib/paper_trail/version_association_concern.rb +13 -0
- data/lib/paper_trail/version_concern.rb +145 -38
- data/lib/paper_trail/version_number.rb +3 -3
- data/paper_trail.gemspec +11 -4
- data/spec/generators/install_generator_spec.rb +4 -4
- data/spec/models/fluxor_spec.rb +19 -0
- data/spec/models/gadget_spec.rb +10 -10
- data/spec/models/joined_version_spec.rb +9 -9
- data/spec/models/post_with_status_spec.rb +3 -3
- data/spec/models/version_spec.rb +49 -71
- data/spec/models/widget_spec.rb +124 -71
- data/spec/modules/version_concern_spec.rb +8 -8
- data/spec/modules/version_number_spec.rb +16 -16
- data/spec/paper_trail_spec.rb +17 -17
- data/spec/rails_helper.rb +34 -0
- data/spec/requests/articles_spec.rb +11 -11
- data/spec/spec_helper.rb +77 -36
- data/test/dummy/app/models/animal.rb +0 -2
- data/test/dummy/app/models/book.rb +4 -0
- data/test/dummy/app/models/customer.rb +4 -0
- data/test/dummy/app/models/editor.rb +4 -0
- data/test/dummy/app/models/editorship.rb +5 -0
- data/test/dummy/app/models/line_item.rb +4 -0
- data/test/dummy/app/models/order.rb +5 -0
- data/test/dummy/app/models/person.rb +1 -1
- data/test/dummy/app/models/post.rb +0 -1
- data/test/dummy/app/models/song.rb +0 -20
- data/test/dummy/app/models/widget.rb +4 -0
- data/test/dummy/config/application.rb +3 -0
- data/test/dummy/config/initializers/paper_trail.rb +1 -1
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +41 -0
- data/test/dummy/db/schema.rb +95 -25
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/rails.js +175 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/test_helper.rb +2 -2
- data/test/time_travel_helper.rb +15 -0
- data/test/unit/model_test.rb +613 -185
- data/test/unit/serializer_test.rb +3 -3
- metadata +104 -54
- data/spec/models/animal_spec.rb +0 -19
- data/test/dummy/public/javascripts/prototype.js +0 -6001
@@ -0,0 +1,175 @@
|
|
1
|
+
(function() {
|
2
|
+
// Technique from Juriy Zaytsev
|
3
|
+
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
|
4
|
+
function isEventSupported(eventName) {
|
5
|
+
var el = document.createElement('div');
|
6
|
+
eventName = 'on' + eventName;
|
7
|
+
var isSupported = (eventName in el);
|
8
|
+
if (!isSupported) {
|
9
|
+
el.setAttribute(eventName, 'return;');
|
10
|
+
isSupported = typeof el[eventName] == 'function';
|
11
|
+
}
|
12
|
+
el = null;
|
13
|
+
return isSupported;
|
14
|
+
}
|
15
|
+
|
16
|
+
function isForm(element) {
|
17
|
+
return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
|
18
|
+
}
|
19
|
+
|
20
|
+
function isInput(element) {
|
21
|
+
if (Object.isElement(element)) {
|
22
|
+
var name = element.nodeName.toUpperCase()
|
23
|
+
return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
|
24
|
+
}
|
25
|
+
else return false
|
26
|
+
}
|
27
|
+
|
28
|
+
var submitBubbles = isEventSupported('submit'),
|
29
|
+
changeBubbles = isEventSupported('change')
|
30
|
+
|
31
|
+
if (!submitBubbles || !changeBubbles) {
|
32
|
+
// augment the Event.Handler class to observe custom events when needed
|
33
|
+
Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
|
34
|
+
function(init, element, eventName, selector, callback) {
|
35
|
+
init(element, eventName, selector, callback)
|
36
|
+
// is the handler being attached to an element that doesn't support this event?
|
37
|
+
if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
|
38
|
+
(!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
|
39
|
+
// "submit" => "emulated:submit"
|
40
|
+
this.eventName = 'emulated:' + this.eventName
|
41
|
+
}
|
42
|
+
}
|
43
|
+
)
|
44
|
+
}
|
45
|
+
|
46
|
+
if (!submitBubbles) {
|
47
|
+
// discover forms on the page by observing focus events which always bubble
|
48
|
+
document.on('focusin', 'form', function(focusEvent, form) {
|
49
|
+
// special handler for the real "submit" event (one-time operation)
|
50
|
+
if (!form.retrieve('emulated:submit')) {
|
51
|
+
form.on('submit', function(submitEvent) {
|
52
|
+
var emulated = form.fire('emulated:submit', submitEvent, true)
|
53
|
+
// if custom event received preventDefault, cancel the real one too
|
54
|
+
if (emulated.returnValue === false) submitEvent.preventDefault()
|
55
|
+
})
|
56
|
+
form.store('emulated:submit', true)
|
57
|
+
}
|
58
|
+
})
|
59
|
+
}
|
60
|
+
|
61
|
+
if (!changeBubbles) {
|
62
|
+
// discover form inputs on the page
|
63
|
+
document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
|
64
|
+
// special handler for real "change" events
|
65
|
+
if (!input.retrieve('emulated:change')) {
|
66
|
+
input.on('change', function(changeEvent) {
|
67
|
+
input.fire('emulated:change', changeEvent, true)
|
68
|
+
})
|
69
|
+
input.store('emulated:change', true)
|
70
|
+
}
|
71
|
+
})
|
72
|
+
}
|
73
|
+
|
74
|
+
function handleRemote(element) {
|
75
|
+
var method, url, params;
|
76
|
+
|
77
|
+
var event = element.fire("ajax:before");
|
78
|
+
if (event.stopped) return false;
|
79
|
+
|
80
|
+
if (element.tagName.toLowerCase() === 'form') {
|
81
|
+
method = element.readAttribute('method') || 'post';
|
82
|
+
url = element.readAttribute('action');
|
83
|
+
params = element.serialize();
|
84
|
+
} else {
|
85
|
+
method = element.readAttribute('data-method') || 'get';
|
86
|
+
url = element.readAttribute('href');
|
87
|
+
params = {};
|
88
|
+
}
|
89
|
+
|
90
|
+
new Ajax.Request(url, {
|
91
|
+
method: method,
|
92
|
+
parameters: params,
|
93
|
+
evalScripts: true,
|
94
|
+
|
95
|
+
onComplete: function(request) { element.fire("ajax:complete", request); },
|
96
|
+
onSuccess: function(request) { element.fire("ajax:success", request); },
|
97
|
+
onFailure: function(request) { element.fire("ajax:failure", request); }
|
98
|
+
});
|
99
|
+
|
100
|
+
element.fire("ajax:after");
|
101
|
+
}
|
102
|
+
|
103
|
+
function handleMethod(element) {
|
104
|
+
var method = element.readAttribute('data-method'),
|
105
|
+
url = element.readAttribute('href'),
|
106
|
+
csrf_param = $$('meta[name=csrf-param]')[0],
|
107
|
+
csrf_token = $$('meta[name=csrf-token]')[0];
|
108
|
+
|
109
|
+
var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
|
110
|
+
element.parentNode.insert(form);
|
111
|
+
|
112
|
+
if (method !== 'post') {
|
113
|
+
var field = new Element('input', { type: 'hidden', name: '_method', value: method });
|
114
|
+
form.insert(field);
|
115
|
+
}
|
116
|
+
|
117
|
+
if (csrf_param) {
|
118
|
+
var param = csrf_param.readAttribute('content'),
|
119
|
+
token = csrf_token.readAttribute('content'),
|
120
|
+
field = new Element('input', { type: 'hidden', name: param, value: token });
|
121
|
+
form.insert(field);
|
122
|
+
}
|
123
|
+
|
124
|
+
form.submit();
|
125
|
+
}
|
126
|
+
|
127
|
+
|
128
|
+
document.on("click", "*[data-confirm]", function(event, element) {
|
129
|
+
var message = element.readAttribute('data-confirm');
|
130
|
+
if (!confirm(message)) event.stop();
|
131
|
+
});
|
132
|
+
|
133
|
+
document.on("click", "a[data-remote]", function(event, element) {
|
134
|
+
if (event.stopped) return;
|
135
|
+
handleRemote(element);
|
136
|
+
event.stop();
|
137
|
+
});
|
138
|
+
|
139
|
+
document.on("click", "a[data-method]", function(event, element) {
|
140
|
+
if (event.stopped) return;
|
141
|
+
handleMethod(element);
|
142
|
+
event.stop();
|
143
|
+
});
|
144
|
+
|
145
|
+
document.on("submit", function(event) {
|
146
|
+
var element = event.findElement(),
|
147
|
+
message = element.readAttribute('data-confirm');
|
148
|
+
if (message && !confirm(message)) {
|
149
|
+
event.stop();
|
150
|
+
return false;
|
151
|
+
}
|
152
|
+
|
153
|
+
var inputs = element.select("input[type=submit][data-disable-with]");
|
154
|
+
inputs.each(function(input) {
|
155
|
+
input.disabled = true;
|
156
|
+
input.writeAttribute('data-original-value', input.value);
|
157
|
+
input.value = input.readAttribute('data-disable-with');
|
158
|
+
});
|
159
|
+
|
160
|
+
var element = event.findElement("form[data-remote]");
|
161
|
+
if (element) {
|
162
|
+
handleRemote(element);
|
163
|
+
event.stop();
|
164
|
+
}
|
165
|
+
});
|
166
|
+
|
167
|
+
document.on("ajax:after", "form", function(event, element) {
|
168
|
+
var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
|
169
|
+
inputs.each(function(input) {
|
170
|
+
input.value = input.readAttribute('data-original-value');
|
171
|
+
input.removeAttribute('data-original-value');
|
172
|
+
input.disabled = false;
|
173
|
+
});
|
174
|
+
});
|
175
|
+
})();
|
File without changes
|
data/test/test_helper.rb
CHANGED
@@ -13,7 +13,7 @@ require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
|
13
13
|
require "rails/test_help"
|
14
14
|
require 'shoulda'
|
15
15
|
require 'ffaker'
|
16
|
-
require 'database_cleaner'
|
16
|
+
require 'database_cleaner'
|
17
17
|
|
18
18
|
Rails.backtrace_cleaner.remove_silencers!
|
19
19
|
|
@@ -24,7 +24,7 @@ ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__
|
|
24
24
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
25
25
|
|
26
26
|
# DatabaseCleaner is apparently necessary for doing proper transactions within MySQL (ugh)
|
27
|
-
DatabaseCleaner.strategy = :truncation
|
27
|
+
DatabaseCleaner.strategy = :truncation
|
28
28
|
|
29
29
|
# global setup block resetting Thread.current
|
30
30
|
class ActiveSupport::TestCase
|
data/test/unit/model_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'time_travel_helper'
|
2
3
|
|
3
4
|
class HasPaperTrailModelTest < ActiveSupport::TestCase
|
4
5
|
|
@@ -191,9 +192,8 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
191
192
|
assert @widget.live?
|
192
193
|
end
|
193
194
|
|
194
|
-
|
195
195
|
context 'which is then created' do
|
196
|
-
setup { @widget.update_attributes :name => 'Henry' }
|
196
|
+
setup { @widget.update_attributes :name => 'Henry', :created_at => Time.now - 1.day }
|
197
197
|
|
198
198
|
should 'have one previous version' do
|
199
199
|
assert_equal 1, @widget.versions.length
|
@@ -212,6 +212,10 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
212
212
|
assert @widget.live?
|
213
213
|
end
|
214
214
|
|
215
|
+
should 'use the widget created_at' do
|
216
|
+
assert_equal @widget.created_at.to_i, @widget.versions.first.created_at.to_i
|
217
|
+
end
|
218
|
+
|
215
219
|
should 'have changes' do
|
216
220
|
|
217
221
|
#TODO Postgres does not appear to pass back ActiveSupport::TimeWithZone,
|
@@ -301,13 +305,13 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
301
305
|
should 'not copy the has_one association by default when reifying' do
|
302
306
|
reified_widget = @widget.versions.last.reify
|
303
307
|
assert_equal @wotsit, reified_widget.wotsit # association hasn't been affected by reifying
|
304
|
-
assert_equal @wotsit, @widget.wotsit # confirm that the association is correct
|
308
|
+
assert_equal @wotsit, @widget.wotsit(true) # confirm that the association is correct
|
305
309
|
end
|
306
310
|
|
307
311
|
should 'copy the has_one association when reifying with :has_one => true' do
|
308
312
|
reified_widget = @widget.versions.last.reify(:has_one => true)
|
309
313
|
assert_nil reified_widget.wotsit # wotsit wasn't there at the last version
|
310
|
-
assert_equal @wotsit, @widget.wotsit # wotsit
|
314
|
+
assert_equal @wotsit, @widget.wotsit(true) # wotsit should still exist on live object
|
311
315
|
end
|
312
316
|
end
|
313
317
|
|
@@ -567,9 +571,9 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
567
571
|
|
568
572
|
should 'track who made the change' do
|
569
573
|
assert_equal 'Alice', @version.whodunnit
|
570
|
-
assert_nil @version.
|
574
|
+
assert_nil @version.originator
|
571
575
|
assert_equal 'Alice', @version.terminator
|
572
|
-
assert_equal 'Alice', @widget.
|
576
|
+
assert_equal 'Alice', @widget.originator
|
573
577
|
end
|
574
578
|
|
575
579
|
context 'when a record is updated' do
|
@@ -581,9 +585,9 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
581
585
|
|
582
586
|
should 'track who made the change' do
|
583
587
|
assert_equal 'Bob', @version.whodunnit
|
584
|
-
assert_equal 'Alice', @version.
|
588
|
+
assert_equal 'Alice', @version.originator
|
585
589
|
assert_equal 'Bob', @version.terminator
|
586
|
-
assert_equal 'Bob', @widget.
|
590
|
+
assert_equal 'Bob', @widget.originator
|
587
591
|
end
|
588
592
|
|
589
593
|
context 'when a record is destroyed' do
|
@@ -595,9 +599,9 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
595
599
|
|
596
600
|
should 'track who made the change' do
|
597
601
|
assert_equal 'Charlie', @version.whodunnit
|
598
|
-
assert_equal 'Bob', @version.
|
602
|
+
assert_equal 'Bob', @version.originator
|
599
603
|
assert_equal 'Charlie', @version.terminator
|
600
|
-
assert_equal 'Charlie', @widget.
|
604
|
+
assert_equal 'Charlie', @widget.originator
|
601
605
|
end
|
602
606
|
end
|
603
607
|
end
|
@@ -617,17 +621,15 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
617
621
|
end
|
618
622
|
|
619
623
|
# Currently the gem generates a bunch of deprecation warnings about serialized attributes on AR 4.2
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
@wotsit.update_attributes! :name => 'changed'
|
627
|
-
}
|
624
|
+
should 'not generate warning' do
|
625
|
+
# Tests that it doesn't try to write created_on as an attribute just because a created_on
|
626
|
+
# method exists.
|
627
|
+
warnings = capture(:stderr) { # Deprecation warning in Rails 3.2
|
628
|
+
assert_nothing_raised { # ActiveModel::MissingAttributeError in Rails 4
|
629
|
+
@wotsit.update_attributes! :name => 'changed'
|
628
630
|
}
|
629
|
-
|
630
|
-
|
631
|
+
}
|
632
|
+
assert_equal '', warnings
|
631
633
|
end
|
632
634
|
|
633
635
|
end
|
@@ -649,7 +651,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
649
651
|
should 'should return the correct originator' do
|
650
652
|
PaperTrail.whodunnit = 'Ben'
|
651
653
|
@foo.update_attribute(:name, 'Geoffrey')
|
652
|
-
assert_equal PaperTrail.whodunnit, @foo.
|
654
|
+
assert_equal PaperTrail.whodunnit, @foo.originator
|
653
655
|
end
|
654
656
|
|
655
657
|
context 'when destroyed' do
|
@@ -734,7 +736,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
734
736
|
should 'return versions in the time period' do
|
735
737
|
assert_equal ['Fidget'], @widget.versions_between(20.days.ago, 10.days.ago).map(&:name)
|
736
738
|
assert_equal ['Widget', 'Fidget'], @widget.versions_between(45.days.ago, 10.days.ago).map(&:name)
|
737
|
-
assert_equal ['Fidget', 'Digit'], @widget.versions_between(16.days.ago, 1.minute.ago).map(&:name)
|
739
|
+
assert_equal ['Fidget', 'Digit', 'Digit'], @widget.versions_between(16.days.ago, 1.minute.ago).map(&:name)
|
738
740
|
assert_equal [], @widget.versions_between(60.days.ago, 45.days.ago).map(&:name)
|
739
741
|
end
|
740
742
|
end
|
@@ -930,7 +932,7 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
930
932
|
count = PaperTrail::Version.count
|
931
933
|
@book.authors.create :name => 'Tolstoy'
|
932
934
|
assert_equal 2, PaperTrail::Version.count - count
|
933
|
-
assert_same_elements [Person.last, Authorship.last], [PaperTrail::Version.
|
935
|
+
assert_same_elements [Person.last, Authorship.last], [PaperTrail::Version.order(:id).to_a[-2].item, PaperTrail::Version.last.item]
|
934
936
|
end
|
935
937
|
|
936
938
|
should 'store version on join destroy' do
|
@@ -952,174 +954,100 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
952
954
|
end
|
953
955
|
end
|
954
956
|
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
context 'before the associated was created' do
|
960
|
-
setup do
|
961
|
-
@widget.update_attributes :name => 'widget_1'
|
962
|
-
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
963
|
-
end
|
964
|
-
|
965
|
-
context 'when reified' do
|
966
|
-
setup { @widget_0 = @widget.versions.last.reify(:has_one => 1) }
|
967
|
-
|
968
|
-
should 'see the associated as it was at the time' do
|
969
|
-
assert_nil @widget_0.wotsit
|
970
|
-
end
|
971
|
-
end
|
972
|
-
end
|
973
|
-
|
974
|
-
context 'where the association is created between model versions' do
|
975
|
-
setup do
|
976
|
-
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
977
|
-
make_last_version_earlier @wotsit
|
978
|
-
|
979
|
-
@widget.update_attributes :name => 'widget_1'
|
980
|
-
end
|
981
|
-
|
982
|
-
context 'when reified' do
|
983
|
-
setup { @widget_0 = @widget.versions.last.reify(:has_one => 1) }
|
984
|
-
|
985
|
-
should 'see the associated as it was at the time' do
|
986
|
-
assert_equal 'wotsit_0', @widget_0.wotsit.name
|
987
|
-
end
|
988
|
-
end
|
989
|
-
|
990
|
-
context 'and then the associated is updated between model versions' do
|
991
|
-
setup do
|
992
|
-
@wotsit.update_attributes :name => 'wotsit_1'
|
993
|
-
make_last_version_earlier @wotsit
|
994
|
-
@wotsit.update_attributes :name => 'wotsit_2'
|
995
|
-
make_last_version_earlier @wotsit
|
996
|
-
|
997
|
-
@widget.update_attributes :name => 'widget_2'
|
998
|
-
@wotsit.update_attributes :name => 'wotsit_3'
|
999
|
-
end
|
1000
|
-
|
1001
|
-
context 'when reified' do
|
1002
|
-
setup { @widget_1 = @widget.versions.last.reify(:has_one => 1) }
|
1003
|
-
|
1004
|
-
should 'see the associated as it was at the time' do
|
1005
|
-
assert_equal 'wotsit_2', @widget_1.wotsit.name
|
1006
|
-
end
|
1007
|
-
end
|
1008
|
-
|
1009
|
-
context 'when reified opting out of has_one reification' do
|
1010
|
-
setup { @widget_1 = @widget.versions.last.reify(:has_one => false) }
|
1011
|
-
|
1012
|
-
should 'see the associated as it is live' do
|
1013
|
-
assert_equal 'wotsit_3', @widget_1.wotsit.name
|
1014
|
-
end
|
1015
|
-
end
|
1016
|
-
end
|
1017
|
-
|
1018
|
-
context 'and then the associated is destroyed between model versions' do
|
1019
|
-
setup do
|
1020
|
-
@wotsit.destroy
|
1021
|
-
make_last_version_earlier @wotsit
|
1022
|
-
|
1023
|
-
@widget.update_attributes :name => 'widget_3'
|
1024
|
-
end
|
1025
|
-
|
1026
|
-
context 'when reified' do
|
1027
|
-
setup { @widget_2 = @widget.versions.last.reify(:has_one => 1) }
|
1028
|
-
|
1029
|
-
should 'see the associated as it was at the time' do
|
1030
|
-
assert_nil @widget_2.wotsit
|
1031
|
-
end
|
1032
|
-
end
|
1033
|
-
end
|
1034
|
-
end
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
context 'When an attribute has a custom serializer' do
|
1038
|
-
setup { @person = Person.new(:time_zone => "Samoa") }
|
1039
|
-
|
1040
|
-
should "be an instance of ActiveSupport::TimeZone" do
|
1041
|
-
assert_equal ActiveSupport::TimeZone, @person.time_zone.class
|
1042
|
-
end
|
1043
|
-
|
1044
|
-
context 'when the model is saved' do
|
957
|
+
# `serialized_attributes` is deprecated in ActiveRecord 5.0
|
958
|
+
if ::ActiveRecord::VERSION::MAJOR < 5
|
959
|
+
context 'When an attribute has a custom serializer' do
|
1045
960
|
setup do
|
1046
|
-
|
1047
|
-
@person.
|
961
|
+
PaperTrail.config.serialized_attributes = true
|
962
|
+
@person = Person.new(:time_zone => "Samoa")
|
1048
963
|
end
|
1049
964
|
|
1050
|
-
|
1051
|
-
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1052
|
-
assert @person.versions.last.object_changes.length < 105, "object_changes length was #{@person.versions.last.object_changes.length}"
|
1053
|
-
end
|
1054
|
-
# It should store the serialized value.
|
1055
|
-
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
|
1056
|
-
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
|
1057
|
-
assert_equal [nil, 'Samoa'], as_stored_in_version[:time_zone]
|
1058
|
-
serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
|
1059
|
-
assert_equal serialized_value, as_stored_in_version[:time_zone].last
|
1060
|
-
end
|
965
|
+
teardown { PaperTrail.config.serialized_attributes = false }
|
1061
966
|
|
1062
|
-
|
1063
|
-
|
1064
|
-
unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
|
1065
|
-
assert_equal unserialized_value, @person.versions.last.changeset[:time_zone].last
|
1066
|
-
end
|
1067
|
-
should "record.changes (before save) returns the original, unserialized values" do
|
1068
|
-
assert_equal [NilClass, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
|
1069
|
-
end
|
1070
|
-
should 'version.changeset should be the same as record.changes was before the save' do
|
1071
|
-
assert_equal @changes_before_save, @person.versions.last.changeset.delete_if { |key, val| key.to_sym == :id }
|
1072
|
-
assert_equal [NilClass, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
|
967
|
+
should "be an instance of ActiveSupport::TimeZone" do
|
968
|
+
assert_equal ActiveSupport::TimeZone, @person.time_zone.class
|
1073
969
|
end
|
1074
970
|
|
1075
|
-
context 'when
|
971
|
+
context 'when the model is saved' do
|
1076
972
|
setup do
|
1077
|
-
@attribute_value_before_change = @person.time_zone
|
1078
|
-
@person.assign_attributes({ :time_zone => 'Pacific Time (US & Canada)' })
|
1079
973
|
@changes_before_save = @person.changes.dup
|
1080
974
|
@person.save!
|
1081
975
|
end
|
1082
976
|
|
1083
|
-
#
|
1084
|
-
# Before the serialized attributes fix, the object/object_changes value that was stored was ridiculously long (58723).
|
1085
|
-
should 'version.object should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1086
|
-
assert @person.versions.last.object.length < 105, "object length was #{@person.versions.last.object.length}"
|
1087
|
-
end
|
1088
|
-
# Need an additional clause to detect what version of ActiveRecord is being used for this test because AR4 injects the `updated_at` column into the changeset for updates to models
|
977
|
+
# Test for serialization:
|
1089
978
|
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1090
|
-
assert @person.versions.last.object_changes.length <
|
1091
|
-
end
|
1092
|
-
# But now it stores the short, serialized value.
|
1093
|
-
should 'version.object attribute should have stored the value returned by the attribute serializer' do
|
1094
|
-
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object)]
|
1095
|
-
assert_equal 'Samoa', as_stored_in_version[:time_zone]
|
1096
|
-
serialized_value = Person::TimeZoneSerializer.dump(@attribute_value_before_change)
|
1097
|
-
assert_equal serialized_value, as_stored_in_version[:time_zone]
|
979
|
+
assert @person.versions.last.object_changes.length < 105, "object_changes length was #{@person.versions.last.object_changes.length}"
|
1098
980
|
end
|
981
|
+
# It should store the serialized value.
|
1099
982
|
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
|
1100
983
|
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
|
1101
|
-
assert_equal [
|
984
|
+
assert_equal [nil, 'Samoa'], as_stored_in_version[:time_zone]
|
1102
985
|
serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
|
1103
986
|
assert_equal serialized_value, as_stored_in_version[:time_zone].last
|
1104
987
|
end
|
1105
988
|
|
1106
989
|
# Tests for unserialization:
|
1107
|
-
should 'version.reify should convert the attribute value back to its original, unserialized value' do
|
1108
|
-
unserialized_value = Person::TimeZoneSerializer.load(@attribute_value_before_change)
|
1109
|
-
assert_equal unserialized_value, @person.versions.last.reify.time_zone
|
1110
|
-
end
|
1111
990
|
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
|
1112
991
|
unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
|
1113
992
|
assert_equal unserialized_value, @person.versions.last.changeset[:time_zone].last
|
1114
993
|
end
|
1115
994
|
should "record.changes (before save) returns the original, unserialized values" do
|
1116
|
-
assert_equal [
|
995
|
+
assert_equal [NilClass, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
|
1117
996
|
end
|
1118
997
|
should 'version.changeset should be the same as record.changes was before the save' do
|
1119
|
-
assert_equal @changes_before_save, @person.versions.last.changeset
|
1120
|
-
assert_equal [
|
998
|
+
assert_equal @changes_before_save, @person.versions.last.changeset.delete_if { |key, val| key.to_sym == :id }
|
999
|
+
assert_equal [NilClass, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
|
1121
1000
|
end
|
1122
1001
|
|
1002
|
+
context 'when that attribute is updated' do
|
1003
|
+
setup do
|
1004
|
+
@attribute_value_before_change = @person.time_zone
|
1005
|
+
@person.assign_attributes({ :time_zone => 'Pacific Time (US & Canada)' })
|
1006
|
+
@changes_before_save = @person.changes.dup
|
1007
|
+
@person.save!
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# Tests for serialization:
|
1011
|
+
# Before the serialized attributes fix, the object/object_changes value that was stored was ridiculously long (58723).
|
1012
|
+
should 'version.object should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1013
|
+
assert @person.versions.last.object.length < 105, "object length was #{@person.versions.last.object.length}"
|
1014
|
+
end
|
1015
|
+
# Need an additional clause to detect what version of ActiveRecord is being used for this test because AR4 injects the `updated_at` column into the changeset for updates to models
|
1016
|
+
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
|
1017
|
+
assert @person.versions.last.object_changes.length < (ActiveRecord::VERSION::MAJOR < 4 ? 105 : 118), "object_changes length was #{@person.versions.last.object_changes.length}"
|
1018
|
+
end
|
1019
|
+
# But now it stores the short, serialized value.
|
1020
|
+
should 'version.object attribute should have stored the value returned by the attribute serializer' do
|
1021
|
+
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object)]
|
1022
|
+
assert_equal 'Samoa', as_stored_in_version[:time_zone]
|
1023
|
+
serialized_value = Person::TimeZoneSerializer.dump(@attribute_value_before_change)
|
1024
|
+
assert_equal serialized_value, as_stored_in_version[:time_zone]
|
1025
|
+
end
|
1026
|
+
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
|
1027
|
+
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
|
1028
|
+
assert_equal ['Samoa', 'Pacific Time (US & Canada)'], as_stored_in_version[:time_zone]
|
1029
|
+
serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
|
1030
|
+
assert_equal serialized_value, as_stored_in_version[:time_zone].last
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
# Tests for unserialization:
|
1034
|
+
should 'version.reify should convert the attribute value back to its original, unserialized value' do
|
1035
|
+
unserialized_value = Person::TimeZoneSerializer.load(@attribute_value_before_change)
|
1036
|
+
assert_equal unserialized_value, @person.versions.last.reify.time_zone
|
1037
|
+
end
|
1038
|
+
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
|
1039
|
+
unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
|
1040
|
+
assert_equal unserialized_value, @person.versions.last.changeset[:time_zone].last
|
1041
|
+
end
|
1042
|
+
should "record.changes (before save) returns the original, unserialized values" do
|
1043
|
+
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
|
1044
|
+
end
|
1045
|
+
should 'version.changeset should be the same as record.changes was before the save' do
|
1046
|
+
assert_equal @changes_before_save, @person.versions.last.changeset
|
1047
|
+
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
end
|
1123
1051
|
end
|
1124
1052
|
end
|
1125
1053
|
end
|
@@ -1174,21 +1102,6 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
1174
1102
|
should 'return "overwritten" value on reified instance' do
|
1175
1103
|
assert_equal 4, @song.versions.last.reify.length
|
1176
1104
|
end
|
1177
|
-
|
1178
|
-
context 'Has a virtual attribute injected into the ActiveModel::Dirty changes' do
|
1179
|
-
setup do
|
1180
|
-
@song.name = 'Good Vibrations'
|
1181
|
-
@song.save
|
1182
|
-
@song.name = nil
|
1183
|
-
end
|
1184
|
-
|
1185
|
-
should 'return persist the changes on the live instance properly' do
|
1186
|
-
assert_equal nil, @song.name
|
1187
|
-
end
|
1188
|
-
should 'return "overwritten" virtual attribute on the reified instance' do
|
1189
|
-
assert_equal 'Good Vibrations', @song.versions.last.reify.name
|
1190
|
-
end
|
1191
|
-
end
|
1192
1105
|
end
|
1193
1106
|
|
1194
1107
|
|
@@ -1399,15 +1312,530 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase
|
|
1399
1312
|
assert_equal 3, @widget.versions.size
|
1400
1313
|
end
|
1401
1314
|
end
|
1315
|
+
end
|
1402
1316
|
|
1403
|
-
private
|
1404
1317
|
|
1405
|
-
|
1406
|
-
#
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
PaperTrail::Version.record_timestamps = true
|
1318
|
+
class HasPaperTrailModelTransactionalTest < ActiveSupport::TestCase
|
1319
|
+
# These would have been done in test_helper.rb if using_mysql? is true
|
1320
|
+
unless using_mysql?
|
1321
|
+
self.use_transactional_fixtures = false
|
1322
|
+
setup { DatabaseCleaner.start }
|
1411
1323
|
end
|
1412
1324
|
|
1413
|
-
|
1325
|
+
teardown do
|
1326
|
+
Timecop.return
|
1327
|
+
# This would have been done in test_helper.rb if using_mysql? is true
|
1328
|
+
DatabaseCleaner.clean unless using_mysql?
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
context 'A model with a has_one association' do
|
1332
|
+
setup { @widget = Widget.create :name => 'widget_0' }
|
1333
|
+
|
1334
|
+
context 'before the associated was created' do
|
1335
|
+
setup do
|
1336
|
+
@widget.update_attributes :name => 'widget_1'
|
1337
|
+
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
context 'when reified' do
|
1341
|
+
setup { @widget_0 = @widget.versions.last.reify(:has_one => true) }
|
1342
|
+
|
1343
|
+
should 'see the associated as it was at the time' do
|
1344
|
+
assert_nil @widget_0.wotsit
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
should 'not persist changes to the live association' do
|
1348
|
+
assert_equal @wotsit, @widget.wotsit(true)
|
1349
|
+
end
|
1350
|
+
end
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
context 'where the association is created between model versions' do
|
1354
|
+
setup do
|
1355
|
+
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
|
1356
|
+
Timecop.travel 1.second.since
|
1357
|
+
@widget.update_attributes :name => 'widget_1'
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
context 'when reified' do
|
1361
|
+
setup { @widget_0 = @widget.versions.last.reify(:has_one => true) }
|
1362
|
+
|
1363
|
+
should 'see the associated as it was at the time' do
|
1364
|
+
assert_equal 'wotsit_0', @widget_0.wotsit.name
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
should 'not persist changes to the live association' do
|
1368
|
+
assert_equal @wotsit, @widget.wotsit(true)
|
1369
|
+
end
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
context 'and then the associated is updated between model versions' do
|
1373
|
+
setup do
|
1374
|
+
@wotsit.update_attributes :name => 'wotsit_1'
|
1375
|
+
@wotsit.update_attributes :name => 'wotsit_2'
|
1376
|
+
Timecop.travel 1.second.since
|
1377
|
+
@widget.update_attributes :name => 'widget_2'
|
1378
|
+
@wotsit.update_attributes :name => 'wotsit_3'
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
context 'when reified' do
|
1382
|
+
setup { @widget_1 = @widget.versions.last.reify(:has_one => true) }
|
1383
|
+
|
1384
|
+
should 'see the associated as it was at the time' do
|
1385
|
+
assert_equal 'wotsit_2', @widget_1.wotsit.name
|
1386
|
+
end
|
1387
|
+
|
1388
|
+
should 'not persist changes to the live association' do
|
1389
|
+
assert_equal 'wotsit_3', @widget.wotsit(true).name
|
1390
|
+
end
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
context 'when reified opting out of has_one reification' do
|
1394
|
+
setup { @widget_1 = @widget.versions.last.reify(:has_one => false) }
|
1395
|
+
|
1396
|
+
should 'see the associated as it is live' do
|
1397
|
+
assert_equal 'wotsit_3', @widget_1.wotsit.name
|
1398
|
+
end
|
1399
|
+
end
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
context 'and then the associated is destroyed' do
|
1403
|
+
setup do
|
1404
|
+
@wotsit.destroy
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
context 'when reify' do
|
1408
|
+
setup { @widget_1 = @widget.versions.last.reify(:has_one => true) }
|
1409
|
+
|
1410
|
+
should 'see the associated as it was at the time' do
|
1411
|
+
assert_equal @wotsit, @widget_1.wotsit
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
should 'not persist changes to the live association' do
|
1415
|
+
assert_nil @widget.wotsit(true)
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
|
1419
|
+
context 'and then the model is updated' do
|
1420
|
+
setup do
|
1421
|
+
Timecop.travel 1.second.since
|
1422
|
+
@widget.update_attributes :name => 'widget_3'
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
context 'when reified' do
|
1426
|
+
setup { @widget_2 = @widget.versions.last.reify(:has_one => true) }
|
1427
|
+
|
1428
|
+
should 'see the associated as it was at the time' do
|
1429
|
+
assert_nil @widget_2.wotsit
|
1430
|
+
end
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
end
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
context 'A model with a has_many association' do
|
1438
|
+
setup { @customer = Customer.create :name => 'customer_0' }
|
1439
|
+
|
1440
|
+
context 'updated before the associated was created' do
|
1441
|
+
setup do
|
1442
|
+
@customer.update_attributes! :name => 'customer_1'
|
1443
|
+
@customer.orders.create! :order_date => Date.today
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
context 'when reified' do
|
1447
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
|
1448
|
+
|
1449
|
+
should 'see the associated as it was at the time' do
|
1450
|
+
assert_equal [], @customer_0.orders
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
should 'not persist changes to the live association' do
|
1454
|
+
assert_not_equal [], @customer.orders(true)
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
context 'when reified with option mark_for_destruction' do
|
1459
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1460
|
+
|
1461
|
+
should 'mark the associated for destruction' do
|
1462
|
+
assert_equal [true], @customer_0.orders.map(&:marked_for_destruction?)
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
context 'where the association is created between model versions' do
|
1468
|
+
setup do
|
1469
|
+
@order = @customer.orders.create! :order_date => 'order_date_0'
|
1470
|
+
Timecop.travel 1.second.since
|
1471
|
+
@customer.update_attributes :name => 'customer_1'
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
context 'when reified' do
|
1475
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
|
1476
|
+
|
1477
|
+
should 'see the associated as it was at the time' do
|
1478
|
+
assert_equal ['order_date_0'], @customer_0.orders.map(&:order_date)
|
1479
|
+
end
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
context 'and then a nested has_many association is created' do
|
1483
|
+
setup do
|
1484
|
+
@order.line_items.create! :product => 'product_0'
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
context 'when reified' do
|
1488
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
|
1489
|
+
|
1490
|
+
should 'see the live version of the nested association' do
|
1491
|
+
assert_equal ['product_0'], @customer_0.orders.first.line_items.map(&:product)
|
1492
|
+
end
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
context 'and then the associated is updated between model versions' do
|
1497
|
+
setup do
|
1498
|
+
@order.update_attributes :order_date => 'order_date_1'
|
1499
|
+
@order.update_attributes :order_date => 'order_date_2'
|
1500
|
+
Timecop.travel 1.second.since
|
1501
|
+
@customer.update_attributes :name => 'customer_2'
|
1502
|
+
@order.update_attributes :order_date => 'order_date_3'
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
context 'when reified' do
|
1506
|
+
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
|
1507
|
+
|
1508
|
+
should 'see the associated as it was at the time' do
|
1509
|
+
assert_equal ['order_date_2'], @customer_1.orders.map(&:order_date)
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
should 'not persist changes to the live association' do
|
1513
|
+
assert_equal ['order_date_3'], @customer.orders(true).map(&:order_date)
|
1514
|
+
end
|
1515
|
+
end
|
1516
|
+
|
1517
|
+
context 'when reified opting out of has_many reification' do
|
1518
|
+
setup { @customer_1 = @customer.versions.last.reify(:has_many => false) }
|
1519
|
+
|
1520
|
+
should 'see the associated as it is live' do
|
1521
|
+
assert_equal ['order_date_3'], @customer_1.orders.map(&:order_date)
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
context 'and then the associated is destroyed' do
|
1526
|
+
setup do
|
1527
|
+
@order.destroy
|
1528
|
+
end
|
1529
|
+
|
1530
|
+
context 'when reified' do
|
1531
|
+
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
|
1532
|
+
|
1533
|
+
should 'see the associated as it was at the time' do
|
1534
|
+
assert_equal ['order_date_2'], @customer_1.orders.map(&:order_date)
|
1535
|
+
end
|
1536
|
+
|
1537
|
+
should 'not persist changes to the live association' do
|
1538
|
+
assert_equal [], @customer.orders(true)
|
1539
|
+
end
|
1540
|
+
end
|
1541
|
+
end
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
context 'and then the associated is destroyed' do
|
1545
|
+
setup do
|
1546
|
+
@order.destroy
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
context 'when reified' do
|
1550
|
+
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
|
1551
|
+
|
1552
|
+
should 'see the associated as it was at the time' do
|
1553
|
+
assert_equal [@order.order_date], @customer_1.orders.map(&:order_date)
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
should 'not persist changes to the live association' do
|
1557
|
+
assert_equal [], @customer.orders(true)
|
1558
|
+
end
|
1559
|
+
end
|
1560
|
+
end
|
1561
|
+
|
1562
|
+
context 'and then the associated is destroyed between model versions' do
|
1563
|
+
setup do
|
1564
|
+
@order.destroy
|
1565
|
+
Timecop.travel 1.second.since
|
1566
|
+
@customer.update_attributes :name => 'customer_2'
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
context 'when reified' do
|
1570
|
+
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
|
1571
|
+
|
1572
|
+
should 'see the associated as it was at the time' do
|
1573
|
+
assert_equal [], @customer_1.orders
|
1574
|
+
end
|
1575
|
+
end
|
1576
|
+
end
|
1577
|
+
|
1578
|
+
context 'and then another association is added' do
|
1579
|
+
setup do
|
1580
|
+
@customer.orders.create! :order_date => 'order_date_1'
|
1581
|
+
end
|
1582
|
+
|
1583
|
+
context 'when reified' do
|
1584
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
|
1585
|
+
|
1586
|
+
should 'see the associated as it was at the time' do
|
1587
|
+
assert_equal ['order_date_0'], @customer_0.orders.map(&:order_date)
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
should 'not persist changes to the live association' do
|
1591
|
+
assert_equal ['order_date_0', 'order_date_1'], @customer.orders(true).map(&:order_date).sort
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
context 'when reified with option mark_for_destruction' do
|
1596
|
+
setup { @customer_0 = @customer.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1597
|
+
|
1598
|
+
should 'mark the newly associated for destruction' do
|
1599
|
+
assert @customer_0.orders.detect { |o| o.order_date == 'order_date_1'}.marked_for_destruction?
|
1600
|
+
end
|
1601
|
+
end
|
1602
|
+
end
|
1603
|
+
end
|
1604
|
+
end
|
1605
|
+
|
1606
|
+
context 'A model with a has_many through association' do
|
1607
|
+
setup { @book = Book.create :title => 'book_0' }
|
1608
|
+
|
1609
|
+
context 'updated before the associated was created' do
|
1610
|
+
setup do
|
1611
|
+
@book.update_attributes! :title => 'book_1'
|
1612
|
+
@book.authors.create! :name => 'author_0'
|
1613
|
+
end
|
1614
|
+
|
1615
|
+
context 'when reified' do
|
1616
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1617
|
+
|
1618
|
+
should 'see the associated as it was at the time' do
|
1619
|
+
assert_equal [], @book_0.authors
|
1620
|
+
end
|
1621
|
+
|
1622
|
+
should 'not persist changes to the live association' do
|
1623
|
+
assert_equal ['author_0'], @book.authors(true).map(&:name)
|
1624
|
+
end
|
1625
|
+
end
|
1626
|
+
|
1627
|
+
context 'when reified with option mark_for_destruction' do
|
1628
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1629
|
+
|
1630
|
+
should 'mark the associated for destruction' do
|
1631
|
+
assert_equal [true], @book_0.authors.map(&:marked_for_destruction?)
|
1632
|
+
end
|
1633
|
+
|
1634
|
+
should 'mark the associated-through for destruction' do
|
1635
|
+
assert_equal [true], @book_0.authorships.map(&:marked_for_destruction?)
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
end
|
1639
|
+
|
1640
|
+
context 'updated before it is associated with an existing one' do
|
1641
|
+
setup do
|
1642
|
+
person_existing = Person.create(:name => 'person_existing')
|
1643
|
+
Timecop.travel 1.second.since
|
1644
|
+
@book.update_attributes! :title => 'book_1'
|
1645
|
+
@book.authors << person_existing
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
context 'when reified' do
|
1649
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1650
|
+
|
1651
|
+
should 'see the associated as it was at the time' do
|
1652
|
+
assert_equal [], @book_0.authors
|
1653
|
+
end
|
1654
|
+
end
|
1655
|
+
|
1656
|
+
context 'when reified with option mark_for_destruction' do
|
1657
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1658
|
+
|
1659
|
+
should 'not mark the associated for destruction' do
|
1660
|
+
assert_equal [false], @book_0.authors.map(&:marked_for_destruction?)
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
should 'mark the associated-through for destruction' do
|
1664
|
+
assert_equal [true], @book_0.authorships.map(&:marked_for_destruction?)
|
1665
|
+
end
|
1666
|
+
end
|
1667
|
+
end
|
1668
|
+
|
1669
|
+
context 'where the association is created between model versions' do
|
1670
|
+
setup do
|
1671
|
+
@author = @book.authors.create! :name => 'author_0'
|
1672
|
+
@person_existing = Person.create(:name => 'person_existing')
|
1673
|
+
Timecop.travel 1.second.since
|
1674
|
+
@book.update_attributes! :title => 'book_1'
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
context 'when reified' do
|
1678
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1679
|
+
|
1680
|
+
should 'see the associated as it was at the time' do
|
1681
|
+
assert_equal ['author_0'], @book_0.authors.map(&:name)
|
1682
|
+
end
|
1683
|
+
end
|
1684
|
+
|
1685
|
+
context 'and then the associated is updated between model versions' do
|
1686
|
+
setup do
|
1687
|
+
@author.update_attributes :name => 'author_1'
|
1688
|
+
@author.update_attributes :name => 'author_2'
|
1689
|
+
Timecop.travel 1.second.since
|
1690
|
+
@book.update_attributes :title => 'book_2'
|
1691
|
+
@author.update_attributes :name => 'author_3'
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
context 'when reified' do
|
1695
|
+
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
|
1696
|
+
|
1697
|
+
should 'see the associated as it was at the time' do
|
1698
|
+
assert_equal ['author_2'], @book_1.authors.map(&:name)
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
should 'not persist changes to the live association' do
|
1702
|
+
assert_equal ['author_3'], @book.authors(true).map(&:name)
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
context 'when reified opting out of has_many reification' do
|
1707
|
+
setup { @book_1 = @book.versions.last.reify(:has_many => false) }
|
1708
|
+
|
1709
|
+
should 'see the associated as it is live' do
|
1710
|
+
assert_equal ['author_3'], @book_1.authors.map(&:name)
|
1711
|
+
end
|
1712
|
+
end
|
1713
|
+
end
|
1714
|
+
|
1715
|
+
context 'and then the associated is destroyed' do
|
1716
|
+
setup do
|
1717
|
+
@author.destroy
|
1718
|
+
end
|
1719
|
+
|
1720
|
+
context 'when reified' do
|
1721
|
+
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
|
1722
|
+
|
1723
|
+
should 'see the associated as it was at the time' do
|
1724
|
+
assert_equal [@author.name], @book_1.authors.map(&:name)
|
1725
|
+
end
|
1726
|
+
|
1727
|
+
should 'not persist changes to the live association' do
|
1728
|
+
assert_equal [], @book.authors(true)
|
1729
|
+
end
|
1730
|
+
end
|
1731
|
+
end
|
1732
|
+
|
1733
|
+
context 'and then the associated is destroyed between model versions' do
|
1734
|
+
setup do
|
1735
|
+
@author.destroy
|
1736
|
+
Timecop.travel 1.second.since
|
1737
|
+
@book.update_attributes :title => 'book_2'
|
1738
|
+
end
|
1739
|
+
|
1740
|
+
context 'when reified' do
|
1741
|
+
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
|
1742
|
+
|
1743
|
+
should 'see the associated as it was at the time' do
|
1744
|
+
assert_equal [], @book_1.authors
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
end
|
1748
|
+
|
1749
|
+
context 'and then the associated is dissociated between model versions' do
|
1750
|
+
setup do
|
1751
|
+
@book.authors = []
|
1752
|
+
Timecop.travel 1.second.since
|
1753
|
+
@book.update_attributes :title => 'book_2'
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
context 'when reified' do
|
1757
|
+
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
|
1758
|
+
|
1759
|
+
should 'see the associated as it was at the time' do
|
1760
|
+
assert_equal [], @book_1.authors
|
1761
|
+
end
|
1762
|
+
end
|
1763
|
+
end
|
1764
|
+
|
1765
|
+
context 'and then another associated is created' do
|
1766
|
+
setup do
|
1767
|
+
@book.authors.create! :name => 'author_1'
|
1768
|
+
end
|
1769
|
+
|
1770
|
+
context 'when reified' do
|
1771
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1772
|
+
|
1773
|
+
should 'only see the first associated' do
|
1774
|
+
assert_equal ['author_0'], @book_0.authors.map(&:name)
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
should 'not persist changes to the live association' do
|
1778
|
+
assert_equal ['author_0', 'author_1'], @book.authors(true).map(&:name)
|
1779
|
+
end
|
1780
|
+
end
|
1781
|
+
|
1782
|
+
context 'when reified with option mark_for_destruction' do
|
1783
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1784
|
+
|
1785
|
+
should 'mark the newly associated for destruction' do
|
1786
|
+
assert @book_0.authors.detect { |a| a.name == 'author_1' }.marked_for_destruction?
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
should 'mark the newly associated-through for destruction' do
|
1790
|
+
assert @book_0.authorships.detect { |as| as.person.name == 'author_1' }.marked_for_destruction?
|
1791
|
+
end
|
1792
|
+
end
|
1793
|
+
end
|
1794
|
+
|
1795
|
+
context 'and then an existing one is associated' do
|
1796
|
+
setup do
|
1797
|
+
@book.authors << @person_existing
|
1798
|
+
end
|
1799
|
+
|
1800
|
+
context 'when reified' do
|
1801
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1802
|
+
|
1803
|
+
should 'only see the first associated' do
|
1804
|
+
assert_equal ['author_0'], @book_0.authors.map(&:name)
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
should 'not persist changes to the live association' do
|
1808
|
+
assert_equal ['author_0', 'person_existing'], @book.authors(true).map(&:name).sort
|
1809
|
+
end
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
context 'when reified with option mark_for_destruction' do
|
1813
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
|
1814
|
+
|
1815
|
+
should 'not mark the newly associated for destruction' do
|
1816
|
+
assert !@book_0.authors.detect { |a| a.name == 'person_existing' }.marked_for_destruction?
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
should 'mark the newly associated-through for destruction' do
|
1820
|
+
assert @book_0.authorships.detect { |as| as.person.name == 'person_existing' }.marked_for_destruction?
|
1821
|
+
end
|
1822
|
+
end
|
1823
|
+
end
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
context 'updated before the associated without paper_trail was created' do
|
1827
|
+
setup do
|
1828
|
+
@book.update_attributes! :title => 'book_1'
|
1829
|
+
@book.editors.create! :name => 'editor_0'
|
1830
|
+
end
|
1831
|
+
|
1832
|
+
context 'when reified' do
|
1833
|
+
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
|
1834
|
+
|
1835
|
+
should 'see the live association' do
|
1836
|
+
assert_equal ['editor_0'], @book_0.editors.map(&:name)
|
1837
|
+
end
|
1838
|
+
end
|
1839
|
+
end
|
1840
|
+
end
|
1841
|
+
end
|