ice_cube_conrad 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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