Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues/50 from ical #226

Closed
wants to merge 52 commits into from
Closed
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7ea22dc
add first (basic) tests for from_ical parse
skyporter Dec 19, 2011
6e22edb
add ruby mine settings to gitignore
skyporter Dec 19, 2011
1e60567
add some tests
skyporter Dec 19, 2011
0d47f2b
add netbeans folder to gitignore
jgauby Dec 19, 2011
08edfef
adding method from_ical for import ical rrules
jgauby Dec 19, 2011
144d6d8
file .gem
jgauby Dec 19, 2011
0624cbc
gem 0.7.5
jgauby Dec 19, 2011
a2ee250
version 0.7.7
jgauby Jan 2, 2012
ecc2e7c
Merge branch 'master' of https://github.com/seejohnrun/ice_cube
skyporter Feb 6, 2012
9a93eed
bump version
skyporter Feb 6, 2012
4f1fd27
bump version
skyporter Feb 6, 2012
d6bd6c7
Fix ZeroDivisionError when INTERVAL is zero
jgauby Feb 7, 2012
0d224b5
increment version
jgauby Feb 7, 2012
b337513
Merge remote-tracking branch 'digITpro/master'
btucker May 3, 2012
0bf72cc
travis config
btucker May 4, 2012
aea04f5
travis stuff
btucker May 4, 2012
b7a6cc2
Merge branch 'master' of git://github.com/seejohnrun/ice_cube
btucker Oct 29, 2012
eb033d3
Add support for parsing ICAL format
btucker Sep 4, 2013
9fa61d7
changed gemspec to have dev dependency on activesupport 4+
spra85 Apr 10, 2014
fa08a15
adding .bundle to gitignore
spra85 Apr 10, 2014
0c01b3e
removing deprecation warnings for using double instead of stub
spra85 Apr 10, 2014
111e91d
moved self.from_ical on Schedule and Rule into separate IcalParser class
spra85 Apr 25, 2014
d1d3651
added more descriptive text for test
spra85 Apr 25, 2014
c6d8584
add first (basic) tests for from_ical parse
skyporter Dec 19, 2011
fe69b40
resolving .gitignore conflicts
spra85 Apr 26, 2014
96bbbca
add some tests
skyporter Dec 19, 2011
83e8b07
rebase
spra85 Apr 26, 2014
4700f8a
adding method from_ical for import ical rrules
jgauby Dec 19, 2011
906bbee
file .gem
jgauby Dec 19, 2011
db9a435
gem 0.7.5
jgauby Dec 19, 2011
b7ce530
bump version
skyporter Feb 6, 2012
dfbf4fd
bump version
skyporter Feb 6, 2012
8f52160
Fix ZeroDivisionError when INTERVAL is zero
jgauby Feb 7, 2012
733fda8
travis config
btucker May 4, 2012
18c950a
rebasing
spra85 Apr 28, 2014
be82268
fixing issues post rebase
spra85 Apr 28, 2014
1db5593
rebasin'
spra85 Apr 28, 2014
b26b437
changed gemspec to have dev dependency on activesupport 4+
spra85 Apr 10, 2014
a380de8
rebasin'
spra85 Apr 28, 2014
4d0cdd2
resolving rebase issues
spra85 Apr 28, 2014
c4b1572
added more descriptive text for test
spra85 Apr 25, 2014
96d6779
resolving merge conflicts
spra85 Apr 28, 2014
ce15cd1
removing dup from_ical methods on Rule missed in rebase
spra85 Apr 28, 2014
a53af38
tabs -> spaces
spra85 Apr 28, 2014
d054d92
tabs -> spaces
spra85 Apr 28, 2014
85d524c
moved IcalParser into parsers directory
spra85 Apr 28, 2014
7bf3f18
fixing indentation
spra85 Apr 28, 2014
70c4ad0
updating from_ical to not use deprecated hash key of start_date, usin…
spra85 May 2, 2014
6d4316f
Raising exception per feedback on pull request instead of a string so…
spra85 May 19, 2014
6b3007f
Added no-op for BYSETPOS in iCal.
uris77 Jul 11, 2014
0000d48
Removed comments
uris77 Jul 11, 2014
6e24f72
Merge pull request #2 from uris77/issues/bysetpos-in_ical
spra85 Jul 11, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ coverage
coverage.data
*.gem
.bundle
*.idea
/nbproject
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ notifications:
branches:
only:
- master
- v0.7
- issues/50-from_ical
rvm:
- 1.9.3
- 2.0.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ schedule.add_recurrence_rule Rule.daily
schedule.occurring_at?(now + 1800) # true
schedule.occurring_between?(t1, t2)

# using end_time also sets the duration
# using end_time also sets the duration
schedule = Schedule.new(start = Time.now, :end_time => start + 3600)
schedule.add_recurrence_rule Rule.daily
schedule.occurring_at?(start + 3599) # true
Expand Down
Binary file added ice_cube-0.7.4.gem
Binary file not shown.
Binary file added ice_cube-0.7.5.gem
Binary file not shown.
Binary file added ice_cube-0.7.71.gem
Binary file not shown.
6 changes: 3 additions & 3 deletions ice_cube.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.rubyforge_project = "ice-cube"

s.add_development_dependency('rake')
s.add_development_dependency('rspec', '~> 2.12.0')
s.add_development_dependency('activesupport', '>= 3.0.0')
s.add_development_dependency('tzinfo')
s.add_development_dependency('rspec')
s.add_development_dependency('activesupport', '~> 4.0.0')
s.add_development_dependency('tzinfo', '~> 0.3')
end
2 changes: 2 additions & 0 deletions lib/ice_cube.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ module IceCube

autoload :HashParser, 'ice_cube/parsers/hash_parser'
autoload :YamlParser, 'ice_cube/parsers/yaml_parser'
autoload :IcalParser, 'ice_cube/parsers/ical_parser'

autoload :CountExceeded, 'ice_cube/errors/count_exceeded'
autoload :UntilExceeded, 'ice_cube/errors/until_exceeded'
autoload :ZeroInterval, 'ice_cube/errors/zero_interval'

autoload :ValidatedRule, 'ice_cube/validated_rule'
autoload :SingleOccurrenceRule, 'ice_cube/single_occurrence_rule'
Expand Down
7 changes: 7 additions & 0 deletions lib/ice_cube/errors/zero_interval.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module IceCube

# An exception for when interval is set to zero
class ZeroInterval < Exception
end

end
8 changes: 6 additions & 2 deletions lib/ice_cube/parsers/hash_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,19 @@ def apply_end_time(schedule, data)
def apply_rrules(schedule, data)
return unless data[:rrules]
data[:rrules].each do |h|
schedule.rrule(IceCube::Rule.from_hash(h))
rrule = h.is_a?(IceCube::Rule) ? h : IceCube::Rule.from_hash(h)

schedule.rrule(rrule)
end
end

def apply_exrules(schedule, data)
return unless data[:exrules]
warn "IceCube: :exrules deprecated. (This will be going away)"
data[:exrules].each do |h|
schedule.exrule(IceCube::Rule.from_hash(h))
rrule = h.is_a?(IceCube::Rule) ? h : IceCube::Rule.from_hash(h)

schedule.exrule(rrule)
end
end

Expand Down
88 changes: 88 additions & 0 deletions lib/ice_cube/parsers/ical_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
module IceCube
class IcalParser
def self.schedule_from_ical(ical_string, options = {})
data = {}
ical_string.each_line do |line|
(property, value) = line.split(':')
(property, tzid) = property.split(';')
case property
when 'DTSTART'
data[:start_time] = Time.parse(value)
when 'DTEND'
data[:end_time] = Time.parse(value)
when 'EXDATE'
data[:extimes] ||= []
data[:extimes] += value.split(',').map{|v| Time.parse(v)}
when 'DURATION'
data[:duration] # FIXME
when 'RRULE'
data[:rrules] = [rule_from_ical(value)]
end
end
Schedule.from_hash data
end

def self.rule_from_ical(ical)
params = { validations: { } }

ical.split(';').each do |rule|
(name, value) = rule.split('=')
value.strip!
case name
when 'FREQ'
params[:freq] = value.downcase
when 'INTERVAL'
params[:interval] = value.to_i
when 'COUNT'
params[:count] = value.to_i
when 'UNTIL'
params[:until] = DateTime.parse(value).to_time.utc
when 'WKST'
params[:wkst] = TimeUtil.ical_day_to_symbol(value)
when 'BYSECOND'
params[:validations][:second_of_minute] = value.split(',').collect{ |v| v.to_i }
when "BYMINUTE"
params[:validations][:minute_of_hour] = value.split(',').collect{ |v| v.to_i }
when "BYHOUR"
params[:validations][:hour_of_day] = value.split(',').collect{ |v| v.to_i }
when "BYDAY"
dows = {}
days = []
value.split(',').each do |expr|
day = TimeUtil.ical_day_to_symbol(expr.strip[-2..-1])
if expr.strip.length > 2 # day with occurence
occ = expr[0..-3].to_i
dows[day].nil? ? dows[day] = [occ] : dows[day].push(occ)
days.delete(TimeUtil.sym_to_wday(day))
else
days.push TimeUtil.sym_to_wday(day) if dows[day].nil?
end
end
params[:validations][:day_of_week] = dows unless dows.empty?
params[:validations][:day] = days unless days.empty?
when "BYMONTHDAY"
params[:validations][:day_of_month] = value.split(',').collect{ |v| v.to_i }
when "BYMONTH"
params[:validations][:month_of_year] = value.split(',').collect{ |v| v.to_i }
when "BYYEARDAY"
params[:validations][:day_of_year] = value.split(',').collect{ |v| v.to_i }
else
raise "Invalid or unsupported rrule command : #{name}"
end
end

params[:interval] ||= 1
# WKST only valid for weekly rules
params.delete(:wkst) unless params[:freq] == 'weekly'

rule = Rule.send(*params.values_at(:freq, :interval, :wkst).compact)
rule.count(params[:count]) if params[:count]
rule.until(params[:until]) if params[:until]
params[:validations].each do |key, value|
value.is_a?(Array) ? rule.send(key, *value) : rule.send(key, value)
end

rule
end
end
end
2 changes: 2 additions & 0 deletions lib/ice_cube/schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ def next_time(time, closing_time)
[min_time, new_time].compact.min
rescue StopIteration
min_time
rescue CountExceeded, UntilExceeded, ZeroInterval
next
end
end
break nil unless min_time
Expand Down
18 changes: 18 additions & 0 deletions lib/ice_cube/time_util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ module TimeUtil
:thursday => 4, :friday => 5, :saturday => 6
}

ICAL_DAYS = {
'SU' => :sunday, 'MO' => :monday, 'TU' => :tuesday, 'WE' => :wednesday,
'TH' => :thursday, 'FR' => :friday, 'SA' => :saturday
}

MONTHS = {
:january => 1, :february => 2, :march => 3, :april => 4, :may => 5,
:june => 6, :july => 7, :august => 8, :september => 9, :october => 10,
Expand Down Expand Up @@ -142,12 +147,25 @@ def self.wday_to_sym(wday)
end
end

# Convert a symbol to an ical day (SU, MO)
def self.week_start(sym)
raise "No such day: #{sym}" unless DAYS.keys.include?(sym)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to class these exceptions to they're easier to rescue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed up a change, thanks for the feedback @btucker

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks!!

day = sym.to_s.upcase[0..1]
day
end

# Convert weekday from base sunday to the schedule's week start.
def self.normalize_wday(wday, week_start)
(wday - sym_to_wday(week_start)) % 7
end
deprecated_alias :normalize_weekday, :normalize_wday

def self.ical_day_to_symbol(str)
day = ICAL_DAYS[str]
raise "No such day: #{str}" if day.nil?
day
end

# Return the count of the number of times wday appears in the month,
# and which of those time falls on
def self.which_occurrence_in_month(time, wday)
Expand Down
18 changes: 10 additions & 8 deletions lib/ice_cube/validations/daily_interval.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ def dst_adjust?
true
end

def validate(step_time, schedule)
t0, t1 = schedule.start_time, step_time
days = Date.new(t1.year, t1.month, t1.day) -
Date.new(t0.year, t0.month, t0.day)
offset = (days % interval).nonzero?
interval - offset if offset
end

def build_s(builder)
builder.base = interval == 1 ? 'Daily' : "Every #{interval} days"
end
Expand All @@ -47,6 +39,16 @@ def build_ical(builder)
builder['INTERVAL'] << interval unless interval == 1
end

def validate(time, schedule)
raise ZeroInterval if interval == 0
time_date = Date.new(time.year, time.month, time.day)
start_date = Date.new(schedule.start_time.year, schedule.start_time.month, schedule.start_time.day)
days = time_date - start_date
unless days % interval === 0
interval - (days % interval)
end
end

end

end
Expand Down
20 changes: 11 additions & 9 deletions lib/ice_cube/validations/hourly_interval.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ def dst_adjust?
false
end

def validate(step_time, schedule)
t0, t1 = schedule.start_time.to_i, step_time.to_i
sec = (t1 - t1 % ONE_HOUR) -
(t0 - t0 % ONE_HOUR)
hours = sec / ONE_HOUR
offset = (hours % interval).nonzero?
interval - offset if offset
end

def build_s(builder)
builder.base = interval == 1 ? 'Hourly' : "Every #{interval} hours"
end
Expand All @@ -47,6 +38,17 @@ def build_ical(builder)
builder['INTERVAL'] << interval unless interval == 1
end

def validate(time, schedule)
raise ZeroInterval if interval == 0
start_time = schedule.start_time
sec = (time.to_i - time.to_i % ONE_HOUR) -
(start_time.to_i - start_time.to_i % ONE_HOUR)
hours = sec / ONE_HOUR
unless hours % interval == 0
interval - (hours % interval)
end
end

end

end
Expand Down
20 changes: 11 additions & 9 deletions lib/ice_cube/validations/minutely_interval.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ def dst_adjust?
false
end

def validate(step_time, schedule)
t0, t1 = schedule.start_time.to_i, step_time.to_i
sec = (t1 - t1 % ONE_MINUTE) -
(t0 - t0 % ONE_MINUTE)
minutes = sec / ONE_MINUTE
offset = (minutes % interval).nonzero?
interval - offset if offset
end

def build_s(builder)
builder.base = interval == 1 ? 'Minutely' : "Every #{interval} minutes"
end
Expand All @@ -47,6 +38,17 @@ def build_ical(builder)
builder['INTERVAL'] << interval unless interval == 1
end

def validate(time, schedule)
raise ZeroInterval if interval == 0
start_time = schedule.start_time
sec = (time.to_i - time.to_i % ONE_MINUTE) -
(start_time.to_i - start_time.to_i % ONE_MINUTE)
minutes = sec / ONE_MINUTE
unless minutes % interval == 0
interval - (minutes % interval)
end
end

end

end
Expand Down
17 changes: 9 additions & 8 deletions lib/ice_cube/validations/monthly_interval.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ def dst_adjust?
true
end

def validate(step_time, schedule)
t0, t1 = schedule.start_time, step_time
months = (t1.month - t0.month) +
(t1.year - t0.year) * 12
offset = (months % interval).nonzero?
interval - offset if offset
end

def build_s(builder)
builder.base = interval == 1 ? 'Monthly' : "Every #{interval} months"
end
Expand All @@ -46,6 +38,15 @@ def build_ical(builder)
builder['INTERVAL'] << interval unless interval == 1
end

def validate(time, schedule)
raise ZeroInterval if interval == 0
start_time = schedule.start_time
months_to_start = (time.month - start_time.month) + (time.year - start_time.year) * 12
unless months_to_start % interval == 0
interval - (months_to_start % interval)
end
end

end

end
Expand Down
14 changes: 8 additions & 6 deletions lib/ice_cube/validations/secondly_interval.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ def dst_adjust?
false
end

def validate(step_time, schedule)
seconds = step_time.to_i - schedule.start_time.to_i
offset = (seconds % interval).nonzero?
interval - offset if offset
end

def build_s(builder)
builder.base = interval == 1 ? 'Secondly' : "Every #{interval} seconds"
end
Expand All @@ -44,6 +38,14 @@ def build_ical(builder)
builder['INTERVAL'] << interval unless interval == 1
end

def validate(time, schedule)
raise ZeroInterval if interval == 0
seconds = time.to_i - schedule.start_time.to_i
unless seconds % interval == 0
interval - (seconds % interval)
end
end

end

end
Expand Down
Loading