gcal4ruby 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.
- data/README +83 -0
- data/lib/gcal4ruby/base.rb +256 -0
- data/lib/gcal4ruby/calendar.rb +297 -0
- data/lib/gcal4ruby/event.rb +403 -0
- data/lib/gcal4ruby/recurrence.rb +164 -0
- data/lib/gcal4ruby/service.rb +61 -0
- data/lib/gcal4ruby.rb +1 -0
- data/test/unit.rb +120 -0
- metadata +59 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
require 'gcal4ruby/recurrence'
|
2
|
+
|
3
|
+
module GCal4Ruby
|
4
|
+
#The Event Class represents a remote event in calendar.
|
5
|
+
#
|
6
|
+
#=Usage
|
7
|
+
#All usages assume a successfully authenticated Service and valid Calendar.
|
8
|
+
#1. Create a new Event
|
9
|
+
# event = Event.new(calendar)
|
10
|
+
# event.title = "Soccer Game"
|
11
|
+
# event.start = Time.parse("12-06-2009 at 12:30 PM")
|
12
|
+
# event.end = Time.parse("12-06-2009 at 1:30 PM")
|
13
|
+
# event.where = "Merry Playfields"
|
14
|
+
# event.save
|
15
|
+
#
|
16
|
+
#2. Find an existing Event
|
17
|
+
# event = Event.find(cal, "Soccer Game", :first)
|
18
|
+
#
|
19
|
+
#3. Find all events containing the search term
|
20
|
+
# event = Event.find(cal, "Soccer Game")
|
21
|
+
#
|
22
|
+
#4. Create a recurring event for every saturday
|
23
|
+
# event = Event.new(calendar)
|
24
|
+
# event.title = "Baseball Game"
|
25
|
+
# event.where = "Municipal Stadium"
|
26
|
+
# event.recurrence = Recurrence.new
|
27
|
+
# event.recurrence.start = Time.parse("13-06-2009 at 4:30 PM")
|
28
|
+
# event.recurrence.end = Time.parse("13-06-2009 at 6:30 PM")
|
29
|
+
# event.recurrence.frequency = {"weekly" => ["SA"]}
|
30
|
+
# event.save
|
31
|
+
#
|
32
|
+
#5. Create an event with a 15 minute email reminder
|
33
|
+
# event = Event.new(calendar)
|
34
|
+
# event.title = "Dinner with Kate"
|
35
|
+
# event.start = Time.parse("20-06-2009 at 5 pm")
|
36
|
+
# event.end = Time.parse("20-06-209 at 8 pm")
|
37
|
+
# event.where = "Luigi's"
|
38
|
+
# event.reminder = {:minutes => 15, :method => 'email'}
|
39
|
+
# event.save
|
40
|
+
#
|
41
|
+
#6. Create an event with attendees
|
42
|
+
# event = Event.new(calendar)
|
43
|
+
# event.title = "Dinner with Kate"
|
44
|
+
# event.start = Time.parse("20-06-2009 at 5 pm")
|
45
|
+
# event.end = Time.parse("20-06-209 at 8 pm")
|
46
|
+
# event.attendees => {:name => "Kate", :email => "kate@gmail.com"}
|
47
|
+
# event.save
|
48
|
+
#
|
49
|
+
#After an event object has been created or loaded, you can change any of the
|
50
|
+
#attributes like you would any other object. Be sure to save the event to write changes
|
51
|
+
#to the Google Calendar service.
|
52
|
+
class Event
|
53
|
+
#The event title
|
54
|
+
attr_accessor :title
|
55
|
+
#The content for the event
|
56
|
+
attr_accessor :content
|
57
|
+
#The location of the event
|
58
|
+
attr_accessor :where
|
59
|
+
#A flag for whether the event show as :free or :busy
|
60
|
+
attr_accessor :transparency
|
61
|
+
#A flag indicating the status of the event. Values can be :confirmed, :tentative or :cancelled
|
62
|
+
attr_accessor :status
|
63
|
+
#The unique event ID
|
64
|
+
attr_accessor :id
|
65
|
+
|
66
|
+
@attendees
|
67
|
+
|
68
|
+
#The event start time
|
69
|
+
attr_reader :start
|
70
|
+
#The event end time
|
71
|
+
attr_reader :end
|
72
|
+
#The reminder settings for the event, returned as a hash
|
73
|
+
attr_reader :reminder
|
74
|
+
|
75
|
+
#Sets the reminder options for the event. Parameter must be a hash containing one of
|
76
|
+
#:hours, :minutes and :days, which are simply the number of each before the event start date you'd like to
|
77
|
+
#receive the reminder.
|
78
|
+
#
|
79
|
+
#:method can be one of the following:
|
80
|
+
#- <b>'alert'</b>: causes an alert to appear when a user is viewing the calendar in a browser
|
81
|
+
#- <b>'email'</b>: sends the user an email message
|
82
|
+
def reminder=(r)
|
83
|
+
@reminder = r
|
84
|
+
end
|
85
|
+
|
86
|
+
#Returns the current event's Recurrence information
|
87
|
+
def recurrence
|
88
|
+
@recurrence
|
89
|
+
end
|
90
|
+
|
91
|
+
#Returns an array of the current attendees
|
92
|
+
def attendees
|
93
|
+
@attendees
|
94
|
+
end
|
95
|
+
|
96
|
+
#Accepts an array of email address/name pairs for attendees.
|
97
|
+
# [{:name => 'Mike Reich', :email => 'mike@seabourneconsulting.com'}]
|
98
|
+
#The email address is requried, but the name is optional
|
99
|
+
def attendees=(a)
|
100
|
+
if a.is_a?(Array)
|
101
|
+
@attendees = a
|
102
|
+
else
|
103
|
+
raise "Attendees must be an Array of email/name hash pairs"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
#Sets the event's recurrence information to a Recurrence object. Returns the recurrence if successful,
|
108
|
+
#false otherwise
|
109
|
+
def recurrence=(r)
|
110
|
+
if r.is_a?(Recurrence)
|
111
|
+
r.event = self
|
112
|
+
@recurrence = r
|
113
|
+
else
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#Returns a duplicate of the current event as a new Event object
|
119
|
+
def copy()
|
120
|
+
e = Event.new()
|
121
|
+
e.load(to_xml)
|
122
|
+
e.calendar = @calendar
|
123
|
+
return e
|
124
|
+
end
|
125
|
+
|
126
|
+
#Sets the start time of the Event. Must be a Time object or a parsable string representation
|
127
|
+
#of a time.
|
128
|
+
def start=(str)
|
129
|
+
if str.class == String
|
130
|
+
@start = Time.parse(str)
|
131
|
+
elsif str.class == Time
|
132
|
+
@start = str
|
133
|
+
else
|
134
|
+
raise "Start Time must be either Time or String"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
#Sets the end time of the Event. Must be a Time object or a parsable string representation
|
139
|
+
#of a time.
|
140
|
+
def end=(str)
|
141
|
+
if str.class == String
|
142
|
+
@end = Time.parse(str)
|
143
|
+
elsif str.class == Time
|
144
|
+
@end = str
|
145
|
+
else
|
146
|
+
raise "End Time must be either Time or String"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
#Deletes the event from the Google Calendar Service. All values are cleared.
|
151
|
+
def delete
|
152
|
+
if @exists
|
153
|
+
if @calendar.service.send_delete(@edit_feed, {"If-Match" => @etag})
|
154
|
+
@exists = false
|
155
|
+
@deleted = true
|
156
|
+
@title = nil
|
157
|
+
@content = nil
|
158
|
+
@id = nil
|
159
|
+
@start = nil
|
160
|
+
@end = nil
|
161
|
+
@transparency = nil
|
162
|
+
@status = nil
|
163
|
+
@where = nil
|
164
|
+
return true
|
165
|
+
else
|
166
|
+
return false
|
167
|
+
end
|
168
|
+
else
|
169
|
+
return false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
#Creates a new Event. Accepts a valid Calendar object.
|
174
|
+
def initialize(calendar)
|
175
|
+
super()
|
176
|
+
@xml = EVENT_XML
|
177
|
+
@calendar = calendar
|
178
|
+
@title = nil
|
179
|
+
@content = nil
|
180
|
+
@start = nil
|
181
|
+
@end = nil
|
182
|
+
@where = nil
|
183
|
+
@transparency = "http://schemas.google.com/g/2005#event.opaque"
|
184
|
+
@status = "http://schemas.google.com/g/2005#event.confirmed"
|
185
|
+
@attendees = []
|
186
|
+
@reminder = nil
|
187
|
+
end
|
188
|
+
|
189
|
+
#If the event does not exist on the Google Calendar service, save creates it. Otherwise
|
190
|
+
#updates the existing event data. Returns true on success, false otherwise.
|
191
|
+
def save
|
192
|
+
if @deleted
|
193
|
+
return false
|
194
|
+
end
|
195
|
+
if @exists
|
196
|
+
ret = @calendar.service.send_put(@edit_feed, to_xml, {'Content-Type' => 'application/atom+xml', "If-Match" => @etag})
|
197
|
+
else
|
198
|
+
ret = @calendar.service.send_post(@calendar.event_feed, to_xml, {'Content-Type' => 'application/atom+xml'})
|
199
|
+
end
|
200
|
+
if !@exists
|
201
|
+
if load(ret.read_body)
|
202
|
+
return true
|
203
|
+
else
|
204
|
+
raise EventSaveFailed
|
205
|
+
end
|
206
|
+
end
|
207
|
+
return true
|
208
|
+
end
|
209
|
+
|
210
|
+
#Returns an XML representation of the event.
|
211
|
+
def to_xml()
|
212
|
+
xml = REXML::Document.new(@xml)
|
213
|
+
xml.root.elements.each(){}.map do |ele|
|
214
|
+
case ele.name
|
215
|
+
when 'id'
|
216
|
+
ele.text = @id
|
217
|
+
when "title"
|
218
|
+
ele.text = @title
|
219
|
+
when "content"
|
220
|
+
ele.text = @content
|
221
|
+
when "when"
|
222
|
+
if not @recurrence
|
223
|
+
ele.attributes["startTime"] = @start.xmlschema
|
224
|
+
ele.attributes["endTime"] = @end.xmlschema
|
225
|
+
set_reminder(ele)
|
226
|
+
else
|
227
|
+
if not @reminder
|
228
|
+
xml.root.delete_element("/entry/gd:when")
|
229
|
+
xml.root.add_element("gd:recurrence").text = @recurrence.to_s
|
230
|
+
else
|
231
|
+
ele.delete_attribute('startTime')
|
232
|
+
ele.delete_attribute('startTime')
|
233
|
+
set_reminder(ele)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
when "eventStatus"
|
237
|
+
ele.attributes["value"] = case @status
|
238
|
+
when :confirmed
|
239
|
+
"http://schemas.google.com/g/2005#event.confirmed"
|
240
|
+
when :tentative
|
241
|
+
"http://schemas.google.com/g/2005#event.tentative"
|
242
|
+
when :cancelled
|
243
|
+
"http://schemas.google.com/g/2005#event.canceled"
|
244
|
+
else
|
245
|
+
"http://schemas.google.com/g/2005#event.confirmed"
|
246
|
+
end
|
247
|
+
when "transparency"
|
248
|
+
ele.attributes["value"] = case @transparency
|
249
|
+
when :free
|
250
|
+
"http://schemas.google.com/g/2005#event.transparent"
|
251
|
+
when :busy
|
252
|
+
"http://schemas.google.com/g/2005#event.opaque"
|
253
|
+
else
|
254
|
+
"http://schemas.google.com/g/2005#event.opaque"
|
255
|
+
end
|
256
|
+
when "where"
|
257
|
+
ele.attributes["valueString"] = @where
|
258
|
+
end
|
259
|
+
end
|
260
|
+
if not @attendees.empty?
|
261
|
+
@attendees.each do |a|
|
262
|
+
xml.root.add_element("gd:who", {"email" => a[:email], "valueString" => a[:name], "rel" => "http://schemas.google.com/g/2005#event.attendee"})
|
263
|
+
end
|
264
|
+
end
|
265
|
+
xml.to_s
|
266
|
+
end
|
267
|
+
|
268
|
+
#Loads the event info from an XML string.
|
269
|
+
def load(string)
|
270
|
+
@xml = string
|
271
|
+
@exists = true
|
272
|
+
xml = REXML::Document.new(string)
|
273
|
+
@etag = xml.root.attributes['etag']
|
274
|
+
xml.root.elements.each(){}.map do |ele|
|
275
|
+
case ele.name
|
276
|
+
when 'id'
|
277
|
+
@id, @edit_feed = ele.text
|
278
|
+
when 'title'
|
279
|
+
@title = ele.text
|
280
|
+
when 'content'
|
281
|
+
@content = ele.text
|
282
|
+
when "when"
|
283
|
+
@start = Time.parse(ele.attributes['startTime'])
|
284
|
+
@end = Time.parse(ele.attributes['endTime'])
|
285
|
+
ele.elements.each("gd:reminder") do |r|
|
286
|
+
@reminder = {:minutes => r.attributes['minutes'] ? r.attributes['minutes'] : 0, :hours => r.attributes['hours'] ? r.attributes['hours'] : 0, :days => r.attributes['days'] ? r.attributes['days'] : 0, :method => r.attributes['method'] ? r.attributes['method'] : ''}
|
287
|
+
end
|
288
|
+
when "where"
|
289
|
+
@where = ele.attributes['valueString']
|
290
|
+
when "link"
|
291
|
+
if ele.attributes['rel'] == 'edit'
|
292
|
+
@edit_feed = ele.attributes['href']
|
293
|
+
end
|
294
|
+
when "who"
|
295
|
+
if ele.attributes['rel'] == "http://schemas.google.com/g/2005#event.attendee"
|
296
|
+
n = {}
|
297
|
+
ele.attributes.each do |name, value|
|
298
|
+
case name
|
299
|
+
when "email"
|
300
|
+
n[:email] = value
|
301
|
+
when "valueString"
|
302
|
+
n[:name] = value
|
303
|
+
end
|
304
|
+
end
|
305
|
+
@attendees << n
|
306
|
+
end
|
307
|
+
when "eventStatus"
|
308
|
+
case ele.attributes["value"]
|
309
|
+
when "http://schemas.google.com/g/2005#event.confirmed"
|
310
|
+
@status = :confirmed
|
311
|
+
when "http://schemas.google.com/g/2005#event.tentative"
|
312
|
+
@status = :tentative
|
313
|
+
when "http://schemas.google.com/g/2005#event.confirmed"
|
314
|
+
@status = :cancelled
|
315
|
+
end
|
316
|
+
when "transparency"
|
317
|
+
case ele.attributes["value"]
|
318
|
+
when "http://schemas.google.com/g/2005#event.transparent"
|
319
|
+
@transparency = :free
|
320
|
+
when "http://schemas.google.com/g/2005#event.opaque"
|
321
|
+
@transparency = :busy
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
#Reloads the event data from the Google Calendar Service. Returns true if successful,
|
328
|
+
#false otherwise.
|
329
|
+
def reload
|
330
|
+
t = Event.find(@calendar, @id, :first)
|
331
|
+
if t
|
332
|
+
if load(t.xml)
|
333
|
+
return true
|
334
|
+
else
|
335
|
+
return false
|
336
|
+
end
|
337
|
+
else
|
338
|
+
return false
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
#Finds the event that matches search_term in title or description full text search. The scope parameter can
|
343
|
+
#be either :all to return an array of all matches, or :first to return the first match as an Event.
|
344
|
+
def self.find(calendar, search_term, scope = :all)
|
345
|
+
events = calendar.service.send_get("http://www.google.com/calendar/feeds/#{calendar.id}/private/full?q="+CGI.escape(search_term))
|
346
|
+
ret = []
|
347
|
+
REXML::Document.new(events.read_body).root.elements.each("entry"){}.map do |entry|
|
348
|
+
entry.attributes["xmlns:gCal"] = "http://schemas.google.com/gCal/2005"
|
349
|
+
entry.attributes["xmlns:gd"] = "http://schemas.google.com/g/2005"
|
350
|
+
entry.attributes["xmlns"] = "http://www.w3.org/2005/Atom"
|
351
|
+
event = Event.new(calendar)
|
352
|
+
event.load("<?xml version='1.0' encoding='UTF-8'?>#{entry.to_s}")
|
353
|
+
ret << event
|
354
|
+
end
|
355
|
+
if scope == :all
|
356
|
+
return ret
|
357
|
+
elsif scope == :first
|
358
|
+
return ret[0]
|
359
|
+
end
|
360
|
+
return false
|
361
|
+
end
|
362
|
+
|
363
|
+
#Returns true if the event exists on the Google Calendar Service.
|
364
|
+
def exists?
|
365
|
+
return @exists
|
366
|
+
end
|
367
|
+
|
368
|
+
private
|
369
|
+
@exists = false
|
370
|
+
@calendar = nil
|
371
|
+
@xml = nil
|
372
|
+
@etag = nil
|
373
|
+
@recurrence = nil
|
374
|
+
@deleted = false
|
375
|
+
@edit_feed = ''
|
376
|
+
|
377
|
+
def set_reminder(ele)
|
378
|
+
ele.delete_element("gd:reminder")
|
379
|
+
if @reminder
|
380
|
+
e = ele.add_element("gd:reminder")
|
381
|
+
used = false
|
382
|
+
if @reminder[:minutes]
|
383
|
+
e.attributes['minutes'] = @reminder[:minutes]
|
384
|
+
used = true
|
385
|
+
elsif @reminder[:hours] and not used
|
386
|
+
e.attributes['hours'] = @reminder[:hours]
|
387
|
+
used = true
|
388
|
+
elsif @reminder[:days] and not used
|
389
|
+
e.attributes['days'] = @reminder[:days]
|
390
|
+
end
|
391
|
+
if @reminder[:method]
|
392
|
+
e.attributes['method'] = @reminder[:method]
|
393
|
+
else
|
394
|
+
e.attributes['method'] = 'email'
|
395
|
+
end
|
396
|
+
else
|
397
|
+
ele.delete_element("gd:reminder")
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
403
|
+
|
@@ -0,0 +1,164 @@
|
|
1
|
+
class Time
|
2
|
+
#Returns a ISO 8601 complete formatted string of the time
|
3
|
+
def complete
|
4
|
+
self.utc.strftime("%Y%m%dT%H%M%S")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module GCal4Ruby
|
9
|
+
#The Recurrence class stores information on an Event's recurrence. The class implements
|
10
|
+
#the RFC 2445 iCalendar recurrence description.
|
11
|
+
class Recurrence
|
12
|
+
#The event start date/time
|
13
|
+
attr_reader :start
|
14
|
+
#The event end date/time
|
15
|
+
attr_reader :end
|
16
|
+
#the event reference
|
17
|
+
attr_reader :event
|
18
|
+
#The date until which the event will be repeated
|
19
|
+
attr_reader :repeat_until
|
20
|
+
#The event frequency
|
21
|
+
attr_reader :frequency
|
22
|
+
#True if the event is all day (i.e. no start/end time)
|
23
|
+
attr_accessor :all_day
|
24
|
+
|
25
|
+
#Returns a new Recurrence object
|
26
|
+
def initialize
|
27
|
+
@start = nil
|
28
|
+
@end = nil
|
29
|
+
@event = nil
|
30
|
+
@day_of_week = nil
|
31
|
+
@repeat_until = nil
|
32
|
+
@frequency = nil
|
33
|
+
@all_day = false
|
34
|
+
end
|
35
|
+
|
36
|
+
#Returns a string with the correctly formatted ISO 8601 recurrence rule
|
37
|
+
def to_s
|
38
|
+
|
39
|
+
output = ''
|
40
|
+
if @all_day
|
41
|
+
output += "DTSTART;VALUE=DATE:#{@start.utc.strftime("%Y%m%d")}\n"
|
42
|
+
else
|
43
|
+
output += "DTSTART;VALUE=DATE-TIME:#{@start.complete}\n"
|
44
|
+
end
|
45
|
+
if @all_day
|
46
|
+
output += "DTEND;VALUE=DATE:#{@end.utc.strftime("%Y%m%d")}\n"
|
47
|
+
else
|
48
|
+
output += "DTEND;VALUE=DATE-TIME:#{@end.complete}\n"
|
49
|
+
end
|
50
|
+
output += "RRULE:"
|
51
|
+
if @frequency
|
52
|
+
f = 'FREQ='
|
53
|
+
i = ''
|
54
|
+
by = ''
|
55
|
+
@frequency.each do |key, v|
|
56
|
+
if v.is_a?(Array)
|
57
|
+
if v.size > 0
|
58
|
+
value = v.join(",")
|
59
|
+
else
|
60
|
+
value = nil
|
61
|
+
end
|
62
|
+
else
|
63
|
+
value = v
|
64
|
+
end
|
65
|
+
f += "#{key.upcase};" if key != 'interval'
|
66
|
+
case key.downcase
|
67
|
+
when "secondly"
|
68
|
+
by += "BYSECOND=#{value};"
|
69
|
+
when "minutely"
|
70
|
+
by += "BYMINUTE=#{value};"
|
71
|
+
when "hourly"
|
72
|
+
by += "BYHOUR=#{value};"
|
73
|
+
when "weekly"
|
74
|
+
by += "BYDAY=#{value};" if value
|
75
|
+
when "monthly"
|
76
|
+
by += "BYDAY=#{value};"
|
77
|
+
when "yearly"
|
78
|
+
by += "BYYEARDAY=#{value}"
|
79
|
+
when 'interval'
|
80
|
+
i += "INTERVAL=#{value};"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
output += f+i+by
|
84
|
+
end
|
85
|
+
if @repeat_until
|
86
|
+
output += ";UNTIL=#{@repeat_until.strftime("%Y%m%d")}"
|
87
|
+
end
|
88
|
+
|
89
|
+
output += "\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
#Sets the start date/time. Must be a Time object.
|
93
|
+
def start=(s)
|
94
|
+
if not s.is_a?(Time)
|
95
|
+
raise RecurrenceValueError, "Start must be a date or a time"
|
96
|
+
else
|
97
|
+
@start = s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
#Sets the end Date/Time. Must be a Time object.
|
102
|
+
def end=(e)
|
103
|
+
if not e.is_a?(Time)
|
104
|
+
raise RecurrenceValueError, "End must be a date or a time"
|
105
|
+
else
|
106
|
+
@end = e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
#Sets the parent event reference
|
111
|
+
def event=(e)
|
112
|
+
if not e.is_a?(Event)
|
113
|
+
raise RecurrenceValueError, "Event must be an event"
|
114
|
+
else
|
115
|
+
@event = e
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#Sets the end date for the recurrence
|
120
|
+
def repeat_until=(r)
|
121
|
+
if not r.is_a?(Date)
|
122
|
+
raise RecurrenceValueError, "Repeat_until must be a date"
|
123
|
+
else
|
124
|
+
@repeat_until = r
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
#Sets the frequency of the recurrence. Should be a hash with one of
|
129
|
+
#"SECONDLY", "MINUTELY", "HOURLY", "DAILY", "WEEKLY", "MONTHLY", "YEARLY" as the key,
|
130
|
+
#and as the value, an array containing zero to n of the following:
|
131
|
+
#- *Secondly*: A value between 0 and 59. Causes the event to repeat on that second of each minut.
|
132
|
+
#- *Minutely*: A value between 0 and 59. Causes the event to repeat on that minute of every hour.
|
133
|
+
#- *Hourly*: A value between 0 and 23. Causes the even to repeat on that hour of every day.
|
134
|
+
#- *Daily*: No value needed - will cause the event to repeat every day until the repeat_until date.
|
135
|
+
#- *Weekly*: A value of the first two letters of a day of the week. Causes the event to repeat on that day.
|
136
|
+
#- *Monthly*: A value of a positive or negative integer (i.e. +1) prepended to a day-of-week string ('TU') to indicate the position of the day within the month. E.g. +1TU would be the first tuesday of the month.
|
137
|
+
#- *Yearly*: A value of 1 to 366 indicating the day of the year. May be negative to indicate counting down from the last day of the year.
|
138
|
+
#
|
139
|
+
#Optionally, you may specific a second hash pair to set the internal the even repeats:
|
140
|
+
# "interval" => '2'
|
141
|
+
#If the interval is missing, it is assumed to be 1.
|
142
|
+
#
|
143
|
+
#===Examples
|
144
|
+
#Repeat event every Tuesday:
|
145
|
+
# frequency = {"Weekly" => ["TU"]}
|
146
|
+
#
|
147
|
+
#Repeat every first and third Monday of the month
|
148
|
+
# frequency = {"Monthly" => ["+1MO", "+3MO"]}
|
149
|
+
#
|
150
|
+
#Repeat on the last day of every year
|
151
|
+
# frequency = {"Yearly" => [366]}
|
152
|
+
#
|
153
|
+
#Repeat every other week on Friday
|
154
|
+
# frequency = {"Weekly" => ["FR"], "interval" => "2"}
|
155
|
+
|
156
|
+
def frequency=(f)
|
157
|
+
if f.is_a?(Hash)
|
158
|
+
@frequency = f
|
159
|
+
else
|
160
|
+
raise RecurrenceValueError, "Frequency must be a hash (see documentation)"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'gcal4ruby/base'
|
2
|
+
require 'gcal4ruby/calendar'
|
3
|
+
|
4
|
+
module GCal4Ruby
|
5
|
+
|
6
|
+
#The service class is the main handler for all direct interactions with the
|
7
|
+
#Google Calendar API. A service represents a single user account. Each user
|
8
|
+
#account can have multiple calendars, so you'll need to find the calendar you
|
9
|
+
#want from the service, using the Calendar#find class method.
|
10
|
+
#=Usage
|
11
|
+
#
|
12
|
+
#1. Authenticate
|
13
|
+
# service = Service.new
|
14
|
+
# service.authenticate("user@gmail.com", "password")
|
15
|
+
#
|
16
|
+
#2. Get Calendar List
|
17
|
+
# calendars = service.calendars
|
18
|
+
#
|
19
|
+
|
20
|
+
class Service < Base
|
21
|
+
#Convenience attribute contains the currently authenticated account name
|
22
|
+
attr_reader :account
|
23
|
+
|
24
|
+
# The token returned by the Google servers, used to authorize all subsequent messages
|
25
|
+
attr_reader :auth_token
|
26
|
+
|
27
|
+
# The authenticate method passes the username and password to google servers.
|
28
|
+
# If authentication succeeds, returns true, otherwise raises the AuthenticationFailed error.
|
29
|
+
def authenticate(username, password)
|
30
|
+
ret = nil
|
31
|
+
ret = send_post(AUTH_URL, "Email=#{username}&Passwd=#{password}&source=GCal4Ruby&service=cl")
|
32
|
+
if ret.class == Net::HTTPOK
|
33
|
+
@auth_token = ret.read_body.to_a[2].gsub("Auth=", "").strip
|
34
|
+
@account = username
|
35
|
+
return true
|
36
|
+
else
|
37
|
+
raise AuthenticationFailed
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#Returns an array of Calendar objects for each calendar associated with
|
42
|
+
#the authenticated account.
|
43
|
+
def calendars
|
44
|
+
if not @auth_token
|
45
|
+
raise NotAuthenticated
|
46
|
+
end
|
47
|
+
ret = send_get(CALENDAR_LIST_FEED)
|
48
|
+
cals = []
|
49
|
+
REXML::Document.new(ret.body).root.elements.each("entry"){}.map do |entry|
|
50
|
+
entry.attributes["xmlns:gCal"] = "http://schemas.google.com/gCal/2005"
|
51
|
+
entry.attributes["xmlns:gd"] = "http://schemas.google.com/g/2005"
|
52
|
+
entry.attributes["xmlns"] = "http://www.w3.org/2005/Atom"
|
53
|
+
cal = Calendar.new(self)
|
54
|
+
cal.load("<?xml version='1.0' encoding='UTF-8'?>#{entry.to_s}")
|
55
|
+
cals << cal
|
56
|
+
end
|
57
|
+
return cals
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/lib/gcal4ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "gcal4ruby/service"
|