acts_as_dated_detail 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/active_record/acts/dated.rb +25 -0
- data/lib/active_record/acts/dated_detail.rb +3 -3
- data/test/dated_detail_test.rb +84 -64
- metadata +2 -2
@@ -19,6 +19,7 @@ module ActiveRecord
|
|
19
19
|
)
|
20
20
|
tracked_attribute_writer_methods << %(
|
21
21
|
def #{attribute}=(value)
|
22
|
+
write_attribute('#{attribute}', value)
|
22
23
|
dated_detail.send('#{attribute}=', value)
|
23
24
|
end
|
24
25
|
)
|
@@ -34,6 +35,8 @@ module ActiveRecord
|
|
34
35
|
|
35
36
|
include ActiveRecord::Acts::Dated::InstanceMethods
|
36
37
|
|
38
|
+
alias_method_chain :reload, :dated_detail
|
39
|
+
|
37
40
|
# def self.columns
|
38
41
|
# tracked_columns_hash = #{acts_as_dated_detail_class.to_s}.columns_hash.slice(*#{acts_as_dated_detail_class.to_s}.tracked_attributes)
|
39
42
|
# @columns ||= tracked_columns_hash.inject(super.columns) do |columns, (key, value)|
|
@@ -46,12 +49,15 @@ module ActiveRecord
|
|
46
49
|
end
|
47
50
|
|
48
51
|
module InstanceMethods
|
52
|
+
|
53
|
+
|
49
54
|
def on
|
50
55
|
@time ||= Time.now
|
51
56
|
end
|
52
57
|
|
53
58
|
def on=(time)
|
54
59
|
@time = time
|
60
|
+
@fixed_time = true
|
55
61
|
@dated_detail = nil
|
56
62
|
end
|
57
63
|
|
@@ -59,10 +65,29 @@ module ActiveRecord
|
|
59
65
|
@dated_detail ||= dated_details.on(on).first || dated_details.build
|
60
66
|
end
|
61
67
|
|
68
|
+
def current?
|
69
|
+
!@fixed_time
|
70
|
+
end
|
71
|
+
|
72
|
+
def current!
|
73
|
+
@fixed_time = false
|
74
|
+
@dated_detail = nil
|
75
|
+
@time = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def reload_with_dated_detail
|
79
|
+
if current?
|
80
|
+
@dated_detail = nil
|
81
|
+
@time = nil
|
82
|
+
end
|
83
|
+
reload_without_dated_detail
|
84
|
+
end
|
85
|
+
|
62
86
|
private
|
63
87
|
|
64
88
|
def save_dated_detail
|
65
89
|
dated_detail.save!
|
90
|
+
@time = dated_detail.start_on
|
66
91
|
end
|
67
92
|
end
|
68
93
|
end
|
@@ -11,6 +11,7 @@ module ActiveRecord
|
|
11
11
|
belongs_to :#{self.name.underscore.sub(/_dated_detail$/, '')}
|
12
12
|
|
13
13
|
before_update :split!
|
14
|
+
before_save :set_start_date
|
14
15
|
|
15
16
|
named_scope :on, lambda { |time| { :conditions => "\#{start_on_or_before_condition(time)} AND \#{end_on_or_after_condition(time)}" } }
|
16
17
|
|
@@ -36,9 +37,8 @@ module ActiveRecord
|
|
36
37
|
end
|
37
38
|
|
38
39
|
module InstanceMethods
|
39
|
-
def
|
40
|
-
|
41
|
-
self.start_on = Time.now
|
40
|
+
def set_start_date
|
41
|
+
self.start_on ||= Time.now
|
42
42
|
end
|
43
43
|
|
44
44
|
def split!
|
data/test/dated_detail_test.rb
CHANGED
@@ -98,6 +98,7 @@ class ActsAsDatedDetailTest < Test::Unit::TestCase
|
|
98
98
|
Time.stubs(:now).returns(time)
|
99
99
|
pirate.update_attributes(:name => pirate_name(time), :catchphrase => catchphrase(time), :ruthlessness => ruthlessness(time), :birth_date => time, :parrot => create_parrot(time))
|
100
100
|
end
|
101
|
+
# A lot of tests rely on this method stubbing Time.now
|
101
102
|
Time.stubs(:now).returns(now)
|
102
103
|
pirate
|
103
104
|
end
|
@@ -119,7 +120,7 @@ class ParentTest < ActsAsDatedDetailTest
|
|
119
120
|
# Creation
|
120
121
|
|
121
122
|
def test_related_dated_detail_created_along_with_model
|
122
|
-
pirate =
|
123
|
+
pirate = create_pirate
|
123
124
|
assert_equal 1, pirate.dated_details.count
|
124
125
|
end
|
125
126
|
|
@@ -127,20 +128,49 @@ class ParentTest < ActsAsDatedDetailTest
|
|
127
128
|
|
128
129
|
def test_default_value_of_currently_effective_timestamp
|
129
130
|
now = Time.now
|
130
|
-
|
131
|
-
|
132
|
-
assert_equal Time.now, pirate.on
|
131
|
+
pirate = create_pirate([now])
|
132
|
+
assert_equal now, pirate.on
|
133
133
|
end
|
134
134
|
|
135
135
|
def test_setting_value_of_currently_effective_timestamp
|
136
|
-
|
137
|
-
Time.stubs(:now).returns(now)
|
138
|
-
pirate = Pirate.create!
|
136
|
+
pirate = create_pirate
|
139
137
|
one_month_ago = Time.now - 1.month
|
140
138
|
pirate.on = one_month_ago
|
141
139
|
assert_equal one_month_ago, pirate.on
|
142
140
|
end
|
143
141
|
|
142
|
+
# Determining if instance is tracking latest history
|
143
|
+
|
144
|
+
def test_tracks_latest_history_by_default
|
145
|
+
pirate = create_pirate
|
146
|
+
assert pirate.current?
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_does_not_track_latest_history_when_currently_effective_timestamp_set
|
150
|
+
[1.day.ago, Time.now].each do |time|
|
151
|
+
pirate = create_pirate
|
152
|
+
pirate.on = time
|
153
|
+
assert !pirate.current?
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_does_not_track_latest_history_when_currently_effective_timestamp_reset_to_now
|
158
|
+
pirate = create_pirate
|
159
|
+
pirate.on = 1.day.ago
|
160
|
+
pirate.on = Time.now
|
161
|
+
assert !pirate.current?
|
162
|
+
end
|
163
|
+
|
164
|
+
# Forcing instance to track latest history again
|
165
|
+
|
166
|
+
def test_tracks_latest_history_again
|
167
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
168
|
+
pirate.on = 6.months.ago
|
169
|
+
pirate.current!
|
170
|
+
assert pirate.current?
|
171
|
+
assert_equal catchphrase(1.month.ago), pirate.catchphrase
|
172
|
+
end
|
173
|
+
|
144
174
|
# Tracked Attribute Retrieval
|
145
175
|
|
146
176
|
def test_tracked_attribute_for_oldest_timestamp_set_by_instance_method
|
@@ -160,40 +190,41 @@ class ParentTest < ActsAsDatedDetailTest
|
|
160
190
|
pirate.on = 2.weeks.ago
|
161
191
|
assert_equal catchphrase(1.month.ago), pirate.catchphrase
|
162
192
|
end
|
163
|
-
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
193
|
+
|
194
|
+
# Reloading
|
195
|
+
|
196
|
+
def test_reloading_when_tracking_latest_history
|
197
|
+
pirate = create_pirate([1.hour.ago])
|
198
|
+
same_pirate = Pirate.find(pirate.id)
|
199
|
+
new_ruthlessness = 999 #same_pirate.ruthlessness + 1
|
200
|
+
same_pirate.update_attribute(:ruthlessness, new_ruthlessness)
|
201
|
+
flunk 'Pirate must be tracking latest history' unless pirate.current?
|
202
|
+
pirate.reload
|
203
|
+
assert_equal new_ruthlessness, pirate.ruthlessness
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_reloading_when_not_tracking_latest_history
|
207
|
+
pirate = create_pirate([6.months.ago, 1.hour.ago])
|
208
|
+
pirate.on = 5.months.ago
|
209
|
+
ruthlessness = pirate.ruthlessness
|
210
|
+
same_pirate = Pirate.find(pirate.id)
|
211
|
+
same_pirate.update_attribute(:ruthlessness, same_pirate.ruthlessness + 1)
|
212
|
+
flunk 'Pirate must not be tracking latest history' if pirate.current?
|
213
|
+
pirate.reload
|
214
|
+
assert_equal ruthlessness, pirate.ruthlessness
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_reloading_returns_self
|
218
|
+
# #reload is aliased. Test that it returns correctly
|
219
|
+
pirate = create_pirate
|
220
|
+
assert_equal pirate, pirate.reload
|
221
|
+
end
|
191
222
|
|
192
223
|
# Tracked Attribute Methods
|
193
224
|
|
194
225
|
def test_read_tracked_attributes
|
195
226
|
# Ensure value from dated_detail is returned
|
196
|
-
pirate =
|
227
|
+
pirate = create_pirate
|
197
228
|
dated_detail = pirate.dated_detail
|
198
229
|
pirate.stubs(:dated_detail).returns(dated_detail)
|
199
230
|
PirateDatedDetail.tracked_attributes.each do |attribute|
|
@@ -205,58 +236,50 @@ class ParentTest < ActsAsDatedDetailTest
|
|
205
236
|
end
|
206
237
|
|
207
238
|
def test_write_tracked_attributes
|
208
|
-
pirate =
|
239
|
+
pirate = create_pirate
|
209
240
|
PirateDatedDetail.tracked_attributes.each do |attribute|
|
210
241
|
value = 10
|
211
242
|
pirate.send("#{attribute}=", value)
|
243
|
+
assert_equal value, pirate.attributes[attribute]
|
212
244
|
assert_equal value, pirate.dated_detail.send(attribute)
|
213
245
|
end
|
214
246
|
end
|
215
247
|
|
216
248
|
# Updating Attributes
|
217
249
|
|
250
|
+
def test_updating_tracked_attribute_updates_currently_effective_timestamp
|
251
|
+
pirate = create_pirate([1.year.ago])
|
252
|
+
pirate.update_attribute(:catchphrase, 'Yar!')
|
253
|
+
assert_equal Time.now, pirate.on
|
254
|
+
end
|
255
|
+
|
218
256
|
def test_updating_tracked_integer_attribute
|
219
|
-
|
220
|
-
Time.stubs(:now).returns(now - 1.year)
|
221
|
-
pirate = Pirate.create!
|
222
|
-
Time.stubs(:now).returns(now)
|
257
|
+
pirate = create_pirate([1.year.ago])
|
223
258
|
pirate.update_attribute(:ruthlessness, 10)
|
224
259
|
assert_equal 2, pirate.dated_details.count
|
225
260
|
end
|
226
261
|
|
227
262
|
def test_updating_tracked_string_attribute
|
228
|
-
|
229
|
-
Time.stubs(:now).returns(now - 1.year)
|
230
|
-
pirate = Pirate.create!
|
231
|
-
Time.stubs(:now).returns(now)
|
263
|
+
pirate = create_pirate([1.year.ago])
|
232
264
|
pirate.update_attribute(:catchphrase, 'Yar!')
|
233
265
|
assert_equal 2, pirate.dated_details.count
|
234
266
|
end
|
235
267
|
|
236
268
|
def test_updating_tracked_datetime_attribute
|
237
|
-
|
238
|
-
Time.stubs(:now).returns(now - 1.year)
|
239
|
-
pirate = create_pirate
|
240
|
-
Time.stubs(:now).returns(now)
|
269
|
+
pirate = create_pirate([1.year.ago])
|
241
270
|
pirate.update_attributes(:birth_date => pirate.birth_date + 1.day)
|
242
271
|
assert_equal 2, pirate.dated_details.count
|
243
272
|
end
|
244
273
|
|
245
274
|
def test_updating_tracked_multiparameter_attribute
|
246
|
-
|
247
|
-
Time.stubs(:now).returns(now - 1.year)
|
248
|
-
pirate = create_pirate
|
249
|
-
Time.stubs(:now).returns(now)
|
275
|
+
pirate = create_pirate([1.year.ago])
|
250
276
|
new_birth_date = pirate.birth_date + 1.day
|
251
277
|
pirate.update_attributes('birth_date(1i)' => "#{new_birth_date.year}", 'birth_date(2i)' => "#{new_birth_date.month}", 'birth_date(3i)' => "#{new_birth_date.day}")
|
252
278
|
assert_equal 2, pirate.dated_details.count
|
253
279
|
end
|
254
280
|
|
255
281
|
def test_updating_untracked_attribute
|
256
|
-
|
257
|
-
Time.stubs(:now).returns(now - 1.year)
|
258
|
-
pirate = Pirate.create!
|
259
|
-
Time.stubs(:now).returns(now)
|
282
|
+
pirate = create_pirate([1.year.ago])
|
260
283
|
pirate.update_attribute(:name, 'Long John Silver')
|
261
284
|
assert_equal 1, pirate.dated_details.count
|
262
285
|
end
|
@@ -281,29 +304,26 @@ class DatedDetailTest < ActsAsDatedDetailTest
|
|
281
304
|
# Creation
|
282
305
|
|
283
306
|
def test_initial_start_on_value
|
284
|
-
pirate =
|
307
|
+
pirate = create_pirate
|
285
308
|
assert_equal Time.now.to_i, PirateDatedDetail.first.start_on.to_i
|
286
309
|
end
|
287
310
|
|
288
311
|
def test_initial_end_on_value
|
289
|
-
pirate =
|
312
|
+
pirate = create_pirate
|
290
313
|
assert_nil PirateDatedDetail.first.end_on
|
291
314
|
end
|
292
315
|
|
293
316
|
# Updating
|
294
317
|
|
295
318
|
def test_updating
|
296
|
-
|
297
|
-
Time.stubs(:now).returns(now - 1.year)
|
298
|
-
pirate = Pirate.create!
|
299
|
-
Time.stubs(:now).returns(now)
|
319
|
+
pirate = create_pirate([Time.now - 1.year])
|
300
320
|
|
301
321
|
dated_detail = pirate.dated_detail
|
302
322
|
original_dated_detail = dated_detail.class.find(dated_detail.id) # Cloning would keep millisecond parts of time which would make later comparisons harder
|
303
323
|
|
304
324
|
dated_detail.update_attribute(:ruthlessness, 10)
|
305
325
|
|
306
|
-
assert_equal now.to_i, dated_detail.start_on.to_i
|
326
|
+
assert_equal Time.now.to_i, dated_detail.start_on.to_i
|
307
327
|
assert_nil dated_detail.end_on
|
308
328
|
|
309
329
|
previous_dated_detail = PirateDatedDetail.find_by_start_on(original_dated_detail.start_on)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_dated_detail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Gillard
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-05 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|