ice_cube_chosko 0.1.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.
- checksums.yaml +7 -0
- data/config/locales/en.yml +178 -0
- data/config/locales/es.yml +176 -0
- data/config/locales/ja.yml +107 -0
- data/lib/ice_cube.rb +92 -0
- data/lib/ice_cube/builders/hash_builder.rb +27 -0
- data/lib/ice_cube/builders/ical_builder.rb +59 -0
- data/lib/ice_cube/builders/string_builder.rb +76 -0
- data/lib/ice_cube/deprecated.rb +36 -0
- data/lib/ice_cube/errors/count_exceeded.rb +7 -0
- data/lib/ice_cube/errors/until_exceeded.rb +7 -0
- data/lib/ice_cube/flexible_hash.rb +40 -0
- data/lib/ice_cube/i18n.rb +24 -0
- data/lib/ice_cube/null_i18n.rb +28 -0
- data/lib/ice_cube/occurrence.rb +101 -0
- data/lib/ice_cube/parsers/hash_parser.rb +91 -0
- data/lib/ice_cube/parsers/ical_parser.rb +91 -0
- data/lib/ice_cube/parsers/yaml_parser.rb +19 -0
- data/lib/ice_cube/rule.rb +123 -0
- data/lib/ice_cube/rules/daily_rule.rb +16 -0
- data/lib/ice_cube/rules/hourly_rule.rb +16 -0
- data/lib/ice_cube/rules/minutely_rule.rb +16 -0
- data/lib/ice_cube/rules/monthly_rule.rb +16 -0
- data/lib/ice_cube/rules/secondly_rule.rb +15 -0
- data/lib/ice_cube/rules/weekly_rule.rb +16 -0
- data/lib/ice_cube/rules/yearly_rule.rb +16 -0
- data/lib/ice_cube/schedule.rb +529 -0
- data/lib/ice_cube/single_occurrence_rule.rb +28 -0
- data/lib/ice_cube/time_util.rb +328 -0
- data/lib/ice_cube/validated_rule.rb +184 -0
- data/lib/ice_cube/validations/count.rb +61 -0
- data/lib/ice_cube/validations/daily_interval.rb +54 -0
- data/lib/ice_cube/validations/day.rb +71 -0
- data/lib/ice_cube/validations/day_of_month.rb +55 -0
- data/lib/ice_cube/validations/day_of_week.rb +77 -0
- data/lib/ice_cube/validations/day_of_year.rb +61 -0
- data/lib/ice_cube/validations/fixed_value.rb +95 -0
- data/lib/ice_cube/validations/hour_of_day.rb +55 -0
- data/lib/ice_cube/validations/hourly_interval.rb +54 -0
- data/lib/ice_cube/validations/lock.rb +95 -0
- data/lib/ice_cube/validations/minute_of_hour.rb +54 -0
- data/lib/ice_cube/validations/minutely_interval.rb +54 -0
- data/lib/ice_cube/validations/month_of_year.rb +54 -0
- data/lib/ice_cube/validations/monthly_interval.rb +53 -0
- data/lib/ice_cube/validations/schedule_lock.rb +46 -0
- data/lib/ice_cube/validations/second_of_minute.rb +54 -0
- data/lib/ice_cube/validations/secondly_interval.rb +51 -0
- data/lib/ice_cube/validations/until.rb +57 -0
- data/lib/ice_cube/validations/weekly_interval.rb +67 -0
- data/lib/ice_cube/validations/yearly_interval.rb +53 -0
- data/lib/ice_cube/version.rb +5 -0
- data/spec/spec_helper.rb +64 -0
- metadata +166 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
module IceCube
|
2
|
+
class HashParser
|
3
|
+
|
4
|
+
attr_reader :hash
|
5
|
+
|
6
|
+
def initialize(original_hash)
|
7
|
+
@hash = original_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_schedule
|
11
|
+
data = normalize_keys(hash)
|
12
|
+
schedule = IceCube::Schedule.new parse_time(data[:start_time])
|
13
|
+
apply_duration schedule, data
|
14
|
+
apply_end_time schedule, data
|
15
|
+
apply_rrules schedule, data
|
16
|
+
apply_exrules schedule, data
|
17
|
+
apply_rtimes schedule, data
|
18
|
+
apply_extimes schedule, data
|
19
|
+
yield schedule if block_given?
|
20
|
+
schedule
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def normalize_keys(hash)
|
26
|
+
data = IceCube::FlexibleHash.new(hash.dup)
|
27
|
+
|
28
|
+
if (start_date = data.delete(:start_date))
|
29
|
+
warn "IceCube: :start_date is deprecated, please use :start_time at: #{ caller[0] }"
|
30
|
+
data[:start_time] = start_date
|
31
|
+
end
|
32
|
+
|
33
|
+
{:rdates => :rtimes, :exdates => :extimes}.each do |old_key, new_key|
|
34
|
+
if (times = data.delete(old_key))
|
35
|
+
warn "IceCube: :#{old_key} is deprecated, please use :#{new_key} at: #{ caller[0] }"
|
36
|
+
(data[new_key] ||= []).concat times
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
data
|
41
|
+
end
|
42
|
+
|
43
|
+
def apply_duration(schedule, data)
|
44
|
+
return unless data[:duration]
|
45
|
+
schedule.duration = data[:duration].to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
def apply_end_time(schedule, data)
|
49
|
+
return unless data[:end_time]
|
50
|
+
schedule.end_time = parse_time(data[:end_time])
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply_rrules(schedule, data)
|
54
|
+
return unless data[:rrules]
|
55
|
+
data[:rrules].each do |h|
|
56
|
+
rrule = h.is_a?(IceCube::Rule) ? h : IceCube::Rule.from_hash(h)
|
57
|
+
|
58
|
+
schedule.rrule(rrule)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_exrules(schedule, data)
|
63
|
+
return unless data[:exrules]
|
64
|
+
warn "IceCube: :exrules is deprecated, and will be removed in a future release. at: #{ caller[0] }"
|
65
|
+
data[:exrules].each do |h|
|
66
|
+
rrule = h.is_a?(IceCube::Rule) ? h : IceCube::Rule.from_hash(h)
|
67
|
+
|
68
|
+
schedule.exrule(rrule)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def apply_rtimes(schedule, data)
|
73
|
+
return unless data[:rtimes]
|
74
|
+
data[:rtimes].each do |t|
|
75
|
+
schedule.add_recurrence_time TimeUtil.deserialize_time(t)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def apply_extimes(schedule, data)
|
80
|
+
return unless data[:extimes]
|
81
|
+
data[:extimes].each do |t|
|
82
|
+
schedule.add_exception_time TimeUtil.deserialize_time(t)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_time(time)
|
87
|
+
TimeUtil.deserialize_time(time)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module IceCube
|
2
|
+
class IcalParser
|
3
|
+
def self.schedule_from_ical(ical_string, options = {})
|
4
|
+
data = {}
|
5
|
+
ical_string.each_line do |line|
|
6
|
+
(property, value) = line.split(':')
|
7
|
+
(property, tzid) = property.split(';')
|
8
|
+
case property
|
9
|
+
when 'DTSTART'
|
10
|
+
data[:start_time] = Time.parse(value)
|
11
|
+
when 'DTEND'
|
12
|
+
data[:end_time] = Time.parse(value)
|
13
|
+
when 'EXDATE'
|
14
|
+
data[:extimes] ||= []
|
15
|
+
data[:extimes] += value.split(',').map{|v| Time.parse(v)}
|
16
|
+
when 'DURATION'
|
17
|
+
data[:duration] # FIXME
|
18
|
+
when 'RRULE'
|
19
|
+
data[:rrules] ||= []
|
20
|
+
data[:rrules] += [rule_from_ical(value)]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
Schedule.from_hash data
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.rule_from_ical(ical)
|
27
|
+
params = { validations: { } }
|
28
|
+
|
29
|
+
ical.split(';').each do |rule|
|
30
|
+
(name, value) = rule.split('=')
|
31
|
+
value.strip!
|
32
|
+
case name
|
33
|
+
when 'FREQ'
|
34
|
+
params[:freq] = value.downcase
|
35
|
+
when 'INTERVAL'
|
36
|
+
params[:interval] = value.to_i
|
37
|
+
when 'COUNT'
|
38
|
+
params[:count] = value.to_i
|
39
|
+
when 'UNTIL'
|
40
|
+
params[:until] = Time.parse(value).utc
|
41
|
+
when 'WKST'
|
42
|
+
params[:wkst] = TimeUtil.ical_day_to_symbol(value)
|
43
|
+
when 'BYSECOND'
|
44
|
+
params[:validations][:second_of_minute] = value.split(',').collect(&:to_i)
|
45
|
+
when 'BYMINUTE'
|
46
|
+
params[:validations][:minute_of_hour] = value.split(',').collect(&:to_i)
|
47
|
+
when 'BYHOUR'
|
48
|
+
params[:validations][:hour_of_day] = value.split(',').collect(&:to_i)
|
49
|
+
when 'BYDAY'
|
50
|
+
dows = {}
|
51
|
+
days = []
|
52
|
+
value.split(',').each do |expr|
|
53
|
+
day = TimeUtil.ical_day_to_symbol(expr.strip[-2..-1])
|
54
|
+
if expr.strip.length > 2 # day with occurence
|
55
|
+
occ = expr[0..-3].to_i
|
56
|
+
dows[day].nil? ? dows[day] = [occ] : dows[day].push(occ)
|
57
|
+
days.delete(TimeUtil.sym_to_wday(day))
|
58
|
+
else
|
59
|
+
days.push TimeUtil.sym_to_wday(day) if dows[day].nil?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
params[:validations][:day_of_week] = dows unless dows.empty?
|
63
|
+
params[:validations][:day] = days unless days.empty?
|
64
|
+
when 'BYMONTHDAY'
|
65
|
+
params[:validations][:day_of_month] = value.split(',').collect(&:to_i)
|
66
|
+
when 'BYMONTH'
|
67
|
+
params[:validations][:month_of_year] = value.split(',').collect(&:to_i)
|
68
|
+
when 'BYYEARDAY'
|
69
|
+
params[:validations][:day_of_year] = value.split(',').collect(&:to_i)
|
70
|
+
when 'BYSETPOS'
|
71
|
+
else
|
72
|
+
raise "Invalid or unsupported rrule command: #{name}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
params[:interval] ||= 1
|
77
|
+
|
78
|
+
# WKST only valid for weekly rules
|
79
|
+
params.delete(:wkst) unless params[:freq] == 'weekly'
|
80
|
+
|
81
|
+
rule = Rule.send(*params.values_at(:freq, :interval, :wkst).compact)
|
82
|
+
rule.count(params[:count]) if params[:count]
|
83
|
+
rule.until(params[:until]) if params[:until]
|
84
|
+
params[:validations].each do |key, value|
|
85
|
+
value.is_a?(Array) ? rule.send(key, *value) : rule.send(key, value)
|
86
|
+
end
|
87
|
+
|
88
|
+
rule
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module IceCube
|
4
|
+
class YamlParser < HashParser
|
5
|
+
|
6
|
+
SERIALIZED_START = /start_(?:time|date): .+(?<tz>(?:-|\+)\d{2}:\d{2})$/
|
7
|
+
|
8
|
+
attr_reader :hash
|
9
|
+
|
10
|
+
def initialize(yaml)
|
11
|
+
@hash = YAML::load(yaml)
|
12
|
+
yaml.match SERIALIZED_START do |match|
|
13
|
+
start_time = hash[:start_time] || hash[:start_date]
|
14
|
+
TimeUtil.restore_deserialized_offset start_time, match[:tz]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module IceCube
|
4
|
+
|
5
|
+
class Rule
|
6
|
+
|
7
|
+
attr_reader :uses
|
8
|
+
|
9
|
+
# Is this a terminating schedule?
|
10
|
+
def terminating?
|
11
|
+
until_time || occurrence_count
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(rule)
|
15
|
+
if rule.is_a? Rule
|
16
|
+
hash = to_hash
|
17
|
+
hash && hash == rule.to_hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash
|
22
|
+
h = to_hash
|
23
|
+
h.nil? ? super : h.hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_ical
|
27
|
+
raise MethodNotImplemented, "Expected to be overrridden by subclasses"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Convert from ical string and create a rule
|
31
|
+
def self.from_ical(ical)
|
32
|
+
IceCube::IcalParser.rule_from_ical(ical)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Yaml implementation
|
36
|
+
def to_yaml(*args)
|
37
|
+
YAML::dump(to_hash, *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
# From yaml
|
41
|
+
def self.from_yaml(yaml)
|
42
|
+
from_hash YAML::load(yaml)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hash
|
46
|
+
raise MethodNotImplemented, "Expected to be overridden by subclasses"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert from a hash and create a rule
|
50
|
+
def self.from_hash(original_hash)
|
51
|
+
hash = IceCube::FlexibleHash.new original_hash
|
52
|
+
return nil unless match = hash[:rule_type].match(/\:\:(.+?)Rule/)
|
53
|
+
rule = IceCube::Rule.send(match[1].downcase.to_sym, hash[:interval] || 1)
|
54
|
+
rule.interval(hash[:interval] || 1, TimeUtil.wday_to_sym(hash[:week_start] || 0)) if match[1] == "Weekly"
|
55
|
+
rule.until(TimeUtil.deserialize_time(hash[:until])) if hash[:until]
|
56
|
+
rule.count(hash[:count]) if hash[:count]
|
57
|
+
hash[:validations] && hash[:validations].each do |key, value|
|
58
|
+
key = key.to_sym unless key.is_a?(Symbol)
|
59
|
+
value.is_a?(Array) ? rule.send(key, *value) : rule.send(key, value)
|
60
|
+
end
|
61
|
+
rule
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reset the uses on the rule to 0
|
65
|
+
def reset
|
66
|
+
@uses = 0
|
67
|
+
end
|
68
|
+
|
69
|
+
def next_time(time, schedule, closing_time)
|
70
|
+
end
|
71
|
+
|
72
|
+
def on?(time, schedule)
|
73
|
+
next_time(time, schedule, time).to_i == time.to_i
|
74
|
+
end
|
75
|
+
|
76
|
+
# Whether this rule requires a full run
|
77
|
+
def full_required?
|
78
|
+
!@count.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Convenience methods for creating Rules
|
82
|
+
class << self
|
83
|
+
|
84
|
+
# Secondly Rule
|
85
|
+
def secondly(interval = 1)
|
86
|
+
SecondlyRule.new(interval)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Minutely Rule
|
90
|
+
def minutely(interval = 1)
|
91
|
+
MinutelyRule.new(interval)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Hourly Rule
|
95
|
+
def hourly(interval = 1)
|
96
|
+
HourlyRule.new(interval)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Daily Rule
|
100
|
+
def daily(interval = 1)
|
101
|
+
DailyRule.new(interval)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Weekly Rule
|
105
|
+
def weekly(interval = 1, week_start = :sunday)
|
106
|
+
WeeklyRule.new(interval, week_start)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Monthly Rule
|
110
|
+
def monthly(interval = 1)
|
111
|
+
MonthlyRule.new(interval)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Yearly Rule
|
115
|
+
def yearly(interval = 1)
|
116
|
+
YearlyRule.new(interval)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
class MonthlyRule < ValidatedRule
|
4
|
+
|
5
|
+
include Validations::MonthlyInterval
|
6
|
+
|
7
|
+
def initialize(interval = 1, week_start = :sunday)
|
8
|
+
super
|
9
|
+
interval(interval)
|
10
|
+
schedule_lock(:day, :hour, :min, :sec)
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
class WeeklyRule < ValidatedRule
|
4
|
+
|
5
|
+
include Validations::WeeklyInterval
|
6
|
+
|
7
|
+
def initialize(interval = 1, week_start = :sunday)
|
8
|
+
super
|
9
|
+
interval(interval, week_start)
|
10
|
+
schedule_lock(:wday, :hour, :min, :sec)
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module IceCube
|
2
|
+
|
3
|
+
class YearlyRule < ValidatedRule
|
4
|
+
|
5
|
+
include Validations::YearlyInterval
|
6
|
+
|
7
|
+
def initialize(interval = 1, week_start = :sunday)
|
8
|
+
super
|
9
|
+
interval(interval)
|
10
|
+
schedule_lock(:month, :day, :hour, :min, :sec)
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|