google_calendar 0.4.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/google/event.rb CHANGED
@@ -8,13 +8,14 @@ module Google
8
8
  #
9
9
  # === Attributes
10
10
  #
11
- # * +id+ - The google assigned id of the event (nil until saved). Read only.
11
+ # * +id+ - The google assigned id of the event (nil until saved). Read Write.
12
12
  # * +status+ - The status of the event (confirmed, tentative or cancelled). Read only.
13
13
  # * +title+ - The title of the event. Read Write.
14
14
  # * +description+ - The content of the event. Read Write.
15
15
  # * +location+ - The location of the event. Read Write.
16
16
  # * +start_time+ - The start time of the event (Time object, defaults to now). Read Write.
17
17
  # * +end_time+ - The end time of the event (Time object, defaults to one hour from now). Read Write.
18
+ # * +recurrence+ - A hash containing recurrence info for repeating events. Read write.
18
19
  # * +calendar+ - What calendar the event belongs to. Read Write.
19
20
  # * +all_day + - Does the event run all day. Read Write.
20
21
  # * +quickadd+ - A string that Google parses when setting up a new event. If set and then saved it will take priority over any attributes you have set. Read Write.
@@ -24,10 +25,11 @@ module Google
24
25
  # * +duration+ - The duration of the event in seconds. Read only.
25
26
  # * +html_link+ - An absolute link to this event in the Google Calendar Web UI. Read only.
26
27
  # * +raw+ - The full google json representation of the event. Read only.
28
+ # * +visibility+ - The visibility of the event (*'default'*, 'public', 'private', 'confidential'). Read Write.
27
29
  #
28
30
  class Event
29
- attr_reader :id, :raw, :html_link, :status
30
- attr_accessor :title, :location, :calendar, :quickadd, :transparency, :attendees, :description, :reminders
31
+ attr_reader :raw, :html_link, :status
32
+ attr_accessor :id, :title, :location, :calendar, :quickadd, :transparency, :attendees, :description, :reminders, :recurrence, :visibility, :creator_name, :color_id
31
33
 
32
34
  #
33
35
  # Create a new event, and optionally set it's attributes.
@@ -36,25 +38,37 @@ module Google
36
38
  #
37
39
  # event = Google::Event.new
38
40
  # event.calendar = AnInstanceOfGoogleCalendaer
41
+ # event.id = "0123456789abcdefghijklmopqrstuv"
39
42
  # event.start_time = Time.now
40
43
  # event.end_time = Time.now + (60 * 60)
44
+ # event.recurrence = {'freq' => 'monthly'}
41
45
  # event.title = "Go Swimming"
42
46
  # event.description = "The polar bear plunge"
43
47
  # event.location = "In the arctic ocean"
44
48
  # event.transparency = "opaque"
45
- # event.reminders = { 'useDefault' => false, 'overrides' => ['minutes' => 10, 'method' => "popup"]}
49
+ # event.visibility = "public"
50
+ # event.reminders = {'useDefault' => false, 'overrides' => ['minutes' => 10, 'method' => "popup"]}
46
51
  # event.attendees = [
47
52
  # {'email' => 'some.a.one@gmail.com', 'displayName' => 'Some A One', 'responseStatus' => 'tentative'},
48
53
  # {'email' => 'some.b.one@gmail.com', 'displayName' => 'Some B One', 'responseStatus' => 'tentative'}
49
54
  # ]
50
55
  #
51
56
  def initialize(params = {})
52
- [:id, :status, :raw, :html_link, :title, :location, :calendar, :quickadd, :attendees, :description, :reminders, :start_time, :end_time, ].each do |attribute|
57
+ [:id, :status, :raw, :html_link, :title, :location, :calendar, :quickadd, :attendees, :description, :reminders, :recurrence, :start_time, :end_time, :color_id].each do |attribute|
53
58
  instance_variable_set("@#{attribute}", params[attribute])
54
59
  end
55
60
 
61
+ self.visibility = params[:visibility]
56
62
  self.transparency = params[:transparency]
57
63
  self.all_day = params[:all_day] if params[:all_day]
64
+ self.creator_name = params[:creator]['displayName'] if params[:creator]
65
+ end
66
+
67
+ #
68
+ # Sets the id of the Event.
69
+ #
70
+ def id=(id)
71
+ @id = Event.parse_id(id) unless id.nil?
58
72
  end
59
73
 
60
74
  #
@@ -124,21 +138,50 @@ module Google
124
138
  # Stores reminders for this event. Multiple reminders are allowed.
125
139
  #
126
140
  # Examples
127
- #
141
+ #
128
142
  # event = cal.create_event do |e|
129
143
  # e.title = 'Some Event'
130
144
  # e.start_time = Time.now + (60 * 10)
131
145
  # e.end_time = Time.now + (60 * 60) # seconds * min
132
- # e.reminders = { 'useDefault' => false, 'overrides' => [{method: 'email', minutes: 4}, {method: 'popup', minutes: 60}, {method: 'sms', minutes: 30}]}
146
+ # e.reminders = { 'useDefault' => false, 'overrides' => [{method: 'email', minutes: 4}, {method: 'popup', minutes: 60}, {method: 'sms', minutes: 30}]}
133
147
  # end
134
- #
148
+ #
135
149
  # event = Event.new :start_time => "2012-03-31", :end_time => "2012-04-03", :reminders => { 'useDefault' => false, 'overrides' => [{'minutes' => 10, 'method' => "popup"}]}
136
150
  #
137
151
  def reminders
138
152
  @reminders ||= {}
139
153
  end
140
154
 
141
- #
155
+ #
156
+ # Stores recurrence rules for repeating events.
157
+ #
158
+ # Allowed contents:
159
+ # :freq => frequence information ("daily", "weekly", "monthly", "yearly") REQUIRED
160
+ # :count => how many times the repeating event should occur OPTIONAL
161
+ # :until => Time class, until when the event should occur OPTIONAL
162
+ # :interval => how often should the event occur (every "2" weeks, ...) OPTIONAL
163
+ # :byday => if frequence is "weekly", contains ordered (starting with OPTIONAL
164
+ # Sunday)comma separated abbreviations of days the event
165
+ # should occur on ("su,mo,th")
166
+ # if frequence is "monthly", can specify which day of month
167
+ # the event should occur on ("2mo" - second Monday, "-1th" - last Thursday,
168
+ # allowed indices are 1,2,3,4,-1)
169
+ #
170
+ # Note: The hash should not contain :count and :until keys simultaneously.
171
+ #
172
+ # ===== Example
173
+ # event = cal.create_event do |e|
174
+ # e.title = 'Work-day Event'
175
+ # e.start_time = Time.now
176
+ # e.end_time = Time.now + (60 * 60) # seconds * min
177
+ # e.recurrence = {freq: "weekly", byday: "mo,tu,we,th,fr"}
178
+ # end
179
+ #
180
+ def recurrence
181
+ @recurrence ||= {}
182
+ end
183
+
184
+ #
142
185
  # Utility method that simplifies setting the transparency of an event.
143
186
  # You can pass true or false. Defaults to transparent.
144
187
  #
@@ -161,11 +204,22 @@ module Google
161
204
  #
162
205
  # Returns true if the event is opaque otherwise returns false.
163
206
  # Opaque events block time on a calendar.
164
- #
207
+ #
165
208
  def opaque?
166
209
  @transparency == "opaque"
167
210
  end
168
211
 
212
+ #
213
+ # Sets the visibility of the Event.
214
+ #
215
+ def visibility=(val)
216
+ if val
217
+ @visibility = Event.parse_visibility(val)
218
+ else
219
+ @visibility = "default"
220
+ end
221
+ end
222
+
169
223
  #
170
224
  # Convenience method used to build an array of events from a Google feed.
171
225
  #
@@ -180,21 +234,30 @@ module Google
180
234
  def to_json
181
235
  "{
182
236
  \"summary\": \"#{title}\",
183
- \"description\": \"#{description}\",
184
- \"location\": \"#{location}\",
237
+ \"visibility\": \"#{visibility}\",
238
+ \"description\": \"#{description}\",
239
+ \"location\": \"#{location}\",
185
240
  \"start\": {
186
241
  \"dateTime\": \"#{start_time}\"
242
+ #{timezone_needed? ? local_timezone_json : ''}
187
243
  },
188
244
  \"end\": {
189
245
  \"dateTime\": \"#{end_time}\"
246
+ #{timezone_needed? ? local_timezone_json : ''}
190
247
  },
248
+ #{recurrence_json}
249
+ #{color_json}
191
250
  #{attendees_json}
192
251
  \"reminders\": {
193
252
  #{reminders_json}
194
253
  }
195
254
  }"
196
255
  end
197
-
256
+
257
+ def color_json
258
+ return unless color_id
259
+ "\"colorId\": \"#{color_id}\","
260
+ end
198
261
  #
199
262
  # JSON representation of attendees
200
263
  #
@@ -203,7 +266,7 @@ module Google
203
266
 
204
267
  attendees = @attendees.map do |attendee|
205
268
  "{
206
- \"displayName\": \"#{attendee['displayName']}\",
269
+ \"displayName\": \"#{attendee['displayName']}\",
207
270
  \"email\": \"#{attendee['email']}\",
208
271
  \"responseStatus\": \"#{attendee['responseStatus']}\"
209
272
  }"
@@ -216,10 +279,10 @@ module Google
216
279
  # JSON representation of a reminder
217
280
  #
218
281
  def reminders_json
219
- if reminders && reminders.is_a?(Hash) && reminders['overrides']
282
+ if reminders && reminders.is_a?(Hash) && reminders['overrides']
220
283
  overrides = reminders['overrides'].map do |reminder|
221
284
  "{
222
- \"method\": \"#{reminder['method']}\",
285
+ \"method\": \"#{reminder['method']}\",
223
286
  \"minutes\": #{reminder['minutes']}
224
287
  }"
225
288
  end.join(",\n")
@@ -229,11 +292,38 @@ module Google
229
292
  end
230
293
  end
231
294
 
295
+ #
296
+ # Timezone info is needed only at recurring events
297
+ #
298
+ def timezone_needed?
299
+ @recurrence && @recurrence[:freq]
300
+ end
301
+
302
+ #
303
+ # JSON representation of local timezone
304
+ #
305
+ def local_timezone_json
306
+ ",\"timeZone\" : \"#{Time.now.getlocal.zone}\""
307
+ end
308
+
309
+ #
310
+ # JSON representation of recurrence rules for repeating events
311
+ #
312
+ def recurrence_json
313
+ return unless @recurrence && @recurrence[:freq]
314
+
315
+ @recurrence[:until] = @recurrence[:until].strftime('%Y%m%dT%H%M%SZ') if @recurrence[:until]
316
+ rrule = "RRULE:" + @recurrence.collect { |k,v| "#{k}=#{v}" }.join(';').upcase
317
+ @recurrence[:until] = Time.parse(@recurrence[:until]) if @recurrence[:until]
318
+
319
+ "\"recurrence\": [\n\"#{rrule}\"],"
320
+ end
321
+
232
322
  #
233
323
  # String representation of an event object.
234
324
  #
235
325
  def to_s
236
- "Event Id '#{self.id}'\n\tStatus: #{status}\n\tTitle: #{title}\n\tStarts: #{start_time}\n\tEnds: #{end_time}\n\tLocation: #{location}\n\tDescription: #{description}\n\n"
326
+ "Event Id '#{self.id}'\n\tStatus: #{status}\n\tTitle: #{title}\n\tStarts: #{start_time}\n\tEnds: #{end_time}\n\tLocation: #{location}\n\tDescription: #{description}\n\tColor: #{color_id}\n\n"
237
327
  end
238
328
 
239
329
  #
@@ -281,16 +371,34 @@ module Google
281
371
  :title => e['summary'],
282
372
  :description => e['description'],
283
373
  :location => e['location'],
374
+ :creator => e['creator'],
284
375
  :start_time => Event.parse_json_time(e['start']),
285
376
  :end_time => Event.parse_json_time(e['end']),
286
377
  :transparency => e['transparency'],
287
378
  :html_link => e['htmlLink'],
288
379
  :updated => e['updated'],
289
380
  :reminders => e['reminders'],
290
- :attendees => e['attendees'] )
381
+ :attendees => e['attendees'],
382
+ :recurrence => Event.parse_recurrence_rule(e['recurrence']),
383
+ :visibility => e['visibility'],
384
+ :color_id => e['colorId'])
291
385
 
292
386
  end
293
387
 
388
+ #
389
+ # Parse recurrence rule
390
+ # Returns hash with recurrence info
391
+ #
392
+ def self.parse_recurrence_rule(recurrence_entry)
393
+ return {} unless recurrence_entry && recurrence_entry != []
394
+
395
+ rrule = recurrence_entry[0].sub('RRULE:', '')
396
+ rhash = Hash[*rrule.downcase.split(/[=;]/)]
397
+
398
+ rhash[:until] = Time.parse(rhash[:until]) if rhash[:until]
399
+ rhash
400
+ end
401
+
294
402
  #
295
403
  # Set the ID after google assigns it (only necessary when we are creating a new event)
296
404
  #
@@ -308,7 +416,7 @@ module Google
308
416
  Time.parse(time_hash['dateTime']).utc
309
417
  else
310
418
  Time.now.utc
311
- end
419
+ end
312
420
  end
313
421
 
314
422
  #
@@ -319,5 +427,20 @@ module Google
319
427
  (time.is_a? String) ? Time.parse(time) : time.dup.utc
320
428
  end
321
429
 
430
+ #
431
+ # Validates id format
432
+ #
433
+ def self.parse_id(id)
434
+ raise ArgumentError, "Event ID is invalid. Please check Google documentation: https://developers.google.com/google-apps/calendar/v3/reference/events/insert" unless id.gsub(/(^[a-v0-9]{5,1024}$)/o)
435
+ end
436
+
437
+ #
438
+ # Validates visibility value
439
+ #
440
+ def self.parse_visibility(visibility)
441
+ raise ArgumentError, "Event visibility must be 'default', 'public', 'private' or 'confidential'." unless ['default', 'public', 'private', 'confidential'].include?(visibility)
442
+ return visibility
443
+ end
444
+
322
445
  end
323
446
  end
@@ -0,0 +1,76 @@
1
+ require 'time'
2
+ require 'json'
3
+
4
+ module Google
5
+
6
+ #
7
+ # Freebusy returns free/busy information for a set of calendars
8
+ #
9
+ class Freebusy
10
+
11
+ attr_reader :connection
12
+
13
+ #
14
+ # Setup and query the free/busy status of a collection of calendars.
15
+ #
16
+ # The +params+ parameter accepts
17
+ # * :client_id => the client ID that you received from Google after registering your application with them (https://console.developers.google.com/). REQUIRED
18
+ # * :client_secret => the client secret you received from Google after registering your application with them. REQUIRED
19
+ # * :redirect_url => the url where your users will be redirected to after they have successfully permitted access to their calendars. Use 'urn:ietf:wg:oauth:2.0:oob' if you are using an 'application'" REQUIRED
20
+ # * :refresh_token => if a user has already given you access to their calendars, you can specify their refresh token here and you will be 'logged on' automatically (i.e. they don't need to authorize access again). OPTIONAL
21
+ #
22
+ # See Readme.rdoc or readme_code.rb for an explication on the OAuth2 authorization process.
23
+ #
24
+ def initialize(params={}, connection=nil)
25
+ @connection = connection || Connection.new(
26
+ :client_id => params[:client_id],
27
+ :client_secret => params[:client_secret],
28
+ :refresh_token => params[:refresh_token],
29
+ :redirect_url => params[:redirect_url]
30
+ )
31
+ end
32
+
33
+ #
34
+ # Find the busy times of the supplied calendar IDs, within the boundaries
35
+ # of the supplied start_time and end_time
36
+ #
37
+ # The arguments supplied are
38
+ # * calendar_ids => array of Google calendar IDs as strings
39
+ # * start_time => a Time object, the start of the interval for the query.
40
+ # * end_time => a Time object, the end of the interval for the query.
41
+ #
42
+ def query(calendar_ids, start_time, end_time)
43
+ query_content = json_for_query(calendar_ids, start_time, end_time)
44
+ response = @connection.send("/freeBusy", :post, query_content)
45
+
46
+ return nil if response.status != 200 || response.body.empty?
47
+
48
+ parse_freebusy_response(response.body)
49
+ end
50
+
51
+ private
52
+
53
+ #
54
+ # Prepare the JSON
55
+ #
56
+ def json_for_query(calendar_ids, start_time, end_time)
57
+ {}.tap{ |obj|
58
+ obj[:items] = calendar_ids.map {|id| Hash[:id, id] }
59
+ obj[:timeMin] = start_time.utc.iso8601
60
+ obj[:timeMax] = end_time.utc.iso8601
61
+ }.to_json
62
+ end
63
+
64
+ def parse_freebusy_response(response_body)
65
+ query_result = JSON.parse(response_body)
66
+
67
+ return nil unless query_result['calendars'].is_a? Hash
68
+
69
+ query_result['calendars'].each_with_object({}) do |(calendar_id, value), result|
70
+ result[calendar_id] = value['busy'] || []
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -1,6 +1,9 @@
1
1
  module Google
2
2
  require 'google/errors'
3
3
  require 'google/calendar'
4
+ require 'google/calendar_list'
5
+ require 'google/calendar_list_entry'
4
6
  require 'google/connection'
5
7
  require 'google/event'
8
+ require 'google/freebusy'
6
9
  end
data/readme_code.rb CHANGED
@@ -54,6 +54,7 @@ puts event
54
54
  event = cal.find_or_create_event_by_id(event.id) do |e|
55
55
  e.title = 'An Updated Cool Event'
56
56
  e.end_time = Time.now + (60 * 60 * 2) # seconds * min * hours
57
+ e.color_id = 3 # google allows colors 0-11
57
58
  end
58
59
 
59
60
  puts event
data/test/helper.rb CHANGED
@@ -14,7 +14,7 @@ rescue Bundler::BundlerError => e
14
14
  end
15
15
 
16
16
  require "minitest/autorun"
17
- require 'minitest/reporters'
17
+ require 'minitest/reporters'
18
18
  require 'shoulda/context'
19
19
  require 'mocha/setup'
20
20
  require 'faraday'
@@ -27,4 +27,4 @@ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
27
27
 
28
28
  class Minitest::Test
29
29
  @@mock_path = File.expand_path(File.join(File.dirname(__FILE__), 'mocks'))
30
- end
30
+ end
@@ -0,0 +1,69 @@
1
+ {
2
+ "kind": "calendar#calendarList",
3
+ "etag": "\"1420633154361000\"",
4
+ "nextSyncToken": "00001420633154361000",
5
+ "items": [
6
+ {
7
+ "kind": "calendar#calendarListEntry",
8
+ "etag": "\"1420564622795000\"",
9
+ "id": "initech.com_ed493d0a9b46ea46c3a0d48611ce@resource.calendar.google.com",
10
+ "summary": "Small cubicle",
11
+ "timeZone": "America/Los_Angeles",
12
+ "colorId": "2",
13
+ "backgroundColor": "#d06b64",
14
+ "foregroundColor": "#000000",
15
+ "accessRole": "owner",
16
+ "defaultReminders": []
17
+ },
18
+ {
19
+ "kind": "calendar#calendarListEntry",
20
+ "etag": "\"1420564132697000\"",
21
+ "id": "initech.com_db18a4e59c230a5cc5d2b069a30f@resource.calendar.google.com",
22
+ "summary": "Large cubicle",
23
+ "timeZone": "America/Los_Angeles",
24
+ "colorId": "3",
25
+ "backgroundColor": "#f83a22",
26
+ "foregroundColor": "#000000",
27
+ "accessRole": "reader",
28
+ "defaultReminders": []
29
+ },
30
+ {
31
+ "kind": "calendar#calendarListEntry",
32
+ "etag": "\"1420564622222000\"",
33
+ "id": "bob@initech.com",
34
+ "summary": "Bob's Calendar",
35
+ "timeZone": "Europe/London",
36
+ "colorId": "17",
37
+ "backgroundColor": "#9a9cff",
38
+ "foregroundColor": "#000000",
39
+ "accessRole": "owner",
40
+ "defaultReminders": [
41
+ {
42
+ "method": "popup",
43
+ "minutes": 10
44
+ }
45
+ ],
46
+ "notificationSettings": {
47
+ "notifications": [
48
+ {
49
+ "type": "eventCreation",
50
+ "method": "email"
51
+ },
52
+ {
53
+ "type": "eventChange",
54
+ "method": "email"
55
+ },
56
+ {
57
+ "type": "eventCancellation",
58
+ "method": "email"
59
+ },
60
+ {
61
+ "type": "eventResponse",
62
+ "method": "email"
63
+ }
64
+ ]
65
+ },
66
+ "primary": true
67
+ }
68
+ ]
69
+ }