chronological 0.0.9 → 1.0.0beta1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/chronological.rb CHANGED
@@ -1,7 +1,97 @@
1
1
  require 'chronological/version'
2
- require 'chronological/base'
3
- require 'chronological/absolute_timeframe'
4
- require 'chronological/relative_timeframe'
2
+ require 'chronological/errors'
3
+ require 'chronological/strategy_resolver'
4
+ require 'chronological/strategies'
5
5
 
6
6
  module Chronological
7
+ def timeframe(*args)
8
+ options = args.first.is_a?(Hash) ? args.pop : {}
9
+ strategy = Chronological::StrategyResolver.resolve(options)
10
+
11
+ class_eval do
12
+ columns_hash[strategy.field_names[:starting_time]] ||= ActiveRecord::ConnectionAdapters::Column.new(strategy.field_names[:starting_time], nil, 'datetime')
13
+ columns_hash[strategy.field_names[:ending_time]] ||= ActiveRecord::ConnectionAdapters::Column.new(strategy.field_names[:ending_time], nil, 'datetime')
14
+ columns_hash[strategy.field_names[:starting_date]] ||= ActiveRecord::ConnectionAdapters::Column.new(strategy.field_names[:starting_date], nil, 'date')
15
+ columns_hash[strategy.field_names[:ending_date]] ||= ActiveRecord::ConnectionAdapters::Column.new(strategy.field_names[:ending_date], nil, 'date')
16
+ end
17
+
18
+ unless strategy.has_absolute_start?
19
+ define_method(strategy.field_names[:starting_time]) do
20
+ strategy.starting_time(self)
21
+ end
22
+ end
23
+
24
+ unless strategy.has_absolute_end?
25
+ define_method(strategy.field_names[:ending_time]) do
26
+ strategy.ending_time(self)
27
+ end
28
+ end
29
+
30
+ define_method(:scheduled?) do
31
+ strategy.scheduled?(self)
32
+ end
33
+
34
+ define_method(:partially_scheduled?) do
35
+ strategy.partially_scheduled?(self)
36
+ end
37
+
38
+ define_method(:inactive?) do
39
+ strategy.inactive?(self)
40
+ end
41
+
42
+ define_method(:in_progress?) do
43
+ strategy.in_progress?(self)
44
+ end
45
+
46
+ define_method(:duration) do
47
+ strategy.duration(self)
48
+ end
49
+
50
+ define_method(strategy.field_names[:starting_date]) do
51
+ strategy.starting_date(self)
52
+ end
53
+
54
+ define_method(strategy.field_names[:ending_date]) do
55
+ strategy.ending_date(self)
56
+ end
57
+
58
+ ###
59
+ # Scopes
60
+ #
61
+ define_singleton_method(:by_date) do |direction = :asc|
62
+ strategy.class.by_date(self, strategy.field_names, direction)
63
+ end
64
+
65
+ define_singleton_method(:ended) do
66
+ strategy.class.ended(self, strategy.field_names)
67
+ end
68
+
69
+ define_singleton_method(:not_yet_ended) do
70
+ strategy.class.not_yet_ended(self, strategy.field_names)
71
+ end
72
+
73
+ define_singleton_method(:in_progress) do
74
+ strategy.class.in_progress(self, strategy.field_names)
75
+ end
76
+
77
+ define_singleton_method(:started) do
78
+ strategy.class.started(self, strategy.field_names)
79
+ end
80
+
81
+ define_singleton_method(:in_progress?) do
82
+ strategy.class.in_progress?(self, strategy.field_names)
83
+ end
84
+
85
+ ###
86
+ # Aliases
87
+ #
88
+ instance_eval do
89
+ alias active? in_progress?
90
+ alias active in_progress
91
+ end
92
+
93
+ class_eval do
94
+ alias active? in_progress?
95
+ end
96
+ end
7
97
  end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ class ChronologicableStrategyClass
4
+ extend Chronological
5
+ end
6
+
7
+ describe Chronological do
8
+ describe '.timeframe' do
9
+ context 'when it is called with a symbol representing a strategy' do
10
+ before do
11
+ ChronologicableStrategyClass.class_eval do
12
+ timeframe type: :absolute,
13
+ start_utc: :started_at_utc,
14
+ end_utc: :ended_at_utc
15
+ end
16
+ end
17
+
18
+ pit 'is translated properly' do
19
+ ChronologicableStrategyClass.class_variable_get(:@@chronological_strategy).should eql Chronological::AbsoluteStrategy
20
+ end
21
+ end
22
+
23
+ context 'when it is called with a symbol that does not represent a strategy' do
24
+ it 'it throws an error' do
25
+ lambda do
26
+ ChronologicableStrategyClass.class_eval do
27
+ timeframe :grey_goose
28
+ end
29
+ end.should raise_error Chronological::UndefinedStrategy
30
+ end
31
+ end
32
+ end
33
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'sqlite3'
1
+ require 'pg'
2
2
  require 'active_record'
3
3
  require 'timecop'
4
4
  require 'chronological'
@@ -6,50 +6,4 @@ require 'chronological'
6
6
  Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
7
7
 
8
8
  RSpec.configure do |config|
9
- config.before(:suite) do
10
- SQLite3::Database.new 'tmp/test.db'
11
-
12
- ActiveRecord::Base.establish_connection(
13
- :adapter => 'sqlite3',
14
- :database => 'tmp/test.db'
15
- )
16
-
17
- class SetupTests < ActiveRecord::Migration
18
- def up
19
- create_table :base_chronologicables do |t|
20
- t.datetime :started_at
21
- t.datetime :ended_at
22
- end
23
-
24
- create_table :relative_chronologicables do |t|
25
- t.integer :starting_offset
26
- t.integer :ending_offset
27
- t.datetime :base_datetime_utc
28
- end
29
-
30
- create_table :absolute_chronologicables do |t|
31
- t.datetime :started_at_utc
32
- t.datetime :ended_at_utc
33
- end
34
-
35
- create_table :chronologicable_with_time_zones do |t|
36
- t.datetime :started_at_utc
37
- t.datetime :ended_at_utc
38
- t.string :time_zone
39
- end
40
- end
41
- end
42
-
43
- SetupTests.new.migrate(:up)
44
- end
45
-
46
- config.before(:each) do
47
- ActiveRecord::Base.connection.execute 'DELETE FROM relative_chronologicables'
48
- ActiveRecord::Base.connection.execute 'DELETE FROM absolute_chronologicables'
49
- ActiveRecord::Base.connection.execute 'DELETE FROM chronologicable_with_time_zones'
50
- end
51
-
52
- config.after(:suite) do
53
- `rm -f ./tmp/test.db`
54
- end
55
9
  end
@@ -1,21 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class AbsoluteChronologicable < ActiveRecord::Base
4
- include Chronological::AbsoluteTimeframe
4
+ extend Chronological
5
5
 
6
- absolute_timeframe start_utc: :started_at_utc,
7
- end_utc: :ended_at_utc
6
+ timeframe type: :absolute,
7
+ starting_time: :started_at_utc,
8
+ ending_time: :ended_at_utc
8
9
  end
9
10
 
10
11
  class ChronologicableWithTimeZone < ActiveRecord::Base
11
- include Chronological::AbsoluteTimeframe
12
+ extend Chronological
12
13
 
13
- absolute_timeframe start_utc: :started_at_utc,
14
- end_utc: :ended_at_utc,
15
- time_zone: :time_zone
14
+ timeframe type: :absolute,
15
+ starting_time: :started_at_utc,
16
+ ending_time: :ended_at_utc,
17
+ time_zone: :time_zone
16
18
  end
17
19
 
18
- describe Chronological::AbsoluteTimeframe, :timecop => true do
20
+ describe Chronological::AbsoluteStrategy, :timecop => true do
19
21
  let(:later) { Time.local(2012, 7, 26, 6, 0, 26) }
20
22
  let(:now) { Time.local(2012, 7, 26, 6, 0, 25) }
21
23
  let(:past) { Time.local(2012, 7, 26, 6, 0, 24) }
@@ -26,90 +28,125 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
26
28
 
27
29
  before { Timecop.freeze(now) }
28
30
 
29
- it { AbsoluteChronologicable.new.respond_to?(:starts_at_utc).should be_true }
30
- it { AbsoluteChronologicable.new.respond_to?(:starting_at_utc).should be_true }
31
- it { AbsoluteChronologicable.new.respond_to?(:ends_at_utc).should be_true }
32
- it { AbsoluteChronologicable.new.respond_to?(:ending_at_utc).should be_true }
33
- it { AbsoluteChronologicable.new.respond_to?(:active?).should be_true }
31
+ describe '.by_date' do
32
+ context 'when there are two chronologicables that start at the same time' do
33
+ context 'but end at different times' do
34
+ let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => past }
35
+ let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
34
36
 
35
- it { AbsoluteChronologicable.respond_to?(:active?).should be_true }
36
- it { AbsoluteChronologicable.respond_to?(:active).should be_true }
37
-
38
- context 'when there are two chronologicables that start at the same time' do
39
- context 'but end at different times' do
40
- let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => past }
41
- let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
37
+ context 'when no option is passed' do
38
+ it 'properly sorts them in ascending order' do
39
+ AbsoluteChronologicable.by_date.first.should eql chronologicable_1
40
+ AbsoluteChronologicable.by_date.last.should eql chronologicable_2
41
+ end
42
+ end
42
43
 
43
- describe '.by_date' do
44
- it 'properly sorts them' do
45
- AbsoluteChronologicable.by_date.first.should eql chronologicable_1
46
- AbsoluteChronologicable.by_date.last.should eql chronologicable_2
44
+ context 'when the :desc option is passed' do
45
+ it 'sorts them backwards by the start date' do
46
+ AbsoluteChronologicable.by_date(:desc).first.should eql chronologicable_2
47
+ AbsoluteChronologicable.by_date(:desc).last.should eql chronologicable_1
48
+ end
47
49
  end
48
50
  end
49
51
 
50
- describe '.by_date_reversed' do
51
- it 'sorts them backwards by the start date' do
52
- AbsoluteChronologicable.by_date_reversed.first.should eql chronologicable_2
53
- AbsoluteChronologicable.by_date_reversed.last.should eql chronologicable_1
52
+ context 'and end at the same time' do
53
+ let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
54
+ let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
55
+
56
+ describe '.by_date' do
57
+ context 'when in ascending order' do
58
+ it 'does not matter what order they are in as long as they are all there' do
59
+ AbsoluteChronologicable.by_date.should include chronologicable_1
60
+ AbsoluteChronologicable.by_date.should include chronologicable_2
61
+ end
62
+ end
63
+
64
+ context 'when in descending order' do
65
+ it 'does not matter what order they are in as long as they are all there' do
66
+ AbsoluteChronologicable.by_date(:desc).should include chronologicable_1
67
+ AbsoluteChronologicable.by_date(:desc).should include chronologicable_2
68
+ end
69
+ end
54
70
  end
55
71
  end
56
72
  end
57
73
 
58
- context 'and end at the same time' do
59
- let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
60
- let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
74
+ context 'when there are two chronologicables that start at different times' do
75
+ context 'and end at different times' do
76
+ let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
77
+ let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => now, :ended_at_utc => later }
61
78
 
62
- describe '.by_date' do
63
- it 'does not matter what order they are in as long as they are all there' do
64
- AbsoluteChronologicable.by_date.should include chronologicable_1
65
- AbsoluteChronologicable.by_date.should include chronologicable_2
79
+ context 'when in ascending order' do
80
+ it 'sorts them by the start date' do
81
+ AbsoluteChronologicable.by_date.first.should eql chronologicable_1
82
+ AbsoluteChronologicable.by_date.last.should eql chronologicable_2
83
+ end
84
+ end
85
+
86
+ context 'when in descending order' do
87
+ it 'sorts them backwards by the start date' do
88
+ AbsoluteChronologicable.by_date(:desc).first.should eql chronologicable_2
89
+ AbsoluteChronologicable.by_date(:desc).last.should eql chronologicable_1
90
+ end
66
91
  end
67
92
  end
68
93
 
69
- describe '.by_date_reversed' do
70
- it 'does not matter what order they are in as long as they are all there' do
71
- AbsoluteChronologicable.by_date.should include chronologicable_1
72
- AbsoluteChronologicable.by_date.should include chronologicable_2
94
+ context 'but end at the same time' do
95
+ let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => later }
96
+ let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => now, :ended_at_utc => later }
97
+
98
+ context 'when in ascending order' do
99
+ it 'sorts them by the start date' do
100
+ AbsoluteChronologicable.by_date.first.should eql chronologicable_1
101
+ AbsoluteChronologicable.by_date.last.should eql chronologicable_2
102
+ end
103
+ end
104
+
105
+ context 'when in descending order' do
106
+ it 'sorts them backwards by the start date' do
107
+ AbsoluteChronologicable.by_date(:desc).first.should eql chronologicable_2
108
+ AbsoluteChronologicable.by_date(:desc).last.should eql chronologicable_1
109
+ end
73
110
  end
74
111
  end
75
112
  end
76
113
  end
77
114
 
78
- context 'when there are two chronologicables that start at different times' do
79
- context 'and end at different times' do
80
- let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => now }
81
- let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => now, :ended_at_utc => later }
115
+ describe '.by_duration' do
116
+ context 'when there are two chronologicables that are different durations' do
117
+ let!(:chronologicable_1) { AbsoluteChronologicable.create(:started_at_utc => 3.seconds.ago, :ended_at_utc => 2.seconds.ago) }
118
+ let!(:chronologicable_2) { AbsoluteChronologicable.create(:started_at_utc => 3.seconds.ago, :ended_at_utc => 1.second.ago) }
82
119
 
83
- describe '.by_date' do
84
- it 'sorts them by the start date' do
120
+ context 'when no option is passed' do
121
+ it 'properly sorts them in ascending order' do
85
122
  AbsoluteChronologicable.by_date.first.should eql chronologicable_1
86
123
  AbsoluteChronologicable.by_date.last.should eql chronologicable_2
87
124
  end
88
125
  end
89
126
 
90
- describe '.by_date_reversed' do
127
+ context 'when the :desc option is passed' do
91
128
  it 'sorts them backwards by the start date' do
92
- AbsoluteChronologicable.by_date_reversed.first.should eql chronologicable_2
93
- AbsoluteChronologicable.by_date_reversed.last.should eql chronologicable_1
129
+ AbsoluteChronologicable.by_date(:desc).first.should eql chronologicable_2
130
+ AbsoluteChronologicable.by_date(:desc).last.should eql chronologicable_1
94
131
  end
95
132
  end
96
133
  end
97
134
 
98
- context 'but end at the same time' do
99
- let!(:chronologicable_1) { AbsoluteChronologicable.create :started_at_utc => past, :ended_at_utc => later }
100
- let!(:chronologicable_2) { AbsoluteChronologicable.create :started_at_utc => now, :ended_at_utc => later }
135
+ context 'when there are two chronologicables that are the same duration' do
136
+ let!(:chronologicable_1) { AbsoluteChronologicable.create(:started_at_utc => 3.seconds.ago, :ended_at_utc => 1.second.ago) }
137
+ let!(:chronologicable_2) { AbsoluteChronologicable.create(:started_at_utc => 3.seconds.ago, :ended_at_utc => 1.second.ago) }
101
138
 
102
- describe '.by_date' do
103
- it 'sorts them by the start date' do
104
- AbsoluteChronologicable.by_date.first.should eql chronologicable_1
105
- AbsoluteChronologicable.by_date.last.should eql chronologicable_2
139
+ context 'when no option is passed' do
140
+ it 'does not matter what order they are in' do
141
+ AbsoluteChronologicable.by_date.should include chronologicable_1
142
+ AbsoluteChronologicable.by_date.should include chronologicable_2
106
143
  end
107
144
  end
108
145
 
109
- describe '.by_date_reversed' do
110
- it 'sorts them backwards by the start date' do
111
- AbsoluteChronologicable.by_date_reversed.first.should eql chronologicable_2
112
- AbsoluteChronologicable.by_date_reversed.last.should eql chronologicable_1
146
+ context 'when the :desc option is passed' do
147
+ it 'does not matter what order they are in' do
148
+ AbsoluteChronologicable.by_date(:desc).should include chronologicable_1
149
+ AbsoluteChronologicable.by_date(:desc).should include chronologicable_2
113
150
  end
114
151
  end
115
152
  end
@@ -304,9 +341,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
304
341
  end
305
342
  end
306
343
 
307
- describe '.expired' do
344
+ describe '.ended' do
308
345
  it 'includes that chronologicable' do
309
- AbsoluteChronologicable.expired.should include chronologicable
346
+ AbsoluteChronologicable.ended.should include chronologicable
310
347
  end
311
348
  end
312
349
 
@@ -316,9 +353,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
316
353
  end
317
354
  end
318
355
 
319
- describe '.current' do
356
+ describe '.not_yet_ended' do
320
357
  it 'does not include that chronologicable' do
321
- AbsoluteChronologicable.current.should_not include chronologicable
358
+ AbsoluteChronologicable.not_yet_ended.should_not include chronologicable
322
359
  end
323
360
  end
324
361
 
@@ -338,9 +375,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
338
375
  end
339
376
  end
340
377
 
341
- describe '.expired' do
378
+ describe '.ended' do
342
379
  it 'does include that chronologicable' do
343
- AbsoluteChronologicable.expired.should include chronologicable
380
+ AbsoluteChronologicable.ended.should include chronologicable
344
381
  end
345
382
  end
346
383
 
@@ -350,9 +387,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
350
387
  end
351
388
  end
352
389
 
353
- describe '.current' do
390
+ describe '.not_yet_ended' do
354
391
  it 'does not include that chronologicable' do
355
- AbsoluteChronologicable.current.should_not include chronologicable
392
+ AbsoluteChronologicable.not_yet_ended.should_not include chronologicable
356
393
  end
357
394
  end
358
395
 
@@ -372,9 +409,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
372
409
  end
373
410
  end
374
411
 
375
- describe '.expired' do
412
+ describe '.ended' do
376
413
  it 'does not include that chronologicable' do
377
- AbsoluteChronologicable.expired.should_not include chronologicable
414
+ AbsoluteChronologicable.ended.should_not include chronologicable
378
415
  end
379
416
  end
380
417
 
@@ -384,9 +421,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
384
421
  end
385
422
  end
386
423
 
387
- describe '.current' do
424
+ describe '.not_yet_ended' do
388
425
  it 'includes that chronologicable' do
389
- AbsoluteChronologicable.current.should include chronologicable
426
+ AbsoluteChronologicable.not_yet_ended.should include chronologicable
390
427
  end
391
428
  end
392
429
 
@@ -410,9 +447,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
410
447
  end
411
448
  end
412
449
 
413
- describe '.expired' do
450
+ describe '.ended' do
414
451
  it 'does include that chronologicable' do
415
- AbsoluteChronologicable.expired.should include chronologicable
452
+ AbsoluteChronologicable.ended.should include chronologicable
416
453
  end
417
454
  end
418
455
 
@@ -422,9 +459,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
422
459
  end
423
460
  end
424
461
 
425
- describe '.current' do
462
+ describe '.not_yet_ended' do
426
463
  it 'does not include that chronologicable' do
427
- AbsoluteChronologicable.current.should_not include chronologicable
464
+ AbsoluteChronologicable.not_yet_ended.should_not include chronologicable
428
465
  end
429
466
  end
430
467
 
@@ -444,9 +481,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
444
481
  end
445
482
  end
446
483
 
447
- describe '.expired' do
484
+ describe '.ended' do
448
485
  it 'does not include that chronologicable' do
449
- AbsoluteChronologicable.expired.should_not include chronologicable
486
+ AbsoluteChronologicable.ended.should_not include chronologicable
450
487
  end
451
488
  end
452
489
 
@@ -456,9 +493,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
456
493
  end
457
494
  end
458
495
 
459
- describe '.current' do
496
+ describe '.not_yet_ended' do
460
497
  it 'includes that chronologicable' do
461
- AbsoluteChronologicable.current.should include chronologicable
498
+ AbsoluteChronologicable.not_yet_ended.should include chronologicable
462
499
  end
463
500
  end
464
501
 
@@ -480,9 +517,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
480
517
  end
481
518
  end
482
519
 
483
- describe '.expired' do
520
+ describe '.ended' do
484
521
  it 'does not include that chronologicable' do
485
- AbsoluteChronologicable.expired.should_not include chronologicable
522
+ AbsoluteChronologicable.ended.should_not include chronologicable
486
523
  end
487
524
  end
488
525
 
@@ -492,9 +529,9 @@ describe Chronological::AbsoluteTimeframe, :timecop => true do
492
529
  end
493
530
  end
494
531
 
495
- describe '.current' do
532
+ describe '.not_yet_ended' do
496
533
  it 'includes that chronologicable' do
497
- AbsoluteChronologicable.current.should include chronologicable
534
+ AbsoluteChronologicable.not_yet_ended.should include chronologicable
498
535
  end
499
536
  end
500
537