hiccup 0.5.14 → 0.5.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hiccup.rb +17 -17
  3. data/lib/hiccup/convenience.rb +9 -9
  4. data/lib/hiccup/core_ext/date.rb +7 -7
  5. data/lib/hiccup/core_ext/duration.rb +4 -4
  6. data/lib/hiccup/core_ext/enumerable.rb +2 -2
  7. data/lib/hiccup/core_ext/fixnum.rb +2 -2
  8. data/lib/hiccup/core_ext/hash.rb +2 -2
  9. data/lib/hiccup/enumerable.rb +43 -29
  10. data/lib/hiccup/enumerable/annually_enumerator.rb +23 -23
  11. data/lib/hiccup/enumerable/monthly_date_enumerator.rb +2 -2
  12. data/lib/hiccup/enumerable/monthly_enumerator.rb +40 -40
  13. data/lib/hiccup/enumerable/never_enumerator.rb +8 -8
  14. data/lib/hiccup/enumerable/schedule_enumerator.rb +39 -39
  15. data/lib/hiccup/enumerable/weekly_enumerator.rb +30 -30
  16. data/lib/hiccup/errors.rb +4 -0
  17. data/lib/hiccup/humanizable.rb +19 -19
  18. data/lib/hiccup/inferable.rb +30 -30
  19. data/lib/hiccup/inferable/dates_enumerator.rb +6 -6
  20. data/lib/hiccup/inferable/guesser.rb +21 -21
  21. data/lib/hiccup/inferable/score.rb +7 -7
  22. data/lib/hiccup/inferable/scorer.rb +19 -19
  23. data/lib/hiccup/schedule.rb +10 -10
  24. data/lib/hiccup/serializable/ical.rb +13 -13
  25. data/lib/hiccup/serializers/ical.rb +59 -59
  26. data/lib/hiccup/validatable.rb +23 -23
  27. data/lib/hiccup/version.rb +1 -1
  28. data/test/core_ext_date_test.rb +5 -5
  29. data/test/duration_ext_test.rb +8 -8
  30. data/test/enumerable_test.rb +103 -103
  31. data/test/humanizable_test.rb +24 -24
  32. data/test/ical_serializable_test.rb +29 -29
  33. data/test/inferrable_test.rb +84 -84
  34. data/test/leap_year_test.rb +7 -7
  35. data/test/monthly_enumerator_test.rb +13 -13
  36. data/test/performance_test.rb +7 -7
  37. data/test/validatable_test.rb +1 -1
  38. data/test/weekly_enumerator_test.rb +38 -38
  39. metadata +4 -3
@@ -4,11 +4,11 @@ module Hiccup
4
4
  module Enumerable
5
5
  class MonthlyDateEnumerator < MonthlyEnumerator
6
6
  protected
7
-
7
+
8
8
  def occurrences_in_month(year, month)
9
9
  monthly_pattern
10
10
  end
11
-
11
+
12
12
  end
13
13
  end
14
14
  end
@@ -3,7 +3,7 @@ require 'hiccup/enumerable/schedule_enumerator'
3
3
  module Hiccup
4
4
  module Enumerable
5
5
  class MonthlyEnumerator < ScheduleEnumerator
6
-
6
+
7
7
  def self.for(schedule)
8
8
  if schedule.monthly_pattern.empty?
9
9
  NeverEnumerator
@@ -13,45 +13,45 @@ module Hiccup
13
13
  self
14
14
  end
15
15
  end
16
-
17
-
18
-
16
+
17
+
18
+
19
19
  def started?
20
20
  !@position.nil?
21
21
  end
22
-
23
-
24
-
22
+
23
+
24
+
25
25
  protected
26
-
26
+
27
27
  attr_reader :year, :month, :cycle, :last_day_of_month
28
-
29
-
30
-
28
+
29
+
30
+
31
31
  def advance!
32
32
  @position += 1
33
33
  next_month if @position >= cycle.length
34
-
34
+
35
35
  day = cycle[@position]
36
36
  return self.next if day > last_day_of_month
37
37
  Date.new(year, month, day)
38
38
  rescue
39
39
  advance!
40
40
  end
41
-
41
+
42
42
  def rewind!
43
43
  @position -= 1
44
44
  prev_month if @position < 0
45
-
45
+
46
46
  day = cycle[@position]
47
47
  return self.prev if day > last_day_of_month
48
48
  Date.new(year, month, day)
49
49
  rescue
50
50
  rewind!
51
51
  end
52
-
53
-
54
-
52
+
53
+
54
+
55
55
  def first_occurrence_on_or_after(date)
56
56
  @year, @month, seed_day = date.year, date.month, date.day
57
57
  if skip > 1
@@ -62,19 +62,19 @@ module Hiccup
62
62
  seed_day = first_day_of_month
63
63
  end
64
64
  end
65
-
65
+
66
66
  get_context
67
-
67
+
68
68
  @position = cycle.index { |day| day >= seed_day }
69
69
  next_month unless @position
70
-
70
+
71
71
  day = cycle[@position]
72
72
  return self.next if day > last_day_of_month
73
73
  Date.new(year, month, day)
74
74
  rescue
75
75
  advance!
76
76
  end
77
-
77
+
78
78
  def first_occurrence_on_or_before(date)
79
79
  @year, @month, seed_day = date.year, date.month, date.day
80
80
  if skip > 1
@@ -86,21 +86,21 @@ module Hiccup
86
86
  seed_day = last_day_of_month
87
87
  end
88
88
  end
89
-
89
+
90
90
  get_context
91
-
91
+
92
92
  @position = cycle.rindex { |day| day <= seed_day }
93
93
  prev_month unless @position
94
-
94
+
95
95
  day = cycle[@position]
96
96
  return self.prev if day > last_day_of_month
97
97
  Date.new(year, month, day)
98
98
  rescue
99
99
  rewind!
100
100
  end
101
-
102
-
103
-
101
+
102
+
103
+
104
104
  def occurrences_in_month(year, month)
105
105
  wday_of_first_of_month = Date.new(year, month, 1).wday
106
106
  monthly_pattern.map do |occurrence|
@@ -117,48 +117,48 @@ module Hiccup
117
117
  end
118
118
  end
119
119
  end
120
-
121
-
122
-
120
+
121
+
122
+
123
123
  def next_month
124
124
  @position = 0
125
125
  add_to_months skip
126
126
  get_context
127
127
  end
128
-
128
+
129
129
  def add_to_months(offset)
130
130
  @month += offset
131
131
  @year, @month = @year + 1, @month - 12 while @month > 12
132
132
  end
133
-
133
+
134
134
  def prev_month
135
135
  @position = @cycle.length - 1
136
136
  subtract_from_months skip
137
137
  get_context
138
138
  end
139
-
139
+
140
140
  def subtract_from_months(offset)
141
141
  @month -= offset
142
142
  @year, @month = @year - 1, @month + 12 while @month < 1
143
143
  end
144
-
144
+
145
145
  def get_context
146
146
  @last_day_of_month = [4, 6, 9, 11].member?(month) ? 30 : 31
147
147
  @last_day_of_month = leap_year?(year) ? 29 : 28 if month == 2
148
148
  @cycle = occurrences_in_month(year, month).sort
149
149
  end
150
-
151
-
152
-
150
+
151
+
152
+
153
153
  def months_since_schedule_start(year, month)
154
154
  (year - start_date.year) * 12 + (month - start_date.month)
155
155
  end
156
-
156
+
157
157
  def first_day_of_month
158
158
  1
159
159
  end
160
-
161
-
160
+
161
+
162
162
  end
163
163
  end
164
164
  end
@@ -3,26 +3,26 @@ require 'hiccup/enumerable/schedule_enumerator'
3
3
  module Hiccup
4
4
  module Enumerable
5
5
  class NeverEnumerator < ScheduleEnumerator
6
-
7
-
6
+
7
+
8
8
  def next
9
9
  @cursor = @cursor ? nil : first_occurrence_on_or_after(seed_date)
10
10
  end
11
-
11
+
12
12
  def prev
13
13
  @cursor = @cursor ? nil : first_occurrence_on_or_before(seed_date)
14
14
  end
15
-
16
-
15
+
16
+
17
17
  def first_occurrence_on_or_after(date)
18
18
  start_date if date <= start_date
19
19
  end
20
-
20
+
21
21
  def first_occurrence_on_or_before(date)
22
22
  start_date unless date < start_date
23
23
  end
24
-
25
-
24
+
25
+
26
26
  end
27
27
  end
28
28
  end
@@ -1,7 +1,7 @@
1
1
  module Hiccup
2
2
  module Enumerable
3
3
  class ScheduleEnumerator
4
-
4
+
5
5
  def self.enum_for(schedule)
6
6
  case schedule.kind
7
7
  when :weekly then WeeklyEnumerator
@@ -10,9 +10,9 @@ module Hiccup
10
10
  else NeverEnumerator
11
11
  end
12
12
  end
13
-
14
-
15
-
13
+
14
+
15
+
16
16
  def initialize(schedule, seed_date)
17
17
  @schedule = schedule
18
18
  @ends = schedule.ends?
@@ -20,95 +20,95 @@ module Hiccup
20
20
  @seed_date = seed_date.to_date if seed_date.respond_to?(:to_date)
21
21
  @cursor = nil
22
22
  end
23
-
23
+
24
24
  attr_reader :schedule, :seed_date, :cursor
25
-
26
-
27
-
25
+
26
+
27
+
28
28
  def next
29
29
  @cursor = started? ? advance! : first_occurrence_on_or_after(seed_start_date)
30
30
  return nil if @cursor && ends? && @cursor > end_date
31
31
  @cursor
32
32
  end
33
-
33
+
34
34
  def prev
35
35
  @cursor = started? ? rewind! : first_occurrence_on_or_before(seed_end_date)
36
36
  return nil if @cursor && @cursor < start_date
37
37
  @cursor
38
38
  end
39
-
40
-
41
-
39
+
40
+
41
+
42
42
  def started?
43
43
  !@cursor.nil?
44
44
  end
45
-
45
+
46
46
  def ends?
47
47
  @ends
48
48
  end
49
-
50
-
51
-
49
+
50
+
51
+
52
52
  protected
53
-
54
-
55
-
53
+
54
+
55
+
56
56
  delegate :start_date, :weekly_pattern, :monthly_pattern, :end_date, :skip, :to => :schedule
57
-
58
-
59
-
57
+
58
+
59
+
60
60
  def leap_year?(year)
61
61
  return false unless (year % 4).zero?
62
62
  return (year % 400).zero? if (year % 100).zero?
63
63
  true
64
64
  end
65
-
66
-
67
-
65
+
66
+
67
+
68
68
  def seed_start_date
69
69
  return start_date if (seed_date < start_date)
70
70
  seed_date
71
71
  end
72
-
72
+
73
73
  def seed_end_date
74
74
  return end_date if (ends? && seed_date > end_date)
75
75
  seed_date
76
76
  end
77
-
78
-
79
-
77
+
78
+
79
+
80
80
  # These two methods DO assume that
81
81
  # date is predicted by the given schedule
82
82
  # Subclasses can probably supply more
83
83
  # performant implementations of these.
84
-
84
+
85
85
  def advance!
86
86
  puts "calling ScheduleEnumerator#advance! slow!"
87
87
  first_occurrence_on_or_after(cursor + 1)
88
88
  end
89
-
89
+
90
90
  def rewind!
91
91
  puts "calling ScheduleEnumerator#rewind! slow!"
92
92
  first_occurrence_on_or_before(cursor - 1)
93
93
  end
94
-
95
-
96
-
94
+
95
+
96
+
97
97
  # These two methods DO NOT assume that
98
98
  # date is predicted by the given schedule
99
99
  # Subclasses _must_ provide implementations
100
100
  # of these methods.
101
-
101
+
102
102
  def first_occurrence_on_or_after(date)
103
103
  raise NotImplementedError
104
104
  end
105
-
105
+
106
106
  def first_occurrence_on_or_before(date)
107
107
  raise NotImplementedError
108
108
  end
109
-
110
-
111
-
109
+
110
+
111
+
112
112
  end
113
113
  end
114
114
  end
@@ -3,57 +3,57 @@ require 'hiccup/enumerable/schedule_enumerator'
3
3
  module Hiccup
4
4
  module Enumerable
5
5
  class WeeklyEnumerator < ScheduleEnumerator
6
-
6
+
7
7
  def initialize(*args)
8
8
  super
9
-
9
+
10
10
  @wday_pattern = weekly_pattern.map do |weekday|
11
11
  Date::DAYNAMES.index(weekday)
12
12
  end.sort
13
-
13
+
14
14
  if @wday_pattern.empty?
15
15
  @base_date = start_date
16
16
  @starting_index = 0
17
17
  @cycle = []
18
18
  return
19
19
  end
20
-
20
+
21
21
  start_wday = start_date.wday
22
22
  if start_wday <= @wday_pattern.first or start_wday > @wday_pattern.last
23
23
  @base_date = start_date
24
24
  else
25
25
  @base_date = start_date - (start_wday - @wday_pattern.first)
26
26
  end
27
-
27
+
28
28
  @starting_index = wday_pattern.index { |wday| wday >= start_wday } || 0
29
29
  @cycle = calculate_cycle(schedule)
30
30
  end
31
-
31
+
32
32
  protected
33
-
34
-
35
-
33
+
34
+
35
+
36
36
  attr_reader :base_date,
37
37
  :wday_pattern,
38
38
  :starting_index,
39
39
  :cycle,
40
40
  :position
41
-
42
-
43
-
41
+
42
+
43
+
44
44
  def advance!
45
45
  date = cursor + cycle[position]
46
46
  @position = (position + 1) % cycle.length
47
47
  date
48
48
  end
49
-
49
+
50
50
  def rewind!
51
51
  @position = position <= 0 ? cycle.length - 1 : position - 1
52
52
  cursor - cycle[position]
53
53
  end
54
-
55
-
56
-
54
+
55
+
56
+
57
57
  def first_occurrence_on_or_after(date)
58
58
  result = nil
59
59
  wday = date.wday
@@ -61,16 +61,16 @@ module Hiccup
61
61
  wd = wd + 7 if wd < wday
62
62
  days_in_the_future = wd - wday
63
63
  temp = date + days_in_the_future
64
-
64
+
65
65
  remainder = ((temp - base_date) / 7).to_i % skip
66
66
  temp += (skip - remainder) * 7 if remainder > 0
67
-
67
+
68
68
  result = temp if !result || (temp < result)
69
69
  end
70
70
  @position = position_of(result) if result
71
71
  result
72
72
  end
73
-
73
+
74
74
  def first_occurrence_on_or_before(date)
75
75
  result = nil
76
76
  wday = date.wday
@@ -78,43 +78,43 @@ module Hiccup
78
78
  wd = wd - 7 if wd > wday
79
79
  days_in_the_past = wday - wd
80
80
  temp = date - days_in_the_past
81
-
81
+
82
82
  remainder = ((temp - base_date) / 7).to_i % skip
83
83
  temp -= remainder * 7 if remainder > 0
84
-
84
+
85
85
  result = temp if !result || (temp > result)
86
86
  end
87
87
  @position = position_of(result) if result
88
88
  result
89
89
  end
90
-
91
-
92
-
90
+
91
+
92
+
93
93
  def calculate_cycle(schedule)
94
94
  cycle = []
95
95
  offset = wday_pattern[starting_index]
96
96
  wdays = wday_pattern.map { |wday| wday - offset }.sort
97
-
97
+
98
98
  while wdays.first <= 0
99
99
  wdays.push (wdays.shift + 7 * skip)
100
100
  end
101
-
101
+
102
102
  cycle = [wdays.first]
103
103
  wdays.each_cons(2) do |wday1, wday2|
104
104
  cycle << (wday2 - wday1)
105
105
  end
106
106
  cycle
107
107
  end
108
-
108
+
109
109
  def position_of(date)
110
110
  date_i = wday_pattern.index(date.wday)
111
111
  position = date_i - starting_index
112
112
  position += wday_pattern.length if position < 0
113
113
  position
114
114
  end
115
-
116
-
117
-
115
+
116
+
117
+
118
118
  end
119
119
  end
120
120
  end