recurs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in recurs.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ #gem 'rails', '3.0.1'
8
+ gem 'rspec'
9
+ end
data/README ADDED
@@ -0,0 +1,17 @@
1
+ # Recurs
2
+ Is a small Recurring Event parsing library. It will take symbolized input and output icalendar standard recurring events,
3
+ see the specs for examples of the api
4
+
5
+ This gem has been tested as a standalone in ruby 1.9, or using Rails 3
6
+
7
+ It also allows the developer to specifiy specialized Recurrence schemes by using the power of Ruby closures
8
+
9
+ ## Rails (ActiveRecord)
10
+
11
+ class Event < AR
12
+ acts_as_recurring
13
+ end
14
+
15
+
16
+ ## TODO
17
+ Finish the instance functionality
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/lib/recurs.rb ADDED
@@ -0,0 +1,245 @@
1
+ require 'util/module' unless defined? Rails
2
+ require 'recurs/consts'
3
+ require 'recurs/rules'
4
+ require 'ri_cal'
5
+ module Recurs
6
+ include RiCal
7
+ # Your code goes here...
8
+
9
+ module Parser
10
+
11
+ def self.included(base)
12
+ base.send :extend, ClassMethods
13
+ end
14
+
15
+ # class << self is NOT the same as including ( seen above ) which makes methods available to CLASSES that include the module
16
+ #, it ONLY makes the methods available to the MODULE itself
17
+ class << self
18
+ def rrule(repeats=nil, args={})
19
+ args[:rule] = :r
20
+ rule(repeats, args)
21
+ end
22
+
23
+ def exrule(repeats=nil, args={})
24
+ args[:rule] = :e
25
+ rule(repeats, args)
26
+ end
27
+
28
+ def rdate(dates=nil, args={})
29
+ args[:rule] = :r
30
+ date(dates, args)
31
+ end
32
+
33
+ def exdate(dates=nil, args={})
34
+ args[:rule] = :e
35
+ date(dates, args)
36
+ end
37
+
38
+ def date(dates=nil, args={})
39
+ args[:rule] == :r ? r = "RDATE" : r = "EXDATE"
40
+ if dates.is_a? Hash
41
+ f_dates = dates.flatten
42
+ if (f_dates[0] == :period) || (f_dates[0] == :range)
43
+ r += ";VALUE=PERIOD:"
44
+ i = 0
45
+ l = f_dates[1].length
46
+ e = 1
47
+ f_dates[1].each { |d|
48
+ r += RiCal::FastDateTime.from_date_time(d.to_datetime).ical_str
49
+ r += '/' if i == 0
50
+ r += ',' if (e < l) && (i != 0)
51
+ i += 1
52
+ e += 1
53
+ }
54
+ elsif f_dates[0] == :dates
55
+ r += ":"
56
+ l = f_dates[1].length
57
+ e = 1
58
+ f_dates[1].each {|d|
59
+ r += "#{RiCal::FastDateTime.from_date_time(d.to_datetime).ical_str}"
60
+ r += "," if (e < l)
61
+ e += 1
62
+ }
63
+ end
64
+ elsif dates.is_a?(Date) || dates.is_a?(DateTime)
65
+ r += ":#{RiCal::FastDateTime.from_date_time(dates.to_datetime).ical_str}"
66
+ end
67
+ r
68
+ end
69
+
70
+ protected
71
+ @@rule = nil
72
+
73
+ # The BYSECOND attr could be eg: 14 or multiple: 14, 45 between 0 and 59 ( i assume )
74
+ # The BYMINUTE attr could be eg: 14 or multiple: 14, 45 between 0 and 59 ( i assume )
75
+ # The BYSECOND attr could be eg: 14 or multiple: 14, 22 between 0 and 23 ( i assume )
76
+ # The BYDAY attribute allows you to specify exactly which days (SA, SU, MO, TU, WE, TH, FR)
77
+ # The BYMONTH is any month value between 1 .. 12
78
+ # the BYMONTHDAY is any value between 1 and 31
79
+ # WKST is the week starting on BYDAY eg SU,MO,TU,WE,TH
80
+
81
+ #args.each {|a| args[a[0]] = a[1].to_s.upcase if (a[1].is_a? String) || (a[1].is_a? Symbol)}
82
+
83
+
84
+ def rule(repeats, args = {})
85
+ args[:rule] == :r ? @@rule = "RRULE" : @@rule = "EXRULE"
86
+ @@rule += ":FREQ=#{repeats.to_s.upcase}"
87
+ interval(args)
88
+ args.each { |ar|
89
+ unless [:rule, :by_set_pos, :by_week_at, :count, :occurrences, :until, :ends_at, :repeats_every, :interval].include? ar[0]
90
+ @@rule += ";#{ar[0].to_s.gsub('_', '').upcase}=#{by_unit(ar[0], ar[1])}"
91
+ end
92
+ }
93
+ @@rule += ";BYSETPOS=#{args[:by_set_pos]}" if args[:by_set_pos]
94
+ @@rule += ";WKST=#{args[:by_week_st]}" if args[:by_week_st]
95
+ ending(args)
96
+ @@rule
97
+ end
98
+
99
+ def by_unit(measure, units)
100
+ ms = {:by_second => [60, get_num, BY_N_SECONDS],
101
+ :by_minute => [60, get_num, BY_N_MINUTES],
102
+ :by_hour => [23, get_num, BY_N_HOURS],
103
+ :by_day => [7, get_day_num, BY_DAYS],
104
+ :by_month_day => [31, get_day_num, BY_N_MONTH_DAYS],
105
+ :by_year_day => [366, get_day_num, BY_N_YEAR_DAYS],
106
+ :by_week => [52, get_num, BY_N_WEEKS],
107
+ :by_month => [12, get_month_num, BY_N_MONTHS]}
108
+
109
+ @measure = ms[measure]
110
+
111
+ r = ""
112
+ d = get_unit_nums(units)
113
+ comp = d.uniq.compact
114
+ c = comp.count
115
+ z = 0
116
+ comp.each { |i|
117
+ z += 1
118
+ r += @measure[2][i].to_s
119
+ r += "," unless z == c
120
+ }
121
+ r
122
+ end
123
+
124
+ def get_unit_nums(units)
125
+ r = []
126
+ # consts DAYS, BY_DAY
127
+ if units.is_a? Array
128
+ units.each { |d|
129
+ r << @measure[1].call(d)
130
+ }
131
+ else
132
+ r << @measure[1].call(units)
133
+ end
134
+ r
135
+ end
136
+
137
+ def get_num
138
+ ->(num){
139
+ num.to_i.modulo(@measure[0]) unless num.is_a? Symbol;
140
+ }
141
+ end
142
+
143
+ def get_day_num
144
+ ->(day){
145
+ if DAYS.include?(day.to_s.capitalize)
146
+ DAYS.find_index(day.to_s.capitalize)
147
+ elsif BY_DAYS.include?(day.to_s.upcase)
148
+ BY_DAYS.find_index(day.to_s.upcase)
149
+ else
150
+ day.to_i.modulo(7) unless day.is_a? Symbol
151
+ end;
152
+ }
153
+ end
154
+
155
+ def get_month_num
156
+ ->(mon){
157
+ if MONTHS.include?(mon.to_s.capitalize)
158
+ MONTHS.find_index(mon.to_s.capitalize)
159
+ elsif BY_N_MONTHS.include?(mon.to_s.upcase)
160
+ BY_N_MONTHS.find_index(mon.to_s.upcase)
161
+ else
162
+ mon.to_i.modulo(12) unless mon.is_a? Symbol
163
+ end;
164
+ }
165
+ end
166
+
167
+ def interval(args)
168
+ if args[:repeats_every]
169
+ @@rule += ";INTERVAL=#{args[:repeats_every]}"
170
+ elsif args[:interval]
171
+ @@rule += ";INTERVAL=#{args[:interval]}"
172
+ end
173
+ end
174
+
175
+ def ending(args)
176
+ if args[:count]
177
+ @@rule += ";COUNT=#{args[:count]}"
178
+ elsif args[:occurrences]
179
+ @@rule += ";COUNT=#{args[:occurrences]}"
180
+ elsif args[:until]
181
+ @@rule += ";UNTIL=#{RiCal::FastDateTime.from_date_time(args[:until].to_datetime).ical_str}"
182
+ elsif args[:ends_at]
183
+ @@rule += ";UNTIL=#{RiCal::FastDateTime.from_date_time(args[:ends_at].to_datetime).ical_str}"
184
+ end
185
+ end
186
+
187
+ end
188
+
189
+ module ClassMethods
190
+ def acts_as_recurring
191
+ send :include, InstanceMethods
192
+ end
193
+ end
194
+
195
+ module InstanceMethods
196
+ def initialize
197
+ @rrules ||= []
198
+ @exrules ||= []
199
+ @rdates ||= []
200
+ @exdates ||= []
201
+ super
202
+ end
203
+
204
+ def recurs
205
+ r = @rrules
206
+ r.concat @exrules
207
+ r.concat @rdates
208
+ r.concat @exdates
209
+ r.join
210
+ end
211
+
212
+ def add_rrule(repeats, args = {})
213
+ rrule = Parser.rrule(repeats, args)
214
+ @rrules << rrule
215
+ rrule
216
+ end
217
+
218
+ def add_exrule(repeats, args = {})
219
+ exrule = Parser.exrule(repeats, args)
220
+ @exrules << exrule
221
+ exrule
222
+ end
223
+
224
+ def add_rdate(args = {})
225
+ rdate = Parser.rdate(args)
226
+ @rdates << rdate
227
+ rdate
228
+ end
229
+
230
+ def add_exdate(args = {})
231
+ exdate = Parser.exdate(args)
232
+ @exdates << exdate
233
+ exdate
234
+ end
235
+
236
+ protected
237
+ attr_accessor :rrules, :exrules, :rdates, :exdates
238
+ end
239
+
240
+
241
+ end
242
+
243
+
244
+ end
245
+ ActiveRecord::Base.send(:include, Recurs::Parser) if defined? Rails
@@ -0,0 +1,40 @@
1
+ module Recurs
2
+ #module Consts
3
+ # 0 1 2 3 4 5 6
4
+ DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
5
+ MONTHS = ['January', "February", 'March', 'April', 'May', 'June',
6
+ 'July', 'August', 'September', 'October', 'November', 'December']
7
+ BY_N_SECONDS = (0..59).to_a
8
+ BY_N_MINUTES = (0..59).to_a
9
+ BY_N_HOURS = (0..23).to_a
10
+ BY_N_DAYS = (0..6).to_a
11
+ BY_N_MONTH_DAYS = (1..31).to_a
12
+ BY_N_YEAR_DAYS = (1..366).to_a
13
+ BY_DAYS = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']
14
+ BY_N_WEEKS = (1..54).to_a
15
+ BY_N_MONTHS = (1..12).to_a
16
+ BY_MONTHS = ['jan', 'feb', 'mar', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
17
+
18
+ #SYM_DAYS = [:sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday]
19
+ SYM_INTEGERS = [:zero, :one, :two, :three, :four, :five, :six, :seven, :eight, :nine, :ten]
20
+ SYM_TEENS = [:eleven, :twelve, :thirteen, :fourteen, :fifteen, :sixteen, :seventeen, :eighteen, :nineteen]
21
+ SYM_INTEGER_GROUP = SYM_INTEGERS+SYM_TEENS
22
+
23
+ # SYM_TENS[(34 - 34.modulo(10))/10]
24
+ SYM_TENS = [:ten, :twenty, :thirty, :fourty, :fifty, :sixty, :seventy, :eighty, :ninety, :hundred]
25
+
26
+ def get_integer_from_sym(sym)
27
+ SYM_INTEGER_GROUP(sym)
28
+ end
29
+
30
+ def build_num_sym(int)
31
+ if int > 19
32
+ r = int.modulo(10)
33
+ t = int - r
34
+ [SYM_TENS(t), SYM_INTEGERS[r]]
35
+ else
36
+ SYM_INTEGER_GROUP[int]
37
+ end
38
+ end
39
+ #end
40
+ end
@@ -0,0 +1,92 @@
1
+ module Recurs
2
+ module Rules
3
+ mattr_accessor :repeat_procs, :schema
4
+ @@repeat_procs = {
5
+
6
+ =begin
7
+ 'Daily' => ->(args = {}){
8
+ args[:repeats] = 'DAILY'
9
+ rrule(args);
10
+ }, # 0
11
+ =end
12
+
13
+ #=begin
14
+ 'Daily' => ->(set=false, args = {}){
15
+ unless set
16
+ @recurrence_template = ['standard','Days']
17
+ else
18
+ Parser.rrule(:daily, args)
19
+ end;
20
+ }, # 0
21
+ #=end
22
+
23
+ 'Every Weekday ( Mon - Fri )' => ->(set=false, args = {}){
24
+ unless set
25
+ @recurrence_template = 'set_points'
26
+ else
27
+ args[:by_day] = [1,2,3,4,5]
28
+ Parser.rrule(:weekly, args)
29
+ #
30
+ # .
31
+ end;
32
+ }, # 1
33
+
34
+ 'Every Mon, Wed, Fri' => ->(set=false, args = {}){
35
+ unless set
36
+ @recurrence_template = 'set_points'
37
+ else
38
+ args[:by_day] = [1,3,5]
39
+ Parser.rrule(:weekly, args)
40
+ end;
41
+ }, # 2
42
+
43
+ 'Every Tues, Thurs' => ->(set=false, args = {}){
44
+ unless set
45
+ @recurrence_template = 'set_points'
46
+ else
47
+ args[:by_day] = [2,4]
48
+ Parser.rrule(:weekly, args)
49
+ end;
50
+ }, # 3
51
+
52
+ 'Every Weekend' => ->(set=false, args = {}){
53
+ unless set
54
+ @recurrence_template = 'set_points'
55
+ else
56
+ args[:by_day] = [0,6]
57
+ Parser.rrule(:weekly, args)
58
+ end;
59
+ }, # 4
60
+
61
+ 'Weekly' => ->(set=false, args = {}){
62
+ unless set
63
+ @recurrence_template = ['weekly', 'Weeks']
64
+ else
65
+ Parser.rrule(:weekly, args)
66
+ end;
67
+ }, # 5
68
+
69
+ 'Monthly' => ->(set=false, args = {}){
70
+ unless set
71
+ @recurrence_template = ['monthly', 'Months']
72
+ else
73
+ Parser.rrule(:monthly, args)
74
+ end;
75
+ }, # 6
76
+
77
+ 'Yearly' => ->(set=false, args = {}){
78
+ unless set
79
+ @recurrence_template = ['standard', 'Years']
80
+ else
81
+ Parser.rrule(:yearly, args)
82
+ end;
83
+ } # 7
84
+
85
+ }
86
+ class << self
87
+ def schemes
88
+ @@schemas = @@repeat_procs.keys if @@repeat_procs.is_a? Hash
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module Recurs
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,67 @@
1
+ # these utility methods are taken from Ruby On Rails ActiveSupport module
2
+
3
+ class Array
4
+ # Extracts options from a set of arguments. Removes and returns the last
5
+ # element in the array if it's a hash, otherwise returns a blank hash.
6
+ #
7
+ # def options(*args)
8
+ # args.extract_options!
9
+ # end
10
+ #
11
+ # options(1, 2) # => {}
12
+ # options(1, 2, :a => :b) # => {:a=>:b}
13
+ def extract_options!
14
+ if last.is_a?(Hash) && last.extractable_options?
15
+ pop
16
+ else
17
+ {}
18
+ end
19
+ end
20
+ end
21
+
22
+ class Module
23
+ def mattr_reader(*syms)
24
+ syms.each do |sym|
25
+ next if sym.is_a?(Hash)
26
+ class_eval(<<-EOS, __FILE__, __LINE__)
27
+ unless defined? @@#{sym}
28
+ @@#{sym} = nil
29
+ end
30
+
31
+ def self.#{sym}
32
+ @@#{sym}
33
+ end
34
+
35
+ def #{sym}
36
+ @@#{sym}
37
+ end
38
+ EOS
39
+ end
40
+ end
41
+
42
+ def mattr_writer(*syms)
43
+ options = syms.extract_options!
44
+ syms.each do |sym|
45
+ class_eval(<<-EOS, __FILE__, __LINE__)
46
+ unless defined? @@#{sym}
47
+ @@#{sym} = nil
48
+ end
49
+
50
+ def self.#{sym}=(obj)
51
+ @@#{sym} = obj
52
+ end
53
+
54
+ #{"
55
+ def #{sym}=(obj)
56
+ @@#{sym} = obj
57
+ end
58
+ " unless options[:instance_writer] == false }
59
+ EOS
60
+ end
61
+ end
62
+
63
+ def mattr_accessor(*syms)
64
+ mattr_reader(*syms)
65
+ mattr_writer(*syms)
66
+ end
67
+ end
data/recurs.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "recurs/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "recurs"
7
+ s.version = Recurs::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Steve Caney Martin"]
10
+ s.email = ["steve@shakewell.co.uk"]
11
+ s.homepage = "http://rubygems.org/gems/recurs"
12
+ s.summary = %q{A recurrence generator for ical format}
13
+ s.description = %q{Specifiy you're recurrence pattern in symbols and strings and get an ical format recurrence string}
14
+
15
+ s.rubyforge_project = "recurs"
16
+ s.add_dependency('ri_cal', '>= 0.8.7')
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ class Event;end
4
+ Event.send(:include, Recurs::Parser)
5
+ class Event
6
+ acts_as_recurring
7
+ end
8
+
9
+ describe Event do
10
+ before :each do
11
+ @event = Event.new
12
+ end
13
+
14
+ it "should act as recurring" do
15
+ #@event.rrules
16
+ #@event.exrules
17
+ #@event.rdates
18
+ #@event.exdates
19
+ @event.recurs
20
+ end
21
+
22
+ it "should add an rrule" do
23
+ @event.add_rrule(:daily).should == "RRULE:FREQ=DAILY"
24
+ @event.recurs.should == "RRULE:FREQ=DAILY"
25
+ end
26
+
27
+ it "should add an exrule" do
28
+ @event.add_exrule(:daily).should == "EXRULE:FREQ=DAILY"
29
+ @event.recurs.should == "EXRULE:FREQ=DAILY"
30
+ end
31
+
32
+ it "should add an rdate" do
33
+ @event.add_rdate(Date.today).should == "RDATE:#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str}"
34
+ end
35
+
36
+ it "should add an rdate range" do
37
+ @event.add_rdate(:period => [Date.today, (Date.today+2)]).should == "RDATE;VALUE=PERIOD:#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str}/#{RiCal::FastDateTime.from_date_time((Date.today+2).to_datetime).ical_str}"
38
+ end
39
+ it "should add a list of rdates" do
40
+ @event.add_rdate(:dates => [Date.today, (Date.today+2)]).should == "RDATE:#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str},#{RiCal::FastDateTime.from_date_time((Date.today+2).to_datetime).ical_str}"
41
+ end
42
+ it "should add an exdate" do
43
+ @event.add_exdate(Date.today).should == "EXDATE:#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str}"
44
+ end
45
+
46
+ end
47
+
48
+ =begin
49
+ An event has recurrence
50
+
51
+ @event.recurs #"DTSTART;TZID=US-Eastern:19970902T0900
52
+ RRULE:FREQ=DAILY;COUNT=10
53
+ EXRULE:"
54
+
55
+ TODO: Ascertain how ri_cal parses complex rules
56
+
57
+ While building the recurrence the event must use private instance attributes from the recurrrence module:
58
+
59
+ The recurrence module must be mixed into the model transparently, either by using an acts_as
60
+ or by inheriting from the module
61
+
62
+ class Event < AR
63
+ acts_as_recurring_event
64
+ end
65
+
66
+ =end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Recurs do
4
+
5
+ end
6
+
7
+ describe Recurs::Parser do
8
+ #pending "add some examples to (or delete) #{__FILE__}"
9
+
10
+ it "should respond to class/module methods:" do
11
+ Recurs::Parser.rrule
12
+ Recurs::Parser.exrule
13
+ end
14
+
15
+ it "should get basic rrule" do
16
+ Recurs::Parser.rrule.should == "RRULE:FREQ="
17
+ end
18
+
19
+ it "should get basic exrule" do
20
+ Recurs::Parser.exrule.should == "EXRULE:FREQ="
21
+ end
22
+
23
+ it "should build a basic daily rule" do
24
+ Recurs::Parser.rrule(:daily).should == "RRULE:FREQ=DAILY"
25
+ end
26
+
27
+ it "should create a daily recurrence with a two day interval" do
28
+ Recurs::Parser.rrule(:daily, :interval => 2).should == "RRULE:FREQ=DAILY;INTERVAL=2"
29
+ Recurs::Parser.rrule(:daily, :repeats_every => 2).should == "RRULE:FREQ=DAILY;INTERVAL=2"
30
+ Recurs::Parser.rrule(:daily, :repeats_every => 2, :interval => 2).should == "RRULE:FREQ=DAILY;INTERVAL=2"
31
+ end
32
+ #=begin
33
+ it "should create a daily recurrence with a two day interval for ten occurrences" do
34
+ Recurs::Parser.rrule(:daily, :interval => 2, :count => 10).should == "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=10"
35
+ Recurs::Parser.rrule(:daily, :interval => 2, :occurrences => 10).should == "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=10"
36
+ Recurs::Parser.rrule(:daily, :interval => 2, :count => 10, :occurrences => 10).should == "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=10"
37
+ end
38
+
39
+ it "should create a valid ending, 'count' OR 'until' NOT both, PREFER 'count'" do
40
+ Recurs::Parser.rrule(:daily, :interval => 2, :count => 10).should == "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=10"
41
+ Recurs::Parser.rrule(:daily, :interval => 2, :ends_at => Date.today).should == "RRULE:FREQ=DAILY;INTERVAL=2;UNTIL=#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str}"
42
+ Recurs::Parser.rrule(:daily, :interval => 2, :until => Date.today).should == "RRULE:FREQ=DAILY;INTERVAL=2;UNTIL=#{RiCal::FastDateTime.from_date_time(Date.today.to_datetime).ical_str}"
43
+ Recurs::Parser.rrule(:daily, :interval => 2, :until => Date.today, :count => 10).should == "RRULE:FREQ=DAILY;INTERVAL=2;COUNT=10"
44
+ end
45
+ #=end
46
+ it "should create a weekly occurrence" do
47
+ Recurs::Parser.rrule(:weekly).should == "RRULE:FREQ=WEEKLY"
48
+ end
49
+
50
+ it "should build a basic daily rule with a count" do
51
+ Recurs::Parser.rrule(:daily, :count => 10).should == "RRULE:FREQ=DAILY;COUNT=10"
52
+ end
53
+
54
+ it "should create a weekly occurrence on monday and thursday" do
55
+ Recurs::Parser.rrule(:weekly, :by_day => [1, 4]).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
56
+ Recurs::Parser.rrule(:weekly, :by_day => [1, '4']).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
57
+ Recurs::Parser.rrule(:weekly, :by_day => ['MO', 'TH']).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
58
+ Recurs::Parser.rrule(:weekly, :by_day => ['Monday', 'THURSDAY']).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
59
+ Recurs::Parser.rrule(:weekly, :by_day => [1, 'TH']).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
60
+ Recurs::Parser.rrule(:weekly, :by_day => [2, 'TH']).should_not == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
61
+
62
+ #Unique
63
+ Recurs::Parser.rrule(:weekly, :by_day => [1, 1, 4, 'TH', 'Thursday', 'MO']).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TH"
64
+ end
65
+
66
+ it "should create a weekly occurrence for three weeks" do
67
+ Recurs::Parser.rrule(:weekly, :count => 3).should == "RRULE:FREQ=WEEKLY;COUNT=3"
68
+ end
69
+
70
+ it "should create a weekly occurrence for three weeks on fridays" do
71
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => 'FR').should == "RRULE:FREQ=WEEKLY;BYDAY=FR;COUNT=3"
72
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => :friday).should == "RRULE:FREQ=WEEKLY;BYDAY=FR;COUNT=3"
73
+ end
74
+
75
+ it "should create a weekly occurrence for three weeks on fridays and sundays" do
76
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => ['FR', 0]).should == "RRULE:FREQ=WEEKLY;BYDAY=FR,SU;COUNT=3"
77
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => [:friday, 0]).should == "RRULE:FREQ=WEEKLY;BYDAY=FR,SU;COUNT=3"
78
+ end
79
+
80
+ it "should bypass badly input days and use good values" do
81
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => ['fer', 0]).should == "RRULE:FREQ=WEEKLY;BYDAY=SU;COUNT=3"
82
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => [:fer, 0]).should == "RRULE:FREQ=WEEKLY;BYDAY=SU;COUNT=3"
83
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => [:fesefra, :sunday]).should == "RRULE:FREQ=WEEKLY;BYDAY=SU;COUNT=3"
84
+ end
85
+
86
+ it "should occur every month" do
87
+ Recurs::Parser.rrule(:monthly).should == "RRULE:FREQ=MONTHLY"
88
+ end
89
+
90
+ it "should occur every year" do
91
+ Recurs::Parser.rrule(:yearly).should == "RRULE:FREQ=YEARLY"
92
+ end
93
+
94
+ it "should occur every january of each year" do
95
+ Recurs::Parser.rrule(:yearly, :by_month => :january).should == "RRULE:FREQ=YEARLY;BYMONTH=1"
96
+ end
97
+
98
+ it "should occur on every thursday of every janruary of each year" do
99
+ Recurs::Parser.rrule(:yearly, :by_month => :january, :by_day => :thursday).should == "RRULE:FREQ=YEARLY;BYMONTH=1;BYDAY=TH"
100
+ end
101
+
102
+ it "should have independent instance rules" do
103
+ Recurs::Parser.rrule(:yearly, :by_month => :january).should == "RRULE:FREQ=YEARLY;BYMONTH=1"
104
+ Recurs::Parser.rrule(:weekly, :count => 3, :by_day => ['fer', 0]).should == "RRULE:FREQ=WEEKLY;BYDAY=SU;COUNT=3"
105
+ Recurs::Parser.rrule(:yearly, :by_month => :january).should == "RRULE:FREQ=YEARLY;BYMONTH=1"
106
+ end
107
+
108
+ end
109
+
110
+
111
+ =begin
112
+ # A recurrence instance should implement a couple of attributes ( recurs, rrules, rdates, exrules )
113
+
114
+ Recurs::Parser.recurs is a composite method combining all rrules and exrules
115
+ Recurs::Parser.rrules is an array of rrules similarly exrules is the same
116
+
117
+ it "instance should respond to instance methods:" do
118
+ Recurs::Parser = Parser.new
119
+ Recurs::Parser.recurs
120
+ Recurs::Parser.rrules
121
+ end
122
+
123
+ # The Parser Class should implement singular versions ( rrule, exrule ), these methods generate atomic rules
124
+
125
+ The Parser class implements the rrule and exrule methods => alias_method_chain perhaps
126
+ ( these are actually just aliases of the same method but with predefined flags )
127
+
128
+ =end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ describe Recurs::Rules do
3
+ it "should respond to class/module variables" do
4
+ Recurs::Rules.repeat_procs
5
+ Recurs::Rules.schemes
6
+ end
7
+
8
+ it "should build Daily" do
9
+ Recurs::Rules.repeat_procs['Daily'].call(true, :count => 10).should == "RRULE:FREQ=DAILY;COUNT=10"
10
+ end
11
+ #=begin
12
+ it 'Every Weekday ( Mon - Fri )' do
13
+ Recurs::Rules.repeat_procs['Every Weekday ( Mon - Fri )'].call(true, :count => 10).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;COUNT=10"
14
+ end
15
+
16
+ it 'Every Mon, Wed, Fri' do
17
+ Recurs::Rules.repeat_procs['Every Mon, Wed, Fri'].call(true, :count => 10).should == "RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10"
18
+ end
19
+
20
+ it 'Every Tues, Thurs' do
21
+ Recurs::Rules.repeat_procs['Every Tues, Thurs'].call(true, :count => 10).should == "RRULE:FREQ=WEEKLY;BYDAY=TU,TH;COUNT=10"
22
+ end
23
+
24
+ it 'Every Weekend' do
25
+ Recurs::Rules.repeat_procs['Every Weekend'].call(true, :count => 10).should == "RRULE:FREQ=WEEKLY;BYDAY=SU,SA;COUNT=10"
26
+ end
27
+
28
+ it 'Weekly' do
29
+ Recurs::Rules.repeat_procs['Weekly'].call(true, :count => 10).should == "RRULE:FREQ=WEEKLY;COUNT=10"
30
+ end
31
+
32
+ it 'Monthly' do
33
+ Recurs::Rules.repeat_procs['Monthly'].call(true, :count => 10).should == "RRULE:FREQ=MONTHLY;COUNT=10"
34
+ end
35
+
36
+ it 'Yearly' do
37
+ Recurs::Rules.repeat_procs['Yearly'].call(true, :count => 10).should == "RRULE:FREQ=YEARLY;COUNT=10"
38
+ end
39
+ #=end
40
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path('lib/recurs')
2
+ RSpec.configure do |config|
3
+ config.mock_with :rspec
4
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recurs
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Steve Caney Martin
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-14 00:00:00 +00:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: ri_cal
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 8
31
+ - 7
32
+ version: 0.8.7
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: Specifiy you're recurrence pattern in symbols and strings and get an ical format recurrence string
36
+ email:
37
+ - steve@shakewell.co.uk
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - .gitignore
46
+ - Gemfile
47
+ - README
48
+ - Rakefile
49
+ - lib/recurs.rb
50
+ - lib/recurs/consts.rb
51
+ - lib/recurs/rules.rb
52
+ - lib/recurs/version.rb
53
+ - lib/util/module.rb
54
+ - recurs.gemspec
55
+ - spec/models/event_spec.rb
56
+ - spec/models/recurrence_spec.rb
57
+ - spec/models/rules_spec.rb
58
+ - spec/spec_helper.rb
59
+ has_rdoc: true
60
+ homepage: http://rubygems.org/gems/recurs
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options: []
65
+
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirements: []
85
+
86
+ rubyforge_project: recurs
87
+ rubygems_version: 1.3.7
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: A recurrence generator for ical format
91
+ test_files:
92
+ - spec/models/event_spec.rb
93
+ - spec/models/recurrence_spec.rb
94
+ - spec/models/rules_spec.rb
95
+ - spec/spec_helper.rb