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
@@ -1,18 +1,18 @@
1
1
  module Hiccup
2
2
  module Inferable
3
3
  class Scorer
4
-
4
+
5
5
  def initialize(options={})
6
6
  @verbose = options.fetch(:verbose, false)
7
7
  end
8
-
9
-
10
-
8
+
9
+
10
+
11
11
  def pick_best_guess(guesses, dates)
12
12
  scored_guesses = guesses \
13
13
  .map { |guess| [guess, score_guess(guess, dates)] } \
14
14
  .sort_by { |(guess, score)| -score.to_f }
15
-
15
+
16
16
  if @verbose
17
17
  puts "\nGUESSES FOR #{dates}:"
18
18
  scored_guesses.each do |(guess, score)|
@@ -24,44 +24,44 @@ module Hiccup
24
24
  end
25
25
  puts ""
26
26
  end
27
-
27
+
28
28
  scored_guesses.first
29
29
  end
30
-
31
-
32
-
30
+
31
+
32
+
33
33
  def score_guess(guess, input_dates)
34
34
  predicted_dates = guess.occurrences_between(guess.start_date, guess.end_date)
35
-
35
+
36
36
  # prediction_rate is the percent of input dates predicted
37
37
  predictions = (predicted_dates & input_dates).length
38
38
  prediction_rate = Float(predictions) / Float(input_dates.length)
39
-
39
+
40
40
  # bricks are dates predicted by this guess but not in the input
41
41
  bricks = (predicted_dates - input_dates).length
42
-
42
+
43
43
  # brick_rate is the percent of bricks to predictions
44
44
  # A brick_rate >= 1 means that this guess bricks more than it predicts
45
45
  brick_rate = Float(bricks) / Float(input_dates.length)
46
-
46
+
47
47
  # complexity measures how many rules are necesary
48
48
  # to describe the pattern
49
49
  complexity = complexity_of(guess)
50
-
50
+
51
51
  # complexity_rate is the number of rules per inputs
52
52
  complexity_rate = Float(complexity) / Float(input_dates.length)
53
-
53
+
54
54
  Score.new(prediction_rate, brick_rate, complexity_rate)
55
55
  end
56
-
57
-
58
-
56
+
57
+
58
+
59
59
  def complexity_of(schedule)
60
60
  return schedule.weekly_pattern.length if schedule.weekly?
61
61
  return schedule.monthly_pattern.length if schedule.monthly?
62
62
  1
63
63
  end
64
-
64
+
65
65
  end
66
66
  end
67
67
  end
@@ -6,15 +6,15 @@ module Hiccup
6
6
  class Schedule
7
7
  extend Hiccup
8
8
  include ActiveModel::Validations
9
-
10
-
9
+
10
+
11
11
  hiccup :enumerable,
12
12
  :validatable,
13
13
  :humanizable,
14
14
  :inferable,
15
15
  :serializable => [:ical]
16
-
17
-
16
+
17
+
18
18
  def initialize(options={})
19
19
  @kind =(options[:kind] || :never).to_sym
20
20
  @start_date =(options[:start_date] || Date.today).to_date
@@ -24,11 +24,11 @@ module Hiccup
24
24
  @weekly_pattern = options[:weekly_pattern] || []
25
25
  @monthly_pattern = options[:monthly_pattern] || []
26
26
  end
27
-
28
-
27
+
28
+
29
29
  attr_accessor :kind, :start_date, :ends, :end_date, :skip, :weekly_pattern, :monthly_pattern
30
-
31
-
30
+
31
+
32
32
  def to_hash
33
33
  {
34
34
  :kind => kind,
@@ -39,7 +39,7 @@ module Hiccup
39
39
  :monthly_pattern => monthly_pattern
40
40
  }
41
41
  end
42
-
43
-
42
+
43
+
44
44
  end
45
45
  end
@@ -6,34 +6,34 @@ module Hiccup
6
6
  module Serializable
7
7
  module Ical
8
8
  extend ActiveSupport::Concern
9
-
10
-
9
+
10
+
11
11
  def to_ical
12
12
  ical_serializer.dump(self)
13
13
  end
14
-
15
-
14
+
15
+
16
16
  module ClassMethods
17
-
17
+
18
18
  def from_ical(ics)
19
19
  ical_serializer.load(ics)
20
20
  end
21
-
21
+
22
22
  def ical_serializer
23
23
  @ical_serializer ||= Serializers::Ical.new(self)
24
24
  end
25
-
25
+
26
26
  end
27
-
28
-
27
+
28
+
29
29
  private
30
-
31
-
30
+
31
+
32
32
  def ical_serializer
33
33
  self.class.ical_serializer
34
34
  end
35
-
36
-
35
+
36
+
37
37
  end
38
38
  end
39
39
  end
@@ -4,38 +4,38 @@ require "ri_cal"
4
4
  module Hiccup
5
5
  module Serializers
6
6
  class Ical
7
-
7
+
8
8
  def initialize(klass)
9
9
  @klass = klass
10
10
  end
11
-
12
-
13
-
11
+
12
+
13
+
14
14
  def dump(obj)
15
15
  @component = RiCal::Component::Event.new
16
16
  @component.dtstart = obj.start_date.to_time if obj.start_date
17
17
  @obj = obj
18
-
18
+
19
19
  case obj.kind
20
20
  when :weekly; add_weekly_rule
21
21
  when :monthly; add_monthly_rule
22
22
  when :annually; add_yearly_rule
23
23
  end
24
-
24
+
25
25
  StringIO.new.tap {|io|
26
26
  @component.export_properties_to(io)
27
27
  @component.export_x_properties_to(io)
28
28
  }.string
29
29
  end
30
-
31
-
32
-
30
+
31
+
32
+
33
33
  def load(ics)
34
34
  return unless ics # Required for now, for ActiveRecord
35
-
35
+
36
36
  component_ics = "BEGIN:VEVENT\n#{ics}\nEND:VEVENT"
37
37
  component = RiCal.parse_string(component_ics).first
38
-
38
+
39
39
  @obj = @klass.new
40
40
  @obj.start_date = component.dtstart_property.try(:to_datetime)
41
41
  component.rrule_property.each do |rule|
@@ -45,22 +45,22 @@ module Hiccup
45
45
  when "YEARLY"; parse_yearly_rule(rule)
46
46
  end
47
47
  end
48
-
48
+
49
49
  @obj
50
50
  end
51
-
52
-
53
-
51
+
52
+
53
+
54
54
  private
55
-
56
-
57
-
55
+
56
+
57
+
58
58
  def add_weekly_rule
59
59
  add_rule("WEEKLY", :byday => abbreviate_weekdays(@obj.weekly_pattern))
60
60
  end
61
-
62
-
63
-
61
+
62
+
63
+
64
64
  def add_monthly_rule
65
65
  byday = []
66
66
  bymonthday = []
@@ -72,19 +72,19 @@ module Hiccup
72
72
  bymonthday << occurrence
73
73
  end
74
74
  end
75
-
75
+
76
76
  add_rule("MONTHLY", :bymonthday => bymonthday) if bymonthday.any?
77
77
  add_rule("MONTHLY", :byday => byday) if byday.any?
78
78
  end
79
-
80
-
81
-
79
+
80
+
81
+
82
82
  def add_yearly_rule
83
83
  add_rule("YEARLY")
84
84
  end
85
-
86
-
87
-
85
+
86
+
87
+
88
88
  def add_rule(freq, options={})
89
89
  merge_default_options_for_new_rule!(freq, options)
90
90
  parent = options.delete(:parent)
@@ -92,7 +92,7 @@ module Hiccup
92
92
  rrule = RiCal::PropertyValue::RecurrenceRule.new(parent, options)
93
93
  @component.rrule_property.push(rrule)
94
94
  end
95
-
95
+
96
96
  def merge_default_options_for_new_rule!(freq, options)
97
97
  options.merge!({
98
98
  :freq => freq,
@@ -100,47 +100,47 @@ module Hiccup
100
100
  :until => @obj.ends? && @obj.end_date && @obj.end_date.to_time
101
101
  })
102
102
  end
103
-
104
-
105
-
106
-
107
-
103
+
104
+
105
+
106
+
107
+
108
108
  def parse_weekly_rule(rule)
109
109
  @obj.kind = :weekly
110
110
  @obj.weekly_pattern = backmap_weekdays(rule.by_list[:byday])
111
111
  parse_rule(rule)
112
112
  end
113
-
114
-
115
-
113
+
114
+
115
+
116
116
  def parse_monthly_rule(rule)
117
117
  @obj.kind = :monthly
118
118
  parse_monthly_bymonthyday(rule.by_list[:bymonthday])
119
119
  parse_monthly_byday(rule.by_list[:byday])
120
120
  parse_rule(rule)
121
121
  end
122
-
122
+
123
123
  def parse_monthly_bymonthyday(bymonthday)
124
124
  (bymonthday || []).each do |bymonthday|
125
125
  @obj.monthly_pattern = @obj.monthly_pattern + [bymonthday.ordinal]
126
126
  end
127
127
  end
128
-
128
+
129
129
  def parse_monthly_byday(byday)
130
130
  (byday || []).each do |byday|
131
131
  @obj.monthly_pattern = @obj.monthly_pattern + [[byday.index, backmap_weekday(byday)]]
132
132
  end
133
133
  end
134
-
135
-
136
-
134
+
135
+
136
+
137
137
  def parse_yearly_rule(rule)
138
138
  @obj.kind = :annually
139
139
  parse_rule(rule)
140
140
  end
141
-
142
-
143
-
141
+
142
+
143
+
144
144
  def parse_rule(rule)
145
145
  @obj.skip = rule.interval
146
146
  if rule.until
@@ -148,30 +148,30 @@ module Hiccup
148
148
  @obj.end_date = rule.until.to_datetime
149
149
  end
150
150
  end
151
-
152
-
153
-
154
-
155
-
151
+
152
+
153
+
154
+
155
+
156
156
  def abbreviate_weekdays(weekdays)
157
157
  weekdays.map(&method(:abbreviate_weekday)).compact
158
158
  end
159
-
159
+
160
160
  def abbreviate_weekday(weekday)
161
161
  WEEKDAY_MAP[weekday.to_s.downcase]
162
162
  end
163
-
163
+
164
164
  def backmap_weekdays(byday)
165
165
  byday ||= []
166
166
  byday.map(&method(:backmap_weekday)).compact
167
167
  end
168
-
168
+
169
169
  def backmap_weekday(byday)
170
170
  Date::DAYNAMES[byday.wday]
171
171
  end
172
-
173
-
174
-
172
+
173
+
174
+
175
175
  WEEKDAY_MAP = {
176
176
  "sunday" => "SU",
177
177
  "monday" => "MO",
@@ -181,9 +181,9 @@ module Hiccup
181
181
  "friday" => "FR",
182
182
  "saturday" => "SA"
183
183
  }
184
-
185
-
186
-
184
+
185
+
186
+
187
187
  end
188
188
  end
189
189
  end
@@ -6,18 +6,18 @@ module Hiccup
6
6
  module Validatable
7
7
  include Convenience
8
8
  extend ActiveSupport::Concern
9
-
10
-
9
+
10
+
11
11
  # !todo: use ActiveModel:Validation rather than a custom method
12
12
  included do
13
13
  validates :skip, numericality: {greater_than: 0}
14
14
  validate :validate_recurrence
15
15
  end
16
-
17
-
16
+
17
+
18
18
  private
19
-
20
-
19
+
20
+
21
21
  # !todo: use i18n to let clients of this library supply their own wording
22
22
  def validate_recurrence
23
23
  case kind
@@ -27,7 +27,7 @@ module Hiccup
27
27
  when :annually;
28
28
  else; invalid_kind!
29
29
  end
30
-
30
+
31
31
  errors.add :start_date, "is a #{start_date.class} not a Date" unless start_date.is_a?(Date)
32
32
  if ends?
33
33
  if end_date.is_a?(Date)
@@ -37,8 +37,8 @@ module Hiccup
37
37
  end
38
38
  end
39
39
  end
40
-
41
-
40
+
41
+
42
42
  def validate_weekly_recurrence
43
43
  if !weekly_pattern.is_a?(Array)
44
44
  errors.add(:weekly_pattern, "is a #{weekly_pattern.class}. It should be an array")
@@ -48,8 +48,8 @@ module Hiccup
48
48
  errors.add(:weekly_pattern, "should contain only weekdays. (#{invalid_names.to_sentence} are invalid)")
49
49
  end
50
50
  end
51
-
52
-
51
+
52
+
53
53
  def validate_monthly_recurrence
54
54
  if !monthly_pattern.is_a?(Array)
55
55
  errors.add(:monthly_pattern, "is a #{monthly_pattern.class}. It should be an array")
@@ -59,12 +59,12 @@ module Hiccup
59
59
  errors.add(:monthly_pattern, "contains invalid monthly occurrences")
60
60
  end
61
61
  end
62
-
63
-
62
+
63
+
64
64
  def invalid_occurrence?(occurrence)
65
65
  !valid_occurrence?(occurrence)
66
66
  end
67
-
67
+
68
68
  def valid_occurrence?(occurrence)
69
69
  if occurrence.is_a?(Array)
70
70
  i, wd = occurrence
@@ -74,26 +74,26 @@ module Hiccup
74
74
  i.is_a?(Fixnum) && (1..31).include?(i)
75
75
  end
76
76
  end
77
-
78
-
77
+
78
+
79
79
  def invalid_kind!
80
80
  errors.add(:kind, "#{kind.inspect} is not recognized. It must be one of #{Kinds.collect{|kind| ":#{kind}"}.to_sentence(:two_words_connector => " or ", :last_word_connector => ", or ")}.")
81
81
  end
82
-
83
-
82
+
83
+
84
84
  # def valid_occurrence?(occurrence)
85
85
  # if occurrence.is_a?(Array)
86
86
  # ordinal, kind = occurrence
87
- #
87
+ #
88
88
  # errors.add(:kind, "is not a valid monthly occurrence kind") unless Date::DAYNAMES.member?(kind)
89
89
  # if ordinal.is_a?(Fixnum)
90
- # errors.add(:ordinal, "is not a valid integer") unless (ordinal==-1) or (1..6).include?(ordinal)
90
+ # errors.add(:ordinal, "is not a valid integer") unless (ordinal==-1) or (1..6).include?(ordinal)
91
91
  # else
92
92
  # errors.add(:ordinal, "is not an integer")
93
93
  # end
94
94
  # else
95
95
  # ordinal = occurrence
96
- #
96
+ #
97
97
  # if ordinal.is_a?(Fixnum)
98
98
  # errors.add(:ordinal, "is not an integer between 1 and 31") unless (1..31).include?(ordinal)
99
99
  # else
@@ -101,7 +101,7 @@ module Hiccup
101
101
  # end
102
102
  # end
103
103
  # end
104
-
105
-
104
+
105
+
106
106
  end
107
107
  end