recurs 0.0.1

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.
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