acts_as_dated_detail 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README.rdoc +39 -0
- data/lib/active_record/acts/dated.rb +70 -0
- data/lib/active_record/acts/dated_detail.rb +55 -0
- data/lib/acts_as_dated_detail.rb +5 -0
- data/test/dated_detail_test.rb +363 -0
- metadata +62 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2009 Paul Gillard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= acts_as_dated_detail
|
2
|
+
|
3
|
+
http://github.com/paulgillard/acts_as_dated_detail
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
acts_as_dated_detil enables versioning of attributes by timestamp.
|
8
|
+
|
9
|
+
== FEATURES:
|
10
|
+
|
11
|
+
== SYNOPSIS:
|
12
|
+
|
13
|
+
== REQUIREMENTS:
|
14
|
+
|
15
|
+
== INSTALL:
|
16
|
+
|
17
|
+
== LICENSE:
|
18
|
+
|
19
|
+
(The MIT License)
|
20
|
+
|
21
|
+
Copyright (c) 2009 Paul Gillard
|
22
|
+
|
23
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
24
|
+
of this software and associated documentation files (the "Software"), to deal
|
25
|
+
in the Software without restriction, including without limitation the rights
|
26
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
27
|
+
copies of the Software, and to permit persons to whom the Software is
|
28
|
+
furnished to do so, subject to the following conditions:
|
29
|
+
|
30
|
+
The above copyright notice and this permission notice shall be included in
|
31
|
+
all copies or substantial portions of the Software.
|
32
|
+
|
33
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
34
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
35
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
36
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
37
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
38
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
39
|
+
THE SOFTWARE.
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module Dated #:nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def acts_as_dated(options = {})
|
10
|
+
acts_as_dated_detail_class = "#{self.name}DatedDetail".constantize
|
11
|
+
|
12
|
+
tracked_attribute_reader_methods = ''
|
13
|
+
tracked_attribute_writer_methods = ''
|
14
|
+
acts_as_dated_detail_class.tracked_attributes.each do |attribute|
|
15
|
+
tracked_attribute_reader_methods << %(
|
16
|
+
def #{attribute}
|
17
|
+
dated_detail.send('#{attribute}')
|
18
|
+
end
|
19
|
+
)
|
20
|
+
tracked_attribute_writer_methods << %(
|
21
|
+
def #{attribute}=(value)
|
22
|
+
dated_detail.send('#{attribute}=', value)
|
23
|
+
end
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
class_eval <<-EOV
|
28
|
+
after_save :save_dated_detail
|
29
|
+
|
30
|
+
has_many :dated_details, :class_name => "#{acts_as_dated_detail_class.to_s}"
|
31
|
+
|
32
|
+
#{tracked_attribute_reader_methods}
|
33
|
+
#{tracked_attribute_writer_methods}
|
34
|
+
|
35
|
+
include ActiveRecord::Acts::Dated::InstanceMethods
|
36
|
+
|
37
|
+
# def self.columns
|
38
|
+
# tracked_columns_hash = #{acts_as_dated_detail_class.to_s}.columns_hash.slice(*#{acts_as_dated_detail_class.to_s}.tracked_attributes)
|
39
|
+
# @columns ||= tracked_columns_hash.inject(super.columns) do |columns, (key, value)|
|
40
|
+
# columns << value
|
41
|
+
# end
|
42
|
+
# @columns
|
43
|
+
# end
|
44
|
+
EOV
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module InstanceMethods
|
49
|
+
def on
|
50
|
+
@time ||= Time.now
|
51
|
+
end
|
52
|
+
|
53
|
+
def on=(time)
|
54
|
+
@time = time
|
55
|
+
@dated_detail = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def dated_detail
|
59
|
+
@dated_detail ||= dated_details.on(on).first || dated_details.build
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def save_dated_detail
|
65
|
+
dated_detail.save!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Acts #:nodoc:
|
3
|
+
module DatedDetail #:nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def acts_as_dated_detail()
|
10
|
+
class_eval <<-EOV
|
11
|
+
belongs_to :#{self.name.underscore.sub(/_dated_detail$/, '')}
|
12
|
+
|
13
|
+
before_update :split!
|
14
|
+
|
15
|
+
named_scope :on, lambda { |time| { :conditions => "\#{start_on_or_before_condition(time)} AND \#{end_on_or_after_condition(time)}" } }
|
16
|
+
|
17
|
+
def self.tracked_attributes
|
18
|
+
columns_hash.keys - ['id', "#{self.name.underscore.sub(/dated_detail$/, 'id')}", 'start_on', 'end_on', 'created_at', 'updated_at']
|
19
|
+
end
|
20
|
+
|
21
|
+
include ActiveRecord::Acts::DatedDetail::InstanceMethods
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Find all records starting on or before given time
|
26
|
+
def self.start_on_or_before_condition(time)
|
27
|
+
"(start_on <= '\#{time.to_s :db}')"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Find all records ending on or after given time
|
31
|
+
def self.end_on_or_after_condition(time)
|
32
|
+
"(end_on IS NULL OR end_on >= '\#{time.to_s :db}')"
|
33
|
+
end
|
34
|
+
EOV
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods
|
39
|
+
def initialize(*args)
|
40
|
+
super
|
41
|
+
self.start_on = Time.now
|
42
|
+
end
|
43
|
+
|
44
|
+
def split!
|
45
|
+
if changed?
|
46
|
+
self.start_on = Time.now
|
47
|
+
original = self.class.find(id).clone
|
48
|
+
original.end_on = start_on - 1
|
49
|
+
original.save!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,363 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'activerecord', '>= 1.15.4.7794'
|
5
|
+
require 'active_record'
|
6
|
+
require 'mocha'
|
7
|
+
require 'ruby-debug'
|
8
|
+
|
9
|
+
require "#{File.dirname(__FILE__)}/../lib/acts_as_dated_detail"
|
10
|
+
|
11
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
12
|
+
|
13
|
+
def setup_db
|
14
|
+
ActiveRecord::Schema.define(:version => 1) do
|
15
|
+
create_table :parrots do |t|
|
16
|
+
t.column :name, :string
|
17
|
+
t.column :created_at, :datetime
|
18
|
+
t.column :updated_at, :datetime
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table :pirates do |t|
|
22
|
+
t.column :name, :string
|
23
|
+
t.column :catchphrase, :string
|
24
|
+
t.column :ruthlessness, :integer
|
25
|
+
t.column :birth_date, :datetime
|
26
|
+
t.column :parrot_id, :integer
|
27
|
+
t.column :created_at, :datetime
|
28
|
+
t.column :updated_at, :datetime
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :pirate_dated_details do |t|
|
32
|
+
# Ensure any attributes added here are added to relevant tests (search for 'catchphrase')
|
33
|
+
t.column :pirate_id, :integer
|
34
|
+
t.column :start_on, :datetime
|
35
|
+
t.column :end_on, :datetime
|
36
|
+
t.column :catchphrase, :string
|
37
|
+
t.column :ruthlessness, :integer
|
38
|
+
t.column :birth_date, :datetime
|
39
|
+
t.column :parrot_id, :integer
|
40
|
+
t.column :created_at, :datetime
|
41
|
+
t.column :updated_at, :datetime
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown_db
|
47
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
48
|
+
ActiveRecord::Base.connection.drop_table(table)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
setup_db
|
53
|
+
|
54
|
+
class Parrot < ActiveRecord::Base
|
55
|
+
has_one :pirate
|
56
|
+
|
57
|
+
def ==(other)
|
58
|
+
name == other.name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class PirateDatedDetail < ActiveRecord::Base
|
63
|
+
acts_as_dated_detail
|
64
|
+
end
|
65
|
+
|
66
|
+
class Pirate < ActiveRecord::Base
|
67
|
+
belongs_to :parrot
|
68
|
+
|
69
|
+
acts_as_dated
|
70
|
+
end
|
71
|
+
|
72
|
+
teardown_db
|
73
|
+
|
74
|
+
class ActsAsDatedDetailTest < Test::Unit::TestCase
|
75
|
+
def setup
|
76
|
+
setup_db
|
77
|
+
end
|
78
|
+
|
79
|
+
def teardown
|
80
|
+
teardown_db
|
81
|
+
end
|
82
|
+
|
83
|
+
def default_test
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def create_parrot(time)
|
89
|
+
parrot = Parrot.create!(:name => time.to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_pirate(times = [Time.now])
|
93
|
+
first_time = times.shift
|
94
|
+
now = Time.now
|
95
|
+
Time.stubs(:now).returns(first_time)
|
96
|
+
pirate = Pirate.create!(:name => pirate_name(first_time), :catchphrase => catchphrase(first_time), :ruthlessness => ruthlessness(first_time), :birth_date => first_time, :parrot => create_parrot(first_time))
|
97
|
+
times.each do |time|
|
98
|
+
Time.stubs(:now).returns(time)
|
99
|
+
pirate.update_attributes(:name => pirate_name(time), :catchphrase => catchphrase(time), :ruthlessness => ruthlessness(time), :birth_date => time, :parrot => create_parrot(time))
|
100
|
+
end
|
101
|
+
Time.stubs(:now).returns(now)
|
102
|
+
pirate
|
103
|
+
end
|
104
|
+
|
105
|
+
def pirate_name(time)
|
106
|
+
time.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def catchphrase(time)
|
110
|
+
time.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
def ruthlessness(time)
|
114
|
+
time.to_i
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class ParentTest < ActsAsDatedDetailTest
|
119
|
+
# Creation
|
120
|
+
|
121
|
+
def test_related_dated_detail_created_along_with_model
|
122
|
+
pirate = Pirate.create!
|
123
|
+
assert_equal 1, pirate.dated_details.count
|
124
|
+
end
|
125
|
+
|
126
|
+
# Currently Effective Timestamp
|
127
|
+
|
128
|
+
def test_default_value_of_currently_effective_timestamp
|
129
|
+
now = Time.now
|
130
|
+
Time.stubs(:now).returns(now)
|
131
|
+
pirate = Pirate.create!
|
132
|
+
assert_equal Time.now, pirate.on
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_setting_value_of_currently_effective_timestamp
|
136
|
+
now = Time.now
|
137
|
+
Time.stubs(:now).returns(now)
|
138
|
+
pirate = Pirate.create!
|
139
|
+
one_month_ago = Time.now - 1.month
|
140
|
+
pirate.on = one_month_ago
|
141
|
+
assert_equal one_month_ago, pirate.on
|
142
|
+
end
|
143
|
+
|
144
|
+
# Tracked Attribute Retrieval
|
145
|
+
|
146
|
+
def test_tracked_attribute_for_oldest_timestamp_set_by_instance_method
|
147
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
148
|
+
pirate.on = 8.months.ago
|
149
|
+
assert_equal catchphrase(1.year.ago), pirate.catchphrase
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_tracked_attribute_for_middle_timestamp_set_by_instance_method
|
153
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
154
|
+
pirate.on = 5.months.ago
|
155
|
+
assert_equal catchphrase(6.months.ago), pirate.catchphrase
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_tracked_attribute_for_newest_timestamp_set_by_instance_method
|
159
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
160
|
+
pirate.on = 2.weeks.ago
|
161
|
+
assert_equal catchphrase(1.month.ago), pirate.catchphrase
|
162
|
+
end
|
163
|
+
|
164
|
+
# def test_start_on
|
165
|
+
#
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# def test_end_on
|
169
|
+
#
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# def test_end_on_for_last_dated_detail
|
173
|
+
#
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# def test_previous
|
177
|
+
#
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# def test_previous_for_first_dated_detail
|
181
|
+
#
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# def test_next
|
185
|
+
#
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
# def test_next_for_last_dated_detail
|
189
|
+
#
|
190
|
+
# end
|
191
|
+
|
192
|
+
# Tracked Attribute Methods
|
193
|
+
|
194
|
+
def test_read_tracked_attributes
|
195
|
+
# Ensure value from dated_detail is returned
|
196
|
+
pirate = Pirate.create!
|
197
|
+
dated_detail = pirate.dated_detail
|
198
|
+
pirate.stubs(:dated_detail).returns(dated_detail)
|
199
|
+
PirateDatedDetail.tracked_attributes.each do |attribute|
|
200
|
+
value = 10
|
201
|
+
PirateDatedDetail.any_instance.stubs(attribute).returns("Error")
|
202
|
+
dated_detail.stubs(attribute).returns(value)
|
203
|
+
assert_equal value, pirate.send(attribute)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_write_tracked_attributes
|
208
|
+
pirate = Pirate.create!
|
209
|
+
PirateDatedDetail.tracked_attributes.each do |attribute|
|
210
|
+
value = 10
|
211
|
+
pirate.send("#{attribute}=", value)
|
212
|
+
assert_equal value, pirate.dated_detail.send(attribute)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Updating Attributes
|
217
|
+
|
218
|
+
def test_updating_tracked_integer_attribute
|
219
|
+
now = Time.now
|
220
|
+
Time.stubs(:now).returns(now - 1.year)
|
221
|
+
pirate = Pirate.create!
|
222
|
+
Time.stubs(:now).returns(now)
|
223
|
+
pirate.update_attribute(:ruthlessness, 10)
|
224
|
+
assert_equal 2, pirate.dated_details.count
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_updating_tracked_string_attribute
|
228
|
+
now = Time.now
|
229
|
+
Time.stubs(:now).returns(now - 1.year)
|
230
|
+
pirate = Pirate.create!
|
231
|
+
Time.stubs(:now).returns(now)
|
232
|
+
pirate.update_attribute(:catchphrase, 'Yar!')
|
233
|
+
assert_equal 2, pirate.dated_details.count
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_updating_tracked_datetime_attribute
|
237
|
+
now = Time.now
|
238
|
+
Time.stubs(:now).returns(now - 1.year)
|
239
|
+
pirate = create_pirate
|
240
|
+
Time.stubs(:now).returns(now)
|
241
|
+
pirate.update_attributes(:birth_date => pirate.birth_date + 1.day)
|
242
|
+
assert_equal 2, pirate.dated_details.count
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_updating_tracked_multiparameter_attribute
|
246
|
+
now = Time.now
|
247
|
+
Time.stubs(:now).returns(now - 1.year)
|
248
|
+
pirate = create_pirate
|
249
|
+
Time.stubs(:now).returns(now)
|
250
|
+
new_birth_date = pirate.birth_date + 1.day
|
251
|
+
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
|
+
assert_equal 2, pirate.dated_details.count
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_updating_untracked_attribute
|
256
|
+
now = Time.now
|
257
|
+
Time.stubs(:now).returns(now - 1.year)
|
258
|
+
pirate = Pirate.create!
|
259
|
+
Time.stubs(:now).returns(now)
|
260
|
+
pirate.update_attribute(:name, 'Long John Silver')
|
261
|
+
assert_equal 1, pirate.dated_details.count
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
class DatedDetailTest < ActsAsDatedDetailTest
|
266
|
+
def setup
|
267
|
+
setup_db
|
268
|
+
# create_pirate_with_dated_detail
|
269
|
+
end
|
270
|
+
|
271
|
+
def teardown
|
272
|
+
teardown_db
|
273
|
+
end
|
274
|
+
|
275
|
+
# Associations
|
276
|
+
|
277
|
+
def test_belongs_to_parent
|
278
|
+
assert_equal create_pirate, PirateDatedDetail.first.pirate
|
279
|
+
end
|
280
|
+
|
281
|
+
# Creation
|
282
|
+
|
283
|
+
def test_initial_start_on_value
|
284
|
+
pirate = Pirate.create!
|
285
|
+
assert_equal Time.now.to_i, PirateDatedDetail.first.start_on.to_i
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_initial_end_on_value
|
289
|
+
pirate = Pirate.create!
|
290
|
+
assert_nil PirateDatedDetail.first.end_on
|
291
|
+
end
|
292
|
+
|
293
|
+
# Updating
|
294
|
+
|
295
|
+
def test_updating
|
296
|
+
now = Time.now
|
297
|
+
Time.stubs(:now).returns(now - 1.year)
|
298
|
+
pirate = Pirate.create!
|
299
|
+
Time.stubs(:now).returns(now)
|
300
|
+
|
301
|
+
dated_detail = pirate.dated_detail
|
302
|
+
original_dated_detail = dated_detail.class.find(dated_detail.id) # Cloning would keep millisecond parts of time which would make later comparisons harder
|
303
|
+
|
304
|
+
dated_detail.update_attribute(:ruthlessness, 10)
|
305
|
+
|
306
|
+
assert_equal now.to_i, dated_detail.start_on.to_i
|
307
|
+
assert_nil dated_detail.end_on
|
308
|
+
|
309
|
+
previous_dated_detail = PirateDatedDetail.find_by_start_on(original_dated_detail.start_on)
|
310
|
+
assert_equal dated_detail.start_on.to_i - 1, previous_dated_detail.end_on.to_i
|
311
|
+
assert_equal original_dated_detail.attributes.except('end_on', 'id'), previous_dated_detail.attributes.except('end_on', 'id')
|
312
|
+
end
|
313
|
+
|
314
|
+
# Tracked Attributes
|
315
|
+
|
316
|
+
def test_tracked_attributes_includes_relevant_columns
|
317
|
+
assert PirateDatedDetail.tracked_attributes.include?('catchphrase')
|
318
|
+
assert PirateDatedDetail.tracked_attributes.include?('ruthlessness')
|
319
|
+
assert PirateDatedDetail.tracked_attributes.include?('birth_date')
|
320
|
+
assert PirateDatedDetail.tracked_attributes.include?('parrot_id')
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_tracked_attributes_excludes_id
|
324
|
+
assert !PirateDatedDetail.tracked_attributes.include?('id')
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_tracked_attributes_excludes_start_on
|
328
|
+
assert !PirateDatedDetail.tracked_attributes.include?('start_on')
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_tracked_attributes_excludes_end_on
|
332
|
+
assert !PirateDatedDetail.tracked_attributes.include?('end_on')
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_tracked_attributes_excludes_updated_at
|
336
|
+
assert !PirateDatedDetail.tracked_attributes.include?('updated_at')
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_tracked_attributes_excludes_created_at
|
340
|
+
assert !PirateDatedDetail.tracked_attributes.include?('created_at')
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_tracked_attributes_excludes_parent_id
|
344
|
+
assert !PirateDatedDetail.tracked_attributes.include?('pirate_id')
|
345
|
+
end
|
346
|
+
|
347
|
+
# Dated Detail Retrieval
|
348
|
+
|
349
|
+
def test_dated_detail_for_oldest_timestamp_retrieved_via_named_scope
|
350
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
351
|
+
assert_equal catchphrase(1.year.ago), pirate.dated_details.on(8.months.ago).first.catchphrase
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_tracked_attribute_for_middle_timestamp_retrieved_via_named_scope
|
355
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
356
|
+
assert_equal catchphrase(6.months.ago), pirate.dated_details.on(5.months.ago).first.catchphrase
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_tracked_attribute_for_newest_timestamp_retrieved_via_named_scope
|
360
|
+
pirate = create_pirate([1.year.ago, 6.months.ago, 1.month.ago])
|
361
|
+
assert_equal catchphrase(1.month.ago), pirate.dated_details.on(2.weeks.ago).first.catchphrase
|
362
|
+
end
|
363
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_dated_detail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Gillard
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-02 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: paulmgillard@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- LICENSE
|
25
|
+
files:
|
26
|
+
- LICENSE
|
27
|
+
- README.rdoc
|
28
|
+
- lib/acts_as_dated_detail.rb
|
29
|
+
- lib/active_record/acts/dated.rb
|
30
|
+
- lib/active_record/acts/dated_detail.rb
|
31
|
+
- test/dated_detail_test.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/paulgillard/acts_as_dated_detail
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README.rdoc
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.3.5
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: acts_as_dated_detail enables versioning of attributes by timestamp
|
61
|
+
test_files: []
|
62
|
+
|