google_calendar 0.5 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac5c278ba3dc50398192d02b52e133f0dbf7394d
4
- data.tar.gz: 69d8905b95b054d7c373243f4237ce9dfc434c1e
3
+ metadata.gz: fc05a032b27f92d14375477f9f114fb4b57aafca
4
+ data.tar.gz: 7068f8e73c52864a6e0d77e26553a8a0c7e00a23
5
5
  SHA512:
6
- metadata.gz: a5237336f30472193452d47bd2690b04ea6c574f86eafa2add99b97ad1ee6f90a489066fac7c47f18e4a630262847c14e44ce2713d764bd5f3a3b2508def7740
7
- data.tar.gz: e54a2cb56293fd44585e15da46e236e0eef390d7cdff5efe23418be2bc096b1f471740f663cd3dddf9b22e620df65ee9241a752a9f57e12310843088baa904a8
6
+ metadata.gz: 21c6b93987a1c3a0f4f3a4a8dbd9cd8cdecaa36bb5168f796999f0567ebf0d43ef4b03490413debca6b2b01b1bcf34163047cee5a822af7fdaae46724ffc6c81
7
+ data.tar.gz: e159da8702d8621b4c0f2b434f64b9989a45a1d912e29d8000444b67de86613e9924f6b697aea58d1bfdeeca2b4a6834c4ec8d884a1a1a48a8fe6761e9d8fd6b
data/.gitignore CHANGED
@@ -34,4 +34,5 @@ tmtags
34
34
  # For vim:
35
35
  #*.swp
36
36
  notes.md
37
- test_harness.rb
37
+ test_harness.rb
38
+ *.gem
@@ -1,46 +1,51 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- google_calendar (0.5)
4
+ google_calendar (0.5.1)
5
+ TimezoneParser (~> 0.2.0)
5
6
  json (~> 1.8)
6
7
  signet (~> 0.6)
7
8
 
8
9
  GEM
9
10
  remote: http://rubygems.org/
10
11
  specs:
12
+ TimezoneParser (0.2.0)
13
+ insensitive_hash
14
+ tzinfo
11
15
  addressable (2.3.8)
12
16
  ansi (1.5.0)
13
17
  builder (3.2.2)
14
- codeclimate-test-reporter (0.4.7)
18
+ codeclimate-test-reporter (0.4.8)
15
19
  simplecov (>= 0.7.1, < 1.0.0)
16
20
  docile (1.1.5)
17
21
  extlib (0.9.16)
18
22
  faraday (0.9.1)
19
23
  multipart-post (>= 1.2, < 3)
20
- json (1.8.2)
21
- jwt (1.5.0)
24
+ insensitive_hash (0.3.3)
25
+ json (1.8.3)
26
+ jwt (1.5.1)
22
27
  metaclass (0.0.4)
23
- minitest (5.6.1)
24
- minitest-reporters (1.0.16)
28
+ minitest (5.8.1)
29
+ minitest-reporters (1.1.2)
25
30
  ansi
26
31
  builder
27
32
  minitest (>= 5.0)
28
33
  ruby-progressbar
29
34
  mocha (1.1.0)
30
35
  metaclass (~> 0.0.1)
31
- multi_json (1.11.0)
36
+ multi_json (1.11.2)
32
37
  multipart-post (2.0.0)
33
38
  rake (10.4.2)
34
- rb-fsevent (0.9.5)
39
+ rb-fsevent (0.9.6)
35
40
  rdoc (4.2.0)
36
41
  json (~> 1.4)
37
42
  ruby-progressbar (1.7.5)
38
43
  shoulda-context (1.2.1)
39
- signet (0.6.0)
44
+ signet (0.6.1)
40
45
  addressable (~> 2.3)
41
46
  extlib (~> 0.9)
42
47
  faraday (~> 0.9)
43
- jwt (~> 1.0)
48
+ jwt (~> 1.5)
44
49
  multi_json (~> 1.10)
45
50
  simplecov (0.10.0)
46
51
  docile (~> 1.1.0)
@@ -48,6 +53,9 @@ GEM
48
53
  simplecov-html (~> 0.10.0)
49
54
  simplecov-html (0.10.0)
50
55
  terminal-notifier-guard (1.6.4)
56
+ thread_safe (0.3.5)
57
+ tzinfo (1.2.2)
58
+ thread_safe (~> 0.1)
51
59
 
52
60
  PLATFORMS
53
61
  ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5
1
+ 0.5.1
@@ -2,8 +2,8 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "google_calendar"
5
- s.version = "0.5"
6
- s.date = "2014-11-30"
5
+ s.version = "0.5.1"
6
+ s.date = "2015-10-05"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
9
 
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
 
29
29
  s.add_runtime_dependency(%q<signet>, ["~> 0.6"])
30
30
  s.add_runtime_dependency(%q<json>, ["~> 1.8"])
31
+ s.add_runtime_dependency(%q<TimezoneParser>, ["~> 0.2.0"])
31
32
 
32
33
  s.add_development_dependency(%q<terminal-notifier-guard>, ["~> 1.6"])
33
34
  s.add_development_dependency(%q<rb-fsevent>, ["~> 0.9"])
@@ -31,13 +31,40 @@ module Google
31
31
  :client_id => params[:client_id],
32
32
  :client_secret => params[:client_secret],
33
33
  :refresh_token => params[:refresh_token],
34
- :redirect_url => params[:redirect_url]
34
+ :redirect_url => params[:redirect_url],
35
+ :state => params[:state]
35
36
  )
36
37
 
37
38
  @id = params[:calendar]
38
39
  # raise CalendarIDMissing unless @id
39
40
  end
40
41
 
42
+ #
43
+ # Setup, connect and create a Google Calendar.
44
+ # the +params+ paramater accepts
45
+ # * :client_id => the client ID that you received from Google after registering your application with them (https://console.developers.google.com/). REQUIRED
46
+ # * :client_secret => the client secret you received from Google after registering your application with them. REQUIRED
47
+ # * :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
48
+ # * :summary => title of the calendar being created.
49
+ # * :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
50
+ #
51
+ # See Readme.rdoc or readme_code.rb for an explication on the OAuth2 authorization process.
52
+ #
53
+ # ==== Example
54
+ # Google::Calendar.create(
55
+ # :client_id => YOUR_CLIENT_ID,
56
+ # :client_secret => YOUR_SECRET,
57
+ # :summary => 'Test Calendar',
58
+ # :redirect_url => "urn:ietf:wg:oauth:2.0:oob" # this is what Google uses for 'applications'
59
+ # )
60
+ #
61
+ def self.create(params={}, connection=nil)
62
+ cal = new(params, connection)
63
+ cal.instance_variable_set(:@summary, params[:summary])
64
+
65
+ cal.save
66
+ end
67
+
41
68
  #
42
69
  # The URL you need to send a user in order to let them grant you access to their calendars.
43
70
  #
@@ -80,6 +107,16 @@ module Google
80
107
  @connection.login_with_refresh_token(refresh_token)
81
108
  end
82
109
 
110
+ #
111
+ # Save a new calender.
112
+ # Returns:
113
+ # the calendar that was saved.
114
+ #
115
+ def save
116
+ response = send_calendar_request("/", :post, {:summary => @summary}.to_json)
117
+ update_after_save(response)
118
+ end
119
+
83
120
  #
84
121
  # Find all of the events associated with this calendar.
85
122
  # Returns:
@@ -97,6 +134,11 @@ module Google
97
134
  # search (i.e. title), by default it searches everything.
98
135
  # If you would like to find specific attribute value (i.e. title=Picnic), run a query
99
136
  # and parse the results.
137
+ #
138
+ # Note that it is not possible to query the extended properties using queries.
139
+ # If you need to do so, use the alternate methods find_events_by_extended_property
140
+ # and find_events_by_extended_property_in_range
141
+ #
100
142
  # Returns:
101
143
  # an empty array if nothing found.
102
144
  # an array with one element if only one found.
@@ -147,6 +189,70 @@ module Google
147
189
  event_lookup(query)
148
190
  end
149
191
 
192
+ #
193
+ # Find all events that match at least one of the specified extended properties.
194
+ #
195
+ # the +extended_properties+ parameter is set up the same way that it is configured when creating an event
196
+ # for example, providing the following hash { 'shared' => {'p1' => 'v1', 'p2' => v2} } will return the list of events
197
+ # that contain either v1 for shared extended property p1 or v2 for p2.
198
+ #
199
+ # the +options+ parameter accepts
200
+ # :max_results => the maximum number of results to return defaults to 25 the largest number Google accepts is 2500
201
+ # :order_by => how you would like the results ordered, can be either 'startTime' or 'updated'. Defaults to 'startTime'. Note: it must be 'updated' if expand_recurring_events is set to false.
202
+ # :expand_recurring_events => When set to true each instance of a recurring event is returned. Defaults to true.
203
+ #
204
+ # Returns:
205
+ # an empty array if nothing found.
206
+ # an array with one element if only one found.
207
+ # an array of events if many found.
208
+ #
209
+ def find_events_by_extended_properties(extended_properties, options = {})
210
+ query_parts = []
211
+ ['shared', 'private'].each do |prop_type|
212
+ if extended_properties[prop_type]
213
+ query_parts << extended_properties[prop_type].map do |key, value|
214
+ (prop_type == "shared" ? "sharedExtendedProperty=" : "privateExtendedProperty=") + "#{key}%3D#{value}"
215
+ end.join("&")
216
+ end
217
+ end
218
+ query = "?" + query_parts.join('&') + parse_options(options)
219
+ event_lookup(query)
220
+ end
221
+
222
+ #
223
+ # Find all events that match at least one of the specified extended properties within a given time frame.
224
+ # The lower bound is inclusive, whereas the upper bound is exclusive.
225
+ # Events that overlap the range are included.
226
+ #
227
+ # the +extended_properties+ parameter is set up the same way that it is configured when creating an event
228
+ # for example, providing the following hash { 'shared' => {'p1' => 'v1', 'p2' => v2} } will return the list of events
229
+ # that contain either v1 for shared extended property p1 or v2 for p2.
230
+ #
231
+ # the +options+ parameter accepts
232
+ # :max_results => the maximum number of results to return defaults to 25 the largest number Google accepts is 2500
233
+ # :order_by => how you would like the results ordered, can be either 'startTime' or 'updated'. Defaults to 'startTime'. Note: it must be 'updated' if expand_recurring_events is set to false.
234
+ # :expand_recurring_events => When set to true each instance of a recurring event is returned. Defaults to true.
235
+ #
236
+ # Returns:
237
+ # an empty array if nothing found.
238
+ # an array with one element if only one found.
239
+ # an array of events if many found.
240
+ #
241
+ def find_events_by_extended_properties_in_range(extended_properties, start_min, start_max, options = {})
242
+ query_parts = []
243
+ ['shared', 'private'].each do |prop_type|
244
+ if extended_properties[prop_type]
245
+ query_parts << extended_properties[prop_type].map do |key, value|
246
+ (prop_type == "shared" ? "sharedExtendedProperty=" : "privateExtendedProperty=") + "#{key}%3D#{value}"
247
+ end.join("&")
248
+ end
249
+ end
250
+ formatted_start_min = encode_time(start_min)
251
+ formatted_start_max = encode_time(start_max)
252
+ query = "?" + query_parts.join('&') + (query_parts.length > 0 ? '&':'') + "timeMin=#{formatted_start_min}&timeMax=#{formatted_start_max}#{parse_options(options)}"
253
+ event_lookup(query)
254
+ end
255
+
150
256
  #
151
257
  # Attempts to find the event specified by the id
152
258
  # Returns:
@@ -155,7 +261,7 @@ module Google
155
261
  # an array of events if many found.
156
262
  #
157
263
  def find_event_by_id(id)
158
- return nil unless id
264
+ return nil unless id
159
265
  event_lookup("/#{id}")
160
266
  end
161
267
 
@@ -201,7 +307,6 @@ module Google
201
307
  def save_event(event)
202
308
  method = event.new_event? ? :post : :put
203
309
  body = event.use_quickadd? ? nil : event.to_json
204
-
205
310
  query_string = if event.use_quickadd?
206
311
  "/quickAdd?text=#{event.title}"
207
312
  elsif event.new_event?
@@ -223,6 +328,18 @@ module Google
223
328
 
224
329
  protected
225
330
 
331
+ #
332
+ # Set the ID after google assigns it (only necessary when we are creating a new event)
333
+ #
334
+ def update_after_save(response) #:nodoc:
335
+ return if @id && @id != ''
336
+ @raw = JSON.parse(response.body)
337
+ @id = @raw['id']
338
+ @html_link = @raw['htmlLink']
339
+
340
+ self
341
+ end
342
+
226
343
  #
227
344
  # Utility method used to centralize the parsing of common query parameters.
228
345
  #
@@ -252,7 +369,7 @@ module Google
252
369
  return events if events.empty?
253
370
  events.length > 1 ? events : [events[0]]
254
371
  rescue Google::HTTPNotFound
255
- return nil
372
+ return []
256
373
  end
257
374
  end
258
375
 
@@ -268,6 +385,13 @@ module Google
268
385
  event
269
386
  end
270
387
 
388
+ #
389
+ # Wraps the `send` method. Send a calendar related request to Google.
390
+ #
391
+ def send_calendar_request(path_and_query_string, method, content = '')
392
+ @connection.send("/calendars#{path_and_query_string}", method, content)
393
+ end
394
+
271
395
  #
272
396
  # Wraps the `send` method. Send an event related request to Google.
273
397
  #
@@ -40,6 +40,7 @@ module Google
40
40
  :client_secret => params[:client_secret],
41
41
  :redirect_uri => params[:redirect_url],
42
42
  :refresh_token => params[:refresh_token],
43
+ :state => params[:state],
43
44
  :authorization_uri => AUTH_URI,
44
45
  :token_credential_uri => TOKEN_URI,
45
46
  :scope => SCOPE
@@ -110,7 +111,6 @@ module Google
110
111
  def send(path, method, content = '')
111
112
 
112
113
  uri = BASE_URI + path
113
-
114
114
  response = @client.fetch_protected_resource(
115
115
  :uri => uri,
116
116
  :method => method,
@@ -1,5 +1,6 @@
1
1
  require 'time'
2
2
  require 'json'
3
+ require 'timezone_parser'
3
4
 
4
5
  module Google
5
6
 
@@ -26,10 +27,11 @@ module Google
26
27
  # * +html_link+ - An absolute link to this event in the Google Calendar Web UI. Read only.
27
28
  # * +raw+ - The full google json representation of the event. Read only.
28
29
  # * +visibility+ - The visibility of the event (*'default'*, 'public', 'private', 'confidential'). Read Write.
30
+ # * +extended_properties - Custom properties which may be shared or private. Read Write
29
31
  #
30
32
  class Event
31
33
  attr_reader :raw, :html_link, :status
32
- attr_accessor :id, :title, :location, :calendar, :quickadd, :transparency, :attendees, :description, :reminders, :recurrence, :visibility, :creator_name, :color_id
34
+ attr_accessor :id, :title, :location, :calendar, :quickadd, :transparency, :attendees, :description, :reminders, :recurrence, :visibility, :creator_name, :color_id, :extended_properties
33
35
 
34
36
  #
35
37
  # Create a new event, and optionally set it's attributes.
@@ -52,9 +54,10 @@ module Google
52
54
  # {'email' => 'some.a.one@gmail.com', 'displayName' => 'Some A One', 'responseStatus' => 'tentative'},
53
55
  # {'email' => 'some.b.one@gmail.com', 'displayName' => 'Some B One', 'responseStatus' => 'tentative'}
54
56
  # ]
57
+ # event.extendedProperties = {'shared' => {'custom_str' => 'some custom string'}}
55
58
  #
56
59
  def initialize(params = {})
57
- [:id, :status, :raw, :html_link, :title, :location, :calendar, :quickadd, :attendees, :description, :reminders, :recurrence, :start_time, :end_time, :color_id].each do |attribute|
60
+ [:id, :status, :raw, :html_link, :title, :location, :calendar, :quickadd, :attendees, :description, :reminders, :recurrence, :start_time, :end_time, :color_id, :extended_properties].each do |attribute|
58
61
  instance_variable_set("@#{attribute}", params[attribute])
59
62
  end
60
63
 
@@ -181,6 +184,27 @@ module Google
181
184
  @recurrence ||= {}
182
185
  end
183
186
 
187
+ #
188
+ # Stores custom data within extended properties which can be shared or private.
189
+ #
190
+ # Allowed contents:
191
+ # :private => a hash containing custom key/values (strings) private to the event OPTIONAL
192
+ # :shared => a hash containing custom key/values (strings) shared with others OPTIONAL
193
+ #
194
+ # Note: Both private and shared can be specified at once
195
+ #
196
+ # ===== Example
197
+ # event = cal.create_event do |e|
198
+ # e.title = 'Work-day Event'
199
+ # e.start_time = Time.now
200
+ # e.end_time = Time.now + (60 * 60) # seconds * min
201
+ # e.extended_properties = {'shared' => {'prop1' => 'value 1'}}
202
+ # end
203
+ #
204
+ def extended_properties
205
+ @extended_properties ||= {}
206
+ end
207
+
184
208
  #
185
209
  # Utility method that simplifies setting the transparency of an event.
186
210
  # You can pass true or false. Defaults to transparent.
@@ -216,7 +240,7 @@ module Google
216
240
  if val
217
241
  @visibility = Event.parse_visibility(val)
218
242
  else
219
- @visibility = "default"
243
+ @visibility = "default"
220
244
  end
221
245
  end
222
246
 
@@ -248,6 +272,7 @@ module Google
248
272
  #{recurrence_json}
249
273
  #{color_json}
250
274
  #{attendees_json}
275
+ #{extended_properties_json}
251
276
  \"reminders\": {
252
277
  #{reminders_json}
253
278
  }
@@ -303,7 +328,9 @@ module Google
303
328
  # JSON representation of local timezone
304
329
  #
305
330
  def local_timezone_json
306
- ",\"timeZone\" : \"#{Time.now.getlocal.zone}\""
331
+ tz = Time.now.getlocal.zone
332
+ tz_name = TimezoneParser::getTimezones(tz).last
333
+ ",\"timeZone\" : \"#{tz_name}\""
307
334
  end
308
335
 
309
336
  #
@@ -319,6 +346,28 @@ module Google
319
346
  "\"recurrence\": [\n\"#{rrule}\"],"
320
347
  end
321
348
 
349
+ #
350
+ # JSON representation of extended properties
351
+ # shared : whether this should handle shared or public properties
352
+ #
353
+ def extended_properties_json
354
+ return unless @extended_properties && (@extended_properties['shared'] || @extended_properties['private'])
355
+ json_extended_properties = []
356
+ ['shared', 'private'].each do |prop_type|
357
+ if @extended_properties[prop_type]
358
+ props_json = @extended_properties[prop_type].map do |key, value|
359
+ "\"#{key}\": \"#{value}\""
360
+ end.join(",\n")
361
+ json_extended_properties << "\"#{prop_type}\": {\n
362
+ #{props_json}
363
+ }\n"
364
+ end
365
+ end
366
+ "\n\"extendedProperties\": {\n
367
+ #{json_extended_properties.join(',')}\n
368
+ },\n"
369
+ end
370
+
322
371
  #
323
372
  # String representation of an event object.
324
373
  #
@@ -381,7 +430,8 @@ module Google
381
430
  :attendees => e['attendees'],
382
431
  :recurrence => Event.parse_recurrence_rule(e['recurrence']),
383
432
  :visibility => e['visibility'],
384
- :color_id => e['colorId'])
433
+ :color_id => e['colorId'],
434
+ :extended_properties => e['extendedProperties'])
385
435
 
386
436
  end
387
437
 
@@ -402,14 +452,16 @@ module Google
402
452
  #
403
453
  # Set the ID after google assigns it (only necessary when we are creating a new event)
404
454
  #
405
- def update_after_save(respose) #:nodoc:
455
+ def update_after_save(response) #:nodoc:
406
456
  return if @id && @id != ''
407
- @raw = JSON.parse(respose.body)
457
+ @raw = JSON.parse(response.body)
408
458
  @id = @raw['id']
409
459
  @html_link = @raw['htmlLink']
410
460
  end
411
461
 
412
462
  def self.parse_json_time(time_hash)
463
+ return nil unless time_hash
464
+
413
465
  if time_hash['date']
414
466
  Time.parse(time_hash['date']).utc
415
467
  elsif time_hash['dateTime']
@@ -431,14 +483,14 @@ module Google
431
483
  # Validates id format
432
484
  #
433
485
  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)
486
+ 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
487
  end
436
488
 
437
489
  #
438
490
  # Validates visibility value
439
491
  #
440
492
  def self.parse_visibility(visibility)
441
- raise ArgumentError, "Event visibility must be 'default', 'public', 'private' or 'confidential'." unless ['default', 'public', 'private', 'confidential'].include?(visibility)
493
+ raise ArgumentError, "Event visibility must be 'default', 'public', 'private' or 'confidential'." unless ['default', 'public', 'private', 'confidential'].include?(visibility)
442
494
  return visibility
443
495
  end
444
496