hiccup 0.5.14 → 0.5.15

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.
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