chrono_model 0.4.0 → 0.5.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +64 -35
- data/README.sql +73 -27
- data/lib/chrono_model/adapter.rb +235 -44
- data/lib/chrono_model/patches.rb +28 -75
- data/lib/chrono_model/railtie.rb +0 -25
- data/lib/chrono_model/time_gate.rb +36 -0
- data/lib/chrono_model/time_machine.rb +345 -122
- data/lib/chrono_model/utils.rb +89 -0
- data/lib/chrono_model/version.rb +1 -1
- data/lib/chrono_model.rb +2 -7
- data/spec/adapter_spec.rb +74 -7
- data/spec/support/connection.rb +1 -1
- data/spec/support/helpers.rb +20 -1
- data/spec/time_machine_spec.rb +216 -12
- metadata +11 -9
data/lib/chrono_model.rb
CHANGED
@@ -3,6 +3,8 @@ require 'chrono_model/adapter'
|
|
3
3
|
require 'chrono_model/compatibility'
|
4
4
|
require 'chrono_model/patches'
|
5
5
|
require 'chrono_model/time_machine'
|
6
|
+
require 'chrono_model/time_gate'
|
7
|
+
require 'chrono_model/utils'
|
6
8
|
|
7
9
|
module ChronoModel
|
8
10
|
class Error < ActiveRecord::ActiveRecordError #:nodoc:
|
@@ -24,11 +26,4 @@ silence_warnings do
|
|
24
26
|
# We need to override the "scoped" method on AR::Association for temporal
|
25
27
|
# associations to work as well
|
26
28
|
ActiveRecord::Associations::Association = ChronoModel::Patches::Association
|
27
|
-
|
28
|
-
# This implements correct WITH syntax on PostgreSQL
|
29
|
-
Arel::Visitors::PostgreSQL = ChronoModel::Patches::Visitor
|
30
|
-
|
31
|
-
# This adds .with support to ActiveRecord::Relation
|
32
|
-
ActiveRecord::Relation.instance_eval { include ChronoModel::Patches::QueryMethods }
|
33
|
-
ActiveRecord::Base.extend ChronoModel::Patches::Querying
|
34
29
|
end
|
data/spec/adapter_spec.rb
CHANGED
@@ -59,7 +59,7 @@ describe ChronoModel::Adapter do
|
|
59
59
|
|
60
60
|
def native.to_proc
|
61
61
|
proc {|t|
|
62
|
-
t.string :test
|
62
|
+
t.string :test, :null => false
|
63
63
|
t.integer :foo
|
64
64
|
t.float :bar
|
65
65
|
t.text :baz
|
@@ -325,8 +325,8 @@ describe ChronoModel::Adapter do
|
|
325
325
|
it { should include(['id', 'integer']) }
|
326
326
|
end
|
327
327
|
|
328
|
-
with_temporal_table
|
329
|
-
with_plain_table
|
328
|
+
with_temporal_table(&assert)
|
329
|
+
with_plain_table( &assert)
|
330
330
|
end
|
331
331
|
|
332
332
|
describe '.primary_key' do
|
@@ -336,8 +336,8 @@ describe ChronoModel::Adapter do
|
|
336
336
|
it { should == 'id' }
|
337
337
|
end
|
338
338
|
|
339
|
-
with_temporal_table
|
340
|
-
with_plain_table
|
339
|
+
with_temporal_table(&assert)
|
340
|
+
with_plain_table( &assert)
|
341
341
|
end
|
342
342
|
|
343
343
|
describe '.indexes' do
|
@@ -353,8 +353,8 @@ describe ChronoModel::Adapter do
|
|
353
353
|
it { subject.map(&:columns).should =~ [['foo'], ['bar', 'baz']] }
|
354
354
|
end
|
355
355
|
|
356
|
-
with_temporal_table
|
357
|
-
with_plain_table
|
356
|
+
with_temporal_table(&assert)
|
357
|
+
with_plain_table( &assert)
|
358
358
|
end
|
359
359
|
|
360
360
|
describe '.on_schema' do
|
@@ -395,4 +395,71 @@ describe ChronoModel::Adapter do
|
|
395
395
|
end
|
396
396
|
end
|
397
397
|
|
398
|
+
|
399
|
+
context 'INSERT multiple values' do
|
400
|
+
before :all do
|
401
|
+
adapter.create_table table, :temporal => true, &columns
|
402
|
+
end
|
403
|
+
|
404
|
+
after :all do
|
405
|
+
adapter.drop_table table
|
406
|
+
end
|
407
|
+
|
408
|
+
let(:current) { [ChronoModel::Adapter::TEMPORAL_SCHEMA, table].join('.') }
|
409
|
+
let(:history) { [ChronoModel::Adapter::HISTORY_SCHEMA, table].join('.') }
|
410
|
+
|
411
|
+
def count(table)
|
412
|
+
adapter.select_value("SELECT COUNT(*) FROM ONLY #{table}").to_i
|
413
|
+
end
|
414
|
+
|
415
|
+
def ids(table)
|
416
|
+
adapter.select_values("SELECT id FROM ONLY #{table} ORDER BY id")
|
417
|
+
end
|
418
|
+
|
419
|
+
context 'when succeeding' do
|
420
|
+
def insert
|
421
|
+
adapter.execute <<-SQL
|
422
|
+
INSERT INTO #{table} (test, foo) VALUES
|
423
|
+
('test1', 1),
|
424
|
+
('test2', 2);
|
425
|
+
SQL
|
426
|
+
end
|
427
|
+
|
428
|
+
it { expect { insert }.to_not raise_error }
|
429
|
+
it { count(current).should == 2 }
|
430
|
+
it { count(history).should == 2 }
|
431
|
+
end
|
432
|
+
|
433
|
+
context 'when failing' do
|
434
|
+
def insert
|
435
|
+
adapter.execute <<-SQL
|
436
|
+
INSERT INTO #{table} (test, foo) VALUES
|
437
|
+
('test3', 3),
|
438
|
+
(NULL, 0);
|
439
|
+
SQL
|
440
|
+
end
|
441
|
+
|
442
|
+
it { expect { insert }.to raise_error }
|
443
|
+
it { count(current).should == 2 } # Because the previous
|
444
|
+
it { count(history).should == 2 } # records are preserved
|
445
|
+
end
|
446
|
+
|
447
|
+
context 'after a failure' do
|
448
|
+
def insert
|
449
|
+
adapter.execute <<-SQL
|
450
|
+
INSERT INTO #{table} (test, foo) VALUES
|
451
|
+
('test4', 3),
|
452
|
+
('test5', 4);
|
453
|
+
SQL
|
454
|
+
end
|
455
|
+
|
456
|
+
it { expect { insert }.to_not raise_error }
|
457
|
+
|
458
|
+
it { count(current).should == 4 }
|
459
|
+
it { count(history).should == 4 }
|
460
|
+
|
461
|
+
it { ids(current).should == ids(history) }
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
398
465
|
end
|
data/spec/support/connection.rb
CHANGED
@@ -6,7 +6,7 @@ module ChronoTest
|
|
6
6
|
extend self
|
7
7
|
|
8
8
|
AR = ActiveRecord::Base
|
9
|
-
log = ENV['VERBOSE'].present? ? $stderr : 'spec/debug.log'.tap{|f| File.
|
9
|
+
log = ENV['VERBOSE'].present? ? $stderr : 'spec/debug.log'.tap{|f| File.open(f, "ab") { |ft| ft.truncate(0) }}
|
10
10
|
AR.logger = ::Logger.new(log).tap do |l|
|
11
11
|
l.level = 0
|
12
12
|
end
|
data/spec/support/helpers.rb
CHANGED
@@ -69,6 +69,11 @@ module ChronoTest::Helpers
|
|
69
69
|
t.string :name
|
70
70
|
t.boolean :active
|
71
71
|
end
|
72
|
+
|
73
|
+
adapter.create_table 'elements', :temporal => true do |t|
|
74
|
+
t.string :title
|
75
|
+
t.string :type
|
76
|
+
end
|
72
77
|
end
|
73
78
|
|
74
79
|
after(:all) do
|
@@ -90,10 +95,16 @@ module ChronoTest::Helpers
|
|
90
95
|
|
91
96
|
belongs_to :foo
|
92
97
|
has_one :baz
|
98
|
+
|
99
|
+
has_timeline :with => :foo
|
93
100
|
end
|
94
101
|
|
95
102
|
class ::Baz < ActiveRecord::Base
|
96
|
-
|
103
|
+
include ChronoModel::TimeGate
|
104
|
+
|
105
|
+
belongs_to :bar
|
106
|
+
|
107
|
+
has_timeline :with => :bar
|
97
108
|
end
|
98
109
|
|
99
110
|
class ::Defoo < ActiveRecord::Base
|
@@ -101,6 +112,14 @@ module ChronoTest::Helpers
|
|
101
112
|
|
102
113
|
default_scope where(:active => true)
|
103
114
|
end
|
115
|
+
|
116
|
+
# STI case (https://github.com/ifad/chronomodel/issues/5)
|
117
|
+
class ::Element < ActiveRecord::Base
|
118
|
+
include ChronoModel::TimeMachine
|
119
|
+
end
|
120
|
+
|
121
|
+
class ::Publication < Element
|
122
|
+
end
|
104
123
|
}
|
105
124
|
|
106
125
|
def define_models!
|
data/spec/time_machine_spec.rb
CHANGED
@@ -10,7 +10,12 @@ describe ChronoModel::TimeMachine do
|
|
10
10
|
describe '.chrono_models' do
|
11
11
|
subject { ChronoModel::TimeMachine.chrono_models }
|
12
12
|
|
13
|
-
it { should == {
|
13
|
+
it { should == {
|
14
|
+
'foos' => Foo::History,
|
15
|
+
'defoos' => Defoo::History,
|
16
|
+
'bars' => Bar::History,
|
17
|
+
'elements' => Element::History
|
18
|
+
} }
|
14
19
|
end
|
15
20
|
|
16
21
|
|
@@ -31,6 +36,10 @@ describe ChronoModel::TimeMachine do
|
|
31
36
|
ts_eval(bar) { update_attributes! :name => 'new bar' }
|
32
37
|
}
|
33
38
|
|
39
|
+
let!(:baz) {
|
40
|
+
Baz.create :name => 'baz', :bar => bar
|
41
|
+
}
|
42
|
+
|
34
43
|
# Specs start here
|
35
44
|
#
|
36
45
|
describe '#as_of' do
|
@@ -110,6 +119,23 @@ describe ChronoModel::TimeMachine do
|
|
110
119
|
it { Defoo.unscoped.as_of(hidden.ts[0]).map(&:name).should == ['active 2', 'hidden 1'] }
|
111
120
|
it { Defoo.unscoped.as_of(hidden.ts[1]).map(&:name).should == ['active 2', 'hidden 2'] }
|
112
121
|
end
|
122
|
+
|
123
|
+
describe 'proxies from non-temporal models to temporal ones' do
|
124
|
+
it { baz.as_of(bar.ts[0]).name.should == 'baz' }
|
125
|
+
it { baz.as_of(bar.ts[1]).name.should == 'baz' }
|
126
|
+
it { baz.as_of(bar.ts[2]).name.should == 'baz' }
|
127
|
+
it { baz.as_of(bar.ts[3]).name.should == 'baz' }
|
128
|
+
|
129
|
+
it { baz.as_of(bar.ts[0]).bar.name.should == 'bar' }
|
130
|
+
it { baz.as_of(bar.ts[1]).bar.name.should == 'foo bar' }
|
131
|
+
it { baz.as_of(bar.ts[2]).bar.name.should == 'bar bar' }
|
132
|
+
it { baz.as_of(bar.ts[3]).bar.name.should == 'new bar' }
|
133
|
+
|
134
|
+
it { baz.as_of(bar.ts[0]).bar.foo.name.should == 'foo bar' }
|
135
|
+
it { baz.as_of(bar.ts[1]).bar.foo.name.should == 'foo bar' }
|
136
|
+
it { baz.as_of(bar.ts[2]).bar.foo.name.should == 'new foo' }
|
137
|
+
it { baz.as_of(bar.ts[3]).bar.foo.name.should == 'new foo' }
|
138
|
+
end
|
113
139
|
end
|
114
140
|
|
115
141
|
describe '#history' do
|
@@ -135,6 +161,82 @@ describe ChronoModel::TimeMachine do
|
|
135
161
|
it { foo.history[2].bars.all?(&:readonly?).should be_true }
|
136
162
|
it { bar.history.all? {|b| b.foo.readonly?}.should be_true }
|
137
163
|
end
|
164
|
+
|
165
|
+
describe 'allows a custom select list' do
|
166
|
+
it { foo.history.select(:id).first.attributes.keys.should == %w( id as_of_time ) }
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'does not add as_of_time when there are aggregates' do
|
170
|
+
it { foo.history.select('max (id)').to_sql.should_not =~ /as_of_time/ }
|
171
|
+
it { foo.history.select('max (id) as foo, min(id) as bar').first.attributes.keys.should == %w( foo bar ) }
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'orders by recorded_at, hid by default' do
|
175
|
+
it { foo.history.to_sql.should =~ /order by.*recorded_at,.*hid/i }
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'allows a custom order list' do
|
179
|
+
it { expect { foo.history.order('id') }.to_not raise_error }
|
180
|
+
it { foo.history.order('id').to_sql.should =~ /order by id/i }
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with STI models' do
|
184
|
+
let!(:pub) {
|
185
|
+
pub = ts_eval { Publication.create! :title => 'wrong title' }
|
186
|
+
ts_eval(pub) { update_attributes! :title => 'correct title' }
|
187
|
+
}
|
188
|
+
|
189
|
+
it { pub.history.map(&:title).should == ['wrong title', 'correct title'] }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#pred' do
|
194
|
+
context 'on the first history entry' do
|
195
|
+
subject { foo.history.first.pred }
|
196
|
+
it { should be_nil }
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'on the second history entry' do
|
200
|
+
subject { foo.history.second.pred }
|
201
|
+
it { should == foo.history.first }
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'on the last history entry' do
|
205
|
+
subject { foo.history.last.pred }
|
206
|
+
it { should == foo.history[foo.history.size - 2] }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#succ' do
|
211
|
+
context 'on the first history entry' do
|
212
|
+
subject { foo.history.first.succ }
|
213
|
+
it { should == foo.history.second }
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'on the second history entry' do
|
217
|
+
subject { foo.history.second.succ }
|
218
|
+
it { should == foo.history.third }
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'on the last history entry' do
|
222
|
+
subject { foo.history.last.succ }
|
223
|
+
it { should be_nil }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#first' do
|
228
|
+
subject { foo.history.sample.first }
|
229
|
+
it { should == foo.history.first }
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#last' do
|
233
|
+
subject { foo.history.sample.last }
|
234
|
+
it { should == foo.history.last }
|
235
|
+
end
|
236
|
+
|
237
|
+
describe '#record' do
|
238
|
+
subject { foo.history.sample.record }
|
239
|
+
it { should == foo }
|
138
240
|
end
|
139
241
|
|
140
242
|
describe '#historical?' do
|
@@ -197,32 +299,133 @@ describe ChronoModel::TimeMachine do
|
|
197
299
|
end
|
198
300
|
end
|
199
301
|
|
200
|
-
describe '#
|
302
|
+
describe '#timeline' do
|
303
|
+
split = lambda {|ts| ts.map!{|t| [t.to_i, t.usec]} }
|
304
|
+
|
201
305
|
timestamps_from = lambda {|*records|
|
202
|
-
records.map(&:history).flatten!.inject([]) {|ret, rec|
|
203
|
-
ret.concat [
|
306
|
+
ts = records.map(&:history).flatten!.inject([]) {|ret, rec|
|
307
|
+
ret.concat [
|
308
|
+
[rec.valid_from.to_i, rec.valid_from.usec + 2],
|
309
|
+
[rec.valid_to.to_i, rec.valid_to.usec + 2]
|
310
|
+
]
|
204
311
|
}.sort.uniq[0..-2]
|
205
312
|
}
|
206
313
|
|
207
314
|
describe 'on records having an :has_many relationship' do
|
208
|
-
|
315
|
+
describe 'by default returns timestamps of the record only' do
|
316
|
+
subject { split.call(foo.timeline) }
|
317
|
+
its(:size) { should == foo.ts.size }
|
318
|
+
it { should == timestamps_from.call(foo) }
|
319
|
+
end
|
209
320
|
|
210
|
-
describe 'returns timestamps
|
321
|
+
describe 'when asked, returns timestamps including the related objects' do
|
322
|
+
subject { split.call(foo.timeline(:with => :bars)) }
|
211
323
|
its(:size) { should == foo.ts.size + bar.ts.size }
|
212
|
-
it { should == timestamps_from.call(foo,
|
324
|
+
it { should == timestamps_from.call(foo, *foo.bars) }
|
213
325
|
end
|
214
326
|
end
|
215
327
|
|
216
|
-
describe 'on records
|
217
|
-
subject { bar.
|
328
|
+
describe 'on records using has_timeline :with' do
|
329
|
+
subject { split.call(bar.timeline) }
|
218
330
|
|
219
331
|
describe 'returns timestamps of the record and its associations' do
|
220
|
-
|
221
|
-
|
332
|
+
|
333
|
+
let!(:expected) do
|
334
|
+
creat = bar.history.first.valid_from
|
335
|
+
c_sec, c_usec = creat.to_i, creat.usec
|
336
|
+
|
337
|
+
timestamps_from.call(foo, bar).reject {|sec, usec|
|
338
|
+
sec < c_sec || ( sec == c_sec && usec < c_usec )
|
339
|
+
}
|
340
|
+
end
|
341
|
+
|
342
|
+
its(:size) { should == expected.size }
|
343
|
+
it { should == expected }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe 'on non-temporal records using has_timeline :with' do
|
348
|
+
subject { split.call(baz.timeline) }
|
349
|
+
|
350
|
+
describe 'returns timestamps of its temporal associations' do
|
351
|
+
its(:size) { should == bar.ts.size }
|
352
|
+
it { should == timestamps_from.call(bar) }
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe '#last_changes' do
|
358
|
+
context 'on plain records' do
|
359
|
+
context 'having history' do
|
360
|
+
subject { bar.last_changes }
|
361
|
+
it { should == {'name' => ['bar bar', 'new bar']} }
|
362
|
+
end
|
363
|
+
|
364
|
+
context 'without history' do
|
365
|
+
let(:record) { Bar.create!(:name => 'foreveralone') }
|
366
|
+
subject { record.last_changes }
|
367
|
+
it { should be_nil }
|
368
|
+
after { record.destroy.history.delete_all } # UGLY
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
context 'on history records' do
|
373
|
+
context 'at the beginning of the timeline' do
|
374
|
+
subject { bar.history.first.last_changes }
|
375
|
+
it { should be_nil }
|
376
|
+
end
|
377
|
+
|
378
|
+
context 'in the middle of the timeline' do
|
379
|
+
subject { bar.history.second.last_changes }
|
380
|
+
it { should == {'name' => ['bar', 'foo bar']} }
|
222
381
|
end
|
223
382
|
end
|
224
383
|
end
|
225
384
|
|
385
|
+
describe '#changes_against' do
|
386
|
+
context 'can compare records against history' do
|
387
|
+
it { bar.changes_against(bar.history.first).should ==
|
388
|
+
{'name' => ['bar', 'new bar']} }
|
389
|
+
|
390
|
+
it { bar.changes_against(bar.history.second).should ==
|
391
|
+
{'name' => ['foo bar', 'new bar']} }
|
392
|
+
|
393
|
+
it { bar.changes_against(bar.history.third).should ==
|
394
|
+
{'name' => ['bar bar', 'new bar']} }
|
395
|
+
|
396
|
+
it { bar.changes_against(bar.history.last).should == {} }
|
397
|
+
end
|
398
|
+
|
399
|
+
context 'can compare history against history' do
|
400
|
+
it { bar.history.first.changes_against(bar.history.third).should ==
|
401
|
+
{'name' => ['bar bar', 'bar']} }
|
402
|
+
|
403
|
+
it { bar.history.second.changes_against(bar.history.third).should ==
|
404
|
+
{'name' => ['bar bar', 'foo bar']} }
|
405
|
+
|
406
|
+
it { bar.history.third.changes_against(bar.history.third).should == {} }
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe '#pred' do
|
411
|
+
context 'on records having history' do
|
412
|
+
subject { bar.pred }
|
413
|
+
its(:name) { should == 'bar bar' }
|
414
|
+
end
|
415
|
+
|
416
|
+
context 'when there is enough history' do
|
417
|
+
subject { bar.pred.pred.pred.pred }
|
418
|
+
its(:name) { should == 'bar' }
|
419
|
+
end
|
420
|
+
|
421
|
+
context 'when no history is recorded' do
|
422
|
+
let(:record) { Bar.create!(:name => 'quuuux') }
|
423
|
+
subject { record.pred }
|
424
|
+
it { should be_nil }
|
425
|
+
after { record.destroy.history.delete_all }
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
226
429
|
context do
|
227
430
|
let!(:history) { foo.history.first }
|
228
431
|
let!(:current) { foo }
|
@@ -239,7 +442,8 @@ describe ChronoModel::TimeMachine do
|
|
239
442
|
|
240
443
|
describe 'on current records' do
|
241
444
|
subject { current.public_send(attr) }
|
242
|
-
|
445
|
+
|
446
|
+
it { should be_nil }
|
243
447
|
end
|
244
448
|
}
|
245
449
|
}
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chrono_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.5.0.beta
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Marcello Barnaba
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-02-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &77788870 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *77788870
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: pg
|
27
|
-
requirement: &
|
27
|
+
requirement: &77787530 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *77787530
|
36
36
|
description: Give your models as-of date temporal extensions. Built entirely for PostgreSQL
|
37
37
|
>= 9.0
|
38
38
|
email:
|
@@ -55,7 +55,9 @@ files:
|
|
55
55
|
- lib/chrono_model/compatibility.rb
|
56
56
|
- lib/chrono_model/patches.rb
|
57
57
|
- lib/chrono_model/railtie.rb
|
58
|
+
- lib/chrono_model/time_gate.rb
|
58
59
|
- lib/chrono_model/time_machine.rb
|
60
|
+
- lib/chrono_model/utils.rb
|
59
61
|
- lib/chrono_model/version.rb
|
60
62
|
- spec/adapter_spec.rb
|
61
63
|
- spec/config.yml.example
|
@@ -83,9 +85,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
86
|
none: false
|
85
87
|
requirements:
|
86
|
-
- - ! '
|
88
|
+
- - ! '>'
|
87
89
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
90
|
+
version: 1.3.1
|
89
91
|
requirements: []
|
90
92
|
rubyforge_project:
|
91
93
|
rubygems_version: 1.8.10
|