ice_cube_conrad 0.8.0

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 (42) hide show
  1. data/lib/ice_cube.rb +80 -0
  2. data/lib/ice_cube/builders/hash_builder.rb +27 -0
  3. data/lib/ice_cube/builders/ical_builder.rb +59 -0
  4. data/lib/ice_cube/builders/string_builder.rb +74 -0
  5. data/lib/ice_cube/deprecated.rb +28 -0
  6. data/lib/ice_cube/errors/count_exceeded.rb +7 -0
  7. data/lib/ice_cube/errors/until_exceeded.rb +7 -0
  8. data/lib/ice_cube/errors/zero_interval.rb +7 -0
  9. data/lib/ice_cube/rule.rb +182 -0
  10. data/lib/ice_cube/rules/daily_rule.rb +14 -0
  11. data/lib/ice_cube/rules/hourly_rule.rb +14 -0
  12. data/lib/ice_cube/rules/minutely_rule.rb +14 -0
  13. data/lib/ice_cube/rules/monthly_rule.rb +14 -0
  14. data/lib/ice_cube/rules/secondly_rule.rb +13 -0
  15. data/lib/ice_cube/rules/weekly_rule.rb +14 -0
  16. data/lib/ice_cube/rules/yearly_rule.rb +14 -0
  17. data/lib/ice_cube/schedule.rb +414 -0
  18. data/lib/ice_cube/single_occurrence_rule.rb +28 -0
  19. data/lib/ice_cube/time_util.rb +250 -0
  20. data/lib/ice_cube/validated_rule.rb +108 -0
  21. data/lib/ice_cube/validations/count.rb +56 -0
  22. data/lib/ice_cube/validations/daily_interval.rb +55 -0
  23. data/lib/ice_cube/validations/day.rb +65 -0
  24. data/lib/ice_cube/validations/day_of_month.rb +52 -0
  25. data/lib/ice_cube/validations/day_of_week.rb +70 -0
  26. data/lib/ice_cube/validations/day_of_year.rb +55 -0
  27. data/lib/ice_cube/validations/hour_of_day.rb +52 -0
  28. data/lib/ice_cube/validations/hourly_interval.rb +57 -0
  29. data/lib/ice_cube/validations/lock.rb +47 -0
  30. data/lib/ice_cube/validations/minute_of_hour.rb +51 -0
  31. data/lib/ice_cube/validations/minutely_interval.rb +57 -0
  32. data/lib/ice_cube/validations/month_of_year.rb +49 -0
  33. data/lib/ice_cube/validations/monthly_interval.rb +51 -0
  34. data/lib/ice_cube/validations/schedule_lock.rb +41 -0
  35. data/lib/ice_cube/validations/second_of_minute.rb +49 -0
  36. data/lib/ice_cube/validations/secondly_interval.rb +54 -0
  37. data/lib/ice_cube/validations/until.rb +51 -0
  38. data/lib/ice_cube/validations/weekly_interval.rb +60 -0
  39. data/lib/ice_cube/validations/yearly_interval.rb +49 -0
  40. data/lib/ice_cube/version.rb +5 -0
  41. data/spec/spec_helper.rb +11 -0
  42. metadata +120 -0
@@ -0,0 +1,65 @@
1
+ require 'date'
2
+
3
+ module IceCube
4
+
5
+ module Validations::Day
6
+
7
+ def day(*days)
8
+ days.each do |day|
9
+ day = TimeUtil.symbol_to_day(day.to_sym) if day.is_a?(Symbol) or day.is_a?(String)
10
+ validations_for(:day) << Validation.new(day)
11
+ end
12
+ clobber_base_validations(:wday, :day)
13
+ self
14
+ end
15
+
16
+ class Validation
17
+
18
+ include Validations::Lock
19
+
20
+ attr_reader :day
21
+ alias :value :day
22
+
23
+ def initialize(day)
24
+ @day = day
25
+ end
26
+
27
+ def build_s(builder)
28
+ builder.piece(:day) << day
29
+ end
30
+
31
+ def build_hash(builder)
32
+ builder.validations_array(:day) << day
33
+ end
34
+
35
+ def build_ical(builder)
36
+ ical_day = IcalBuilder.fixnum_to_ical_day(day)
37
+ # Only add if there aren't others from day_of_week that override
38
+ if builder['BYDAY'].none? { |b| b.end_with?(ical_day) }
39
+ builder['BYDAY'] << ical_day
40
+ end
41
+ end
42
+
43
+ def type
44
+ :wday
45
+ end
46
+
47
+ StringBuilder.register_formatter(:day) do |validation_days|
48
+ # sort the days
49
+ validation_days.sort!
50
+ # pick the right shortening, if applicable
51
+ if validation_days == [0, 6]
52
+ 'on Weekends'
53
+ elsif validation_days == (1..5).to_a
54
+ 'on Weekdays'
55
+ else
56
+ segments = validation_days.map { |d| "#{Date::DAYNAMES[d]}s" }
57
+ "on #{StringBuilder.sentence(segments)}"
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,52 @@
1
+ module IceCube
2
+
3
+ module Validations::DayOfMonth
4
+
5
+ include Validations::Lock
6
+
7
+ def day_of_month(*days)
8
+ days.each do |day|
9
+ validations_for(:day_of_month) << Validation.new(day)
10
+ end
11
+ clobber_base_validations(:day, :wday)
12
+ self
13
+ end
14
+
15
+ class Validation
16
+
17
+ include Validations::Lock
18
+
19
+ StringBuilder.register_formatter(:day_of_month) do |entries|
20
+ str = "on the #{StringBuilder.sentence(entries)} "
21
+ str << (entries.size == 1 ? 'day of the month' : 'days of the month')
22
+ str
23
+ end
24
+
25
+ attr_reader :day
26
+ alias :value :day
27
+
28
+ def initialize(day)
29
+ @day = day
30
+ end
31
+
32
+ def build_s(builder)
33
+ builder.piece(:day_of_month) << StringBuilder.nice_number(day)
34
+ end
35
+
36
+ def build_hash(builder)
37
+ builder.validations_array(:day_of_month) << day
38
+ end
39
+
40
+ def build_ical(builder)
41
+ builder['BYMONTHDAY'] << day
42
+ end
43
+
44
+ def type
45
+ :day
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,70 @@
1
+ module IceCube
2
+
3
+ module Validations::DayOfWeek
4
+
5
+ def day_of_week(dows)
6
+ dows.each do |day, occs|
7
+ occs.each do |occ|
8
+ day = TimeUtil.symbol_to_day(day.to_sym) if day.is_a?(Symbol) or day.is_a?(String)
9
+ validations_for(:day_of_week) << Validation.new(day, occ)
10
+ end
11
+ end
12
+ clobber_base_validations :day, :wday
13
+ self
14
+ end
15
+
16
+ class Validation
17
+
18
+ attr_reader :day, :occ
19
+
20
+ StringBuilder.register_formatter(:day_of_week) do |segments|
21
+ 'on the ' + segments.join(' when it is the ')
22
+ end
23
+
24
+ def type
25
+ :day
26
+ end
27
+
28
+ def build_s(builder)
29
+ builder.piece(:day_of_week) << "#{StringBuilder.nice_number(occ)} #{Date::DAYNAMES[day]}"
30
+ end
31
+
32
+ def build_ical(builder)
33
+ ical_day = IcalBuilder.fixnum_to_ical_day(day)
34
+ # Delete any with this day and no occ first
35
+ builder['BYDAY'].delete_if { |d| d == ical_day }
36
+ builder['BYDAY'] << "#{occ}#{ical_day}"
37
+ end
38
+
39
+ def build_hash(builder)
40
+ builder.validations[:day_of_week] ||= {}
41
+ arr = (builder.validations[:day_of_week][day] ||= [])
42
+ arr << occ
43
+ end
44
+
45
+ def initialize(day, occ)
46
+ @day = day
47
+ @occ = occ
48
+ end
49
+
50
+ def validate(time, schedule)
51
+ # count the days to the weekday
52
+ sum = day >= time.wday ? day - time.wday : 7 - time.wday + day
53
+ wrapper = TimeUtil::TimeWrapper.new(time)
54
+ wrapper.add :day, sum
55
+ # and then count the week until a viable occ
56
+ loop do
57
+ which_occ, num_occ = TimeUtil.which_occurrence_in_month(wrapper.to_time, day)
58
+ this_occ = occ < 0 ? num_occ + occ + 1 : occ
59
+ break if which_occ == this_occ
60
+ sum += 7
61
+ wrapper.add :day, 7 # one week
62
+ end
63
+ sum
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,55 @@
1
+ module IceCube
2
+
3
+ module Validations::DayOfYear
4
+
5
+ def day_of_year(*days)
6
+ days.each do |day|
7
+ validations_for(:day_of_year) << Validation.new(day)
8
+ end
9
+ clobber_base_validations(:month, :day, :wday)
10
+ self
11
+ end
12
+
13
+ class Validation
14
+
15
+ attr_reader :day
16
+
17
+ StringBuilder.register_formatter(:day_of_year) do |entries|
18
+ str = "on the #{StringBuilder.sentence(entries)} "
19
+ str << (entries.size == 1 ? 'day of the year' : 'days of the year')
20
+ str
21
+ end
22
+
23
+ def initialize(day)
24
+ @day = day
25
+ end
26
+
27
+ def type
28
+ :day
29
+ end
30
+
31
+ def build_s(builder)
32
+ builder.piece(:day_of_year) << StringBuilder.nice_number(day)
33
+ end
34
+
35
+ def build_hash(builder)
36
+ builder.validations_array(:day_of_year) << day
37
+ end
38
+
39
+ def build_ical(builder)
40
+ builder['BYYEARDAY'] << day
41
+ end
42
+
43
+ def validate(time, schedule)
44
+ days_in_year = TimeUtil.days_in_year(time)
45
+ the_day = day < 0 ? day + days_in_year : day
46
+ # compute the diff
47
+ diff = the_day - time.yday
48
+ diff >= 0 ? diff : diff + days_in_year
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,52 @@
1
+ module IceCube
2
+
3
+ module Validations::HourOfDay
4
+
5
+ include Validations::Lock
6
+
7
+ # Add hour of day validations
8
+ def hour_of_day(*hours)
9
+ hours.each do |hour|
10
+ validations_for(:hour_of_day) << Validation.new(hour)
11
+ end
12
+ clobber_base_validations(:hour)
13
+ self
14
+ end
15
+
16
+ class Validation
17
+
18
+ include Validations::Lock
19
+
20
+ StringBuilder.register_formatter(:hour_of_day) do |segments|
21
+ str = "on the #{StringBuilder.sentence(segments)} "
22
+ str << (segments.size == 1 ? 'hour of the day' : 'hours of the day')
23
+ end
24
+
25
+ attr_reader :hour
26
+ alias :value :hour
27
+
28
+ def initialize(hour)
29
+ @hour = hour
30
+ end
31
+
32
+ def build_s(builder)
33
+ builder.piece(:hour_of_day) << StringBuilder.nice_number(hour)
34
+ end
35
+
36
+ def type
37
+ :hour
38
+ end
39
+
40
+ def build_hash(builder)
41
+ builder.validations_array(:hour_of_day) << hour
42
+ end
43
+
44
+ def build_ical(builder)
45
+ builder['BYHOUR'] << hour
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,57 @@
1
+ module IceCube
2
+
3
+ module Validations::HourlyInterval
4
+
5
+ def interval(interval)
6
+ validations_for(:interval) << Validation.new(interval)
7
+ clobber_base_validations(:hour)
8
+ self
9
+ end
10
+
11
+ class Validation
12
+
13
+ attr_reader :interval
14
+
15
+ def type
16
+ :hour
17
+ end
18
+
19
+ def build_s(builder)
20
+ builder.base = interval == 1 ? 'Hourly' : "Every #{interval} hours"
21
+ end
22
+
23
+ def build_hash(builder)
24
+ builder[:interval] = interval
25
+ end
26
+
27
+ def build_ical(builder)
28
+ builder['FREQ'] << 'HOURLY'
29
+ unless interval == 1
30
+ builder['INTERVAL'] << interval
31
+ end
32
+ end
33
+
34
+ def initialize(interval)
35
+ @interval = interval
36
+ end
37
+
38
+ def dst_adjust?
39
+ false
40
+ end
41
+
42
+ def validate(time, schedule)
43
+ raise ZeroInterval if interval == 0
44
+ start_time = schedule.start_time
45
+ sec = (time.to_i - time.to_i % ONE_HOUR) -
46
+ (start_time.to_i - start_time.to_i % ONE_HOUR)
47
+ hours = sec / ONE_HOUR
48
+ unless hours % interval == 0
49
+ interval - (hours % interval)
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,47 @@
1
+ module IceCube
2
+
3
+ # A validation mixin that will lock the +type field to
4
+ # +value or +schedule.start_time.send(type) if value is nil
5
+
6
+ module Validations::Lock
7
+
8
+ INTERVALS = { :hour => 24, :min => 60, :sec => 60, :month => 12, :wday => 7 }
9
+ def validate(time, schedule)
10
+ return send(:"validate_#{type}_lock", time, schedule) unless INTERVALS[type]
11
+ start = value || schedule.start_time.send(type)
12
+ start = INTERVALS[type] + start if start < 0 # handle negative values
13
+ start >= time.send(type) ? start - time.send(type) : INTERVALS[type] - time.send(type) + start
14
+ end
15
+
16
+ private
17
+
18
+ # Needs to be custom since we don't know the days in the month
19
+ # (meaning, its not a fixed interval)
20
+ def validate_day_lock(time, schedule)
21
+ start = value || schedule.start_time.day
22
+ days_in_this_month = TimeUtil.days_in_month(time)
23
+ # If this number is positive, then follow our normal procedure
24
+ if start > 0
25
+ return start >= time.day ? start - time.day : days_in_this_month - time.day + start
26
+ end
27
+ # If the number is negative, and it resolved against the current month
28
+ # puts it in the future, just return the difference
29
+ days_in_this_month = TimeUtil.days_in_month(time)
30
+ start_one = days_in_this_month + start + 1
31
+ if start_one >= time.day
32
+ return start_one - time.day
33
+ end
34
+ # Otherwise, we need to figure out the meaning of the value
35
+ # in the next month, and then figure out how to get there
36
+ days_in_next_month = TimeUtil.days_in_next_month(time)
37
+ start_two = days_in_next_month + start + 1
38
+ if start_two >= time.day
39
+ days_in_this_month + start_two - time.day
40
+ else
41
+ days_in_next_month + start_two - time.day
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,51 @@
1
+ module IceCube
2
+
3
+ module Validations::MinuteOfHour
4
+
5
+ include Validations::Lock
6
+
7
+ def minute_of_hour(*minutes)
8
+ minutes.each do |minute|
9
+ validations_for(:minute_of_hour) << Validation.new(minute)
10
+ end
11
+ clobber_base_validations(:min)
12
+ self
13
+ end
14
+
15
+ class Validation
16
+
17
+ include Validations::Lock
18
+
19
+ StringBuilder.register_formatter(:minute_of_hour) do |segments|
20
+ str = "on the #{StringBuilder.sentence(segments)} "
21
+ str << (segments.size == 1 ? 'minute of the hour' : 'minutes of the hour')
22
+ end
23
+
24
+ attr_reader :minute
25
+ alias :value :minute
26
+
27
+ def initialize(minute)
28
+ @minute = minute
29
+ end
30
+
31
+ def build_s(builder)
32
+ builder.piece(:minute_of_hour) << StringBuilder.nice_number(minute)
33
+ end
34
+
35
+ def type
36
+ :min
37
+ end
38
+
39
+ def build_hash(builder)
40
+ builder.validations_array(:minute_of_hour) << minute
41
+ end
42
+
43
+ def build_ical(builder)
44
+ builder['BYMINUTE'] << minute
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end