podio 0.6.0 → 0.7.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.
Files changed (37) hide show
  1. data/.travis.yml +4 -0
  2. data/LICENSE +1 -1
  3. data/lib/podio.rb +7 -3
  4. data/lib/podio/active_podio/base.rb +121 -54
  5. data/lib/podio/client.rb +15 -2
  6. data/lib/podio/error.rb +1 -0
  7. data/lib/podio/middleware/error_response.rb +2 -0
  8. data/lib/podio/models/app_store_share.rb +8 -4
  9. data/lib/podio/models/application.rb +4 -0
  10. data/lib/podio/models/{calendar.rb → calendar_event.rb} +13 -1
  11. data/lib/podio/models/contact.rb +1 -0
  12. data/lib/podio/models/contract.rb +17 -1
  13. data/lib/podio/models/email_subscription_setting.rb +1 -0
  14. data/lib/podio/models/external_file.rb +30 -0
  15. data/lib/podio/models/file_attachment.rb +30 -12
  16. data/lib/podio/models/filter.rb +33 -2
  17. data/lib/podio/models/item.rb +23 -0
  18. data/lib/podio/models/item_field.rb +1 -1
  19. data/lib/podio/models/linked_account.rb +32 -0
  20. data/lib/podio/models/meeting.rb +55 -30
  21. data/lib/podio/models/meeting_participiant.rb +10 -4
  22. data/lib/podio/models/notification_group.rb +3 -4
  23. data/lib/podio/models/organization.rb +11 -9
  24. data/lib/podio/models/organization_member.rb +2 -1
  25. data/lib/podio/models/organization_profile.rb +1 -0
  26. data/lib/podio/models/question_answer.rb +3 -2
  27. data/lib/podio/models/referral.rb +25 -0
  28. data/lib/podio/models/reminder.rb +2 -2
  29. data/lib/podio/models/search.rb +34 -5
  30. data/lib/podio/models/space_invitation.rb +9 -4
  31. data/lib/podio/models/space_member.rb +8 -0
  32. data/lib/podio/models/subscription.rb +7 -3
  33. data/lib/podio/models/user_status.rb +2 -1
  34. data/lib/podio/models/widget.rb +2 -0
  35. data/lib/podio/version.rb +1 -1
  36. data/test/active_podio_test.rb +2 -2
  37. metadata +14 -11
@@ -7,3 +7,7 @@ rvm:
7
7
  notifications:
8
8
  email:
9
9
  - fabricius@podio.com
10
+ - auchenberg@podio.com
11
+ - haugstrup@podio.com
12
+ - daniel@podio.com
13
+ - munz@podio.com
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Florian Munz
1
+ Copyright (c) 2010-2011 Podio
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -54,18 +54,19 @@ module Podio
54
54
  end
55
55
  end
56
56
 
57
- class OAuthToken < Struct.new(:access_token, :refresh_token, :expires_at, :reference, :refreshed)
57
+ class OAuthToken < Struct.new(:access_token, :refresh_token, :expires_at, :expires_in, :reference, :refreshed)
58
58
  def initialize(params = {})
59
59
  self.access_token = params['access_token']
60
60
  self.refresh_token = params['refresh_token']
61
61
  self.reference = params['ref']
62
62
  self.expires_at = Time.now + params['expires_in'] if params['expires_in']
63
+ self.expires_in = params['expires_in'] if params['expires_in']
63
64
  end
64
65
  end
65
66
 
66
67
  autoload :Client, 'podio/client'
67
68
  autoload :ResponseWrapper, 'podio/response_wrapper'
68
-
69
+
69
70
  autoload :Action, 'podio/models/action'
70
71
  autoload :ActivationStatus, 'podio/models/activation_status'
71
72
  autoload :Activity, 'podio/models/activity'
@@ -75,7 +76,7 @@ module Podio
75
76
  autoload :ApplicationField, 'podio/models/application_field'
76
77
  autoload :Bulletin, 'podio/models/bulletin'
77
78
  autoload :ByLine, 'podio/models/by_line'
78
- autoload :Calendar, 'podio/models/calendar'
79
+ autoload :CalendarEvent, 'podio/models/calendar_event'
79
80
  autoload :CalendarMute, 'podio/models/calendar_mute'
80
81
  autoload :Category, 'podio/models/category'
81
82
  autoload :Comment, 'podio/models/comment'
@@ -89,6 +90,7 @@ module Podio
89
90
  autoload :ConversationParticipant, 'podio/models/conversation_participant'
90
91
  autoload :EmailSubscriptionSetting, 'podio/models/email_subscription_setting'
91
92
  autoload :Embed, 'podio/models/embed'
93
+ autoload :ExternalFile, 'podio/models/external_file'
92
94
  autoload :FileAttachment, 'podio/models/file_attachment'
93
95
  autoload :Filter, 'podio/models/filter'
94
96
  autoload :Form, 'podio/models/form'
@@ -99,6 +101,7 @@ module Podio
99
101
  autoload :ItemDiff, 'podio/models/item_diff'
100
102
  autoload :ItemField, 'podio/models/item_field'
101
103
  autoload :ItemRevision, 'podio/models/item_revision'
104
+ autoload :LinkedAccount, 'podio/models/linked_account'
102
105
  autoload :Meeting, 'podio/models/meeting'
103
106
  autoload :MeetingParticipant, 'podio/models/meeting_participiant'
104
107
  autoload :News, 'podio/models/news'
@@ -116,6 +119,7 @@ module Podio
116
119
  autoload :QuestionOption, 'podio/models/question_option'
117
120
  autoload :Rating, 'podio/models/rating'
118
121
  autoload :Recurrence, 'podio/models/recurrence'
122
+ autoload :Referral, 'podio/models/referral'
119
123
  autoload :Reminder, 'podio/models/reminder'
120
124
  autoload :Search, 'podio/models/search'
121
125
  autoload :Space, 'podio/models/space'
@@ -5,8 +5,8 @@ module ActivePodio
5
5
  class Base
6
6
  extend ActiveModel::Naming, ActiveModel::Callbacks
7
7
  include ActiveModel::Conversion
8
-
9
- class_inheritable_accessor :valid_attributes, :associations
8
+
9
+ class_attribute :valid_attributes, :_associations, :_properties
10
10
  attr_accessor :attributes, :error_code, :error_message, :error_parameters, :error_propagate
11
11
  alias_method :propagate_error?, :error_propagate
12
12
 
@@ -16,12 +16,12 @@ module ActivePodio
16
16
  self.attributes = Hash[*self.valid_attributes.collect { |n| [n.to_sym, nil] }.flatten].merge(attributes.symbolize_keys)
17
17
 
18
18
  @values_from_api = options[:values_from_api] # Used to determine if date times should be converted from local to utc, or are already utc
19
-
19
+
20
20
  attributes.each do |key, value|
21
21
  if self.respond_to?("#{key}=".to_sym)
22
22
  self.send("#{key}=".to_sym, value)
23
23
  else
24
- is_association_hash = value.is_a?(Hash) && self.associations.present? && self.associations.has_key?(key.to_sym) && self.associations[key.to_sym] == :has_one && self.send(key.to_sym).respond_to?(:attributes)
24
+ is_association_hash = value.is_a?(Hash) && self._associations.present? && self._associations.has_key?(key.to_sym) && self._associations[key.to_sym] == :has_one && self.send(key.to_sym).respond_to?(:attributes)
25
25
  if valid_attributes.include?(key.to_sym) || is_association_hash
26
26
  # Initialize nested object to get correctly casted values set back, unless the given values are all blank
27
27
  if is_association_hash
@@ -40,26 +40,26 @@ module ActivePodio
40
40
  @belongs_to = options[:belongs_to] # Allows has_one associations to communicate their changed content back to their parent model
41
41
  @values_from_api = false
42
42
  end
43
-
43
+
44
44
  def persisted?
45
45
  ! self.new_record?
46
46
  end
47
-
47
+
48
48
  def new_record?
49
49
  ! (self.respond_to?(:id) && self.id.present?)
50
50
  end
51
-
51
+
52
52
  def to_param
53
53
  local_id = self.id if self.respond_to?(:id)
54
54
  local_id = nil if local_id == self.object_id # Id still returns object_id in Ruby 1.8.7, JRuby and Rubinius
55
55
  local_id.try(:to_s)
56
56
  end
57
-
57
+
58
58
  def [](attribute)
59
59
  @attributes ||= {}
60
60
  @attributes[attribute.to_sym]
61
61
  end
62
-
62
+
63
63
  def []=(attribute, value)
64
64
  @attributes ||= {}
65
65
  @attributes[attribute.to_sym] = value
@@ -68,22 +68,58 @@ module ActivePodio
68
68
  @belongs_to[:model][@belongs_to[:as]][attribute.to_sym] = value
69
69
  end
70
70
  end
71
-
71
+
72
72
  def ==(other)
73
73
  !self.nil? && !other.nil? && self.respond_to?(:id) && other.respond_to?(:id) && self.id == other.id
74
74
  end
75
75
  alias :eql? :==
76
-
76
+
77
77
  def hash
78
78
  self.id.hash if self.respond_to?(:id)
79
79
  end
80
-
80
+
81
81
  def as_json(options={})
82
- self.attributes
82
+ options ||= {}
83
+ result = {}
84
+ result.merge!(:id => self.id) if self.respond_to?(:id)
85
+
86
+ if options[:formatted]
87
+ self.valid_attributes.each do |name|
88
+ result[name] = json_friendly_value(self.send(name), options)
89
+ end
90
+
91
+ unless options[:nested] == false
92
+ if self._associations.respond_to?(:each)
93
+ self._associations.each do |name, type|
94
+ case type
95
+ when :has_one
96
+ result[name] = self.send(name).as_json(options.except(:methods))
97
+ when :has_many
98
+ result[name] = self.send(name).collect { |assoc| assoc.as_json(options.except(:methods)) }
99
+ end
100
+ end
101
+ end
102
+ end
103
+ else
104
+ result.merge!(self.attributes)
105
+ end
106
+
107
+ if options[:methods]
108
+ options[:methods].each do |name|
109
+ result[name] = json_friendly_value(self.send(name), options)
110
+ end
111
+ end
112
+
113
+ result
114
+ end
115
+
116
+ # Override this in models where the class name doesn't match the ref type
117
+ def api_friendly_ref_type
118
+ self.class.name.downcase
83
119
  end
84
-
120
+
85
121
  private
86
-
122
+
87
123
  def klass_for_association(options)
88
124
  klass_name = options[:class]
89
125
  raise "Missing class name of associated model. Provide with :class => 'MyClass'." unless klass_name.present?
@@ -95,7 +131,7 @@ module ActivePodio
95
131
  end
96
132
  return klass
97
133
  end
98
-
134
+
99
135
  def any_values_present_recursive?(values)
100
136
  values.any? do |value|
101
137
  if value.respond_to?(:values)
@@ -106,15 +142,28 @@ module ActivePodio
106
142
  end
107
143
  end
108
144
 
145
+ def json_friendly_value(ruby_value, options)
146
+ if options[:formatted]
147
+ json_value = case ruby_value.class.name
148
+ when "DateTime", "Time"
149
+ ruby_value.iso8601
150
+ else
151
+ ruby_value
152
+ end
153
+ else
154
+ ruby_value
155
+ end
156
+ end
157
+
109
158
  class << self
110
-
159
+
111
160
  public
112
-
161
+
113
162
  # Defines the the supported attributes of the model
114
163
  def property(name, type = :string, options = {})
115
164
  self.valid_attributes ||= []
116
165
  self.valid_attributes << name
117
-
166
+
118
167
  case type
119
168
  when :datetime
120
169
  define_datetime_accessor(name, options)
@@ -131,11 +180,11 @@ module ActivePodio
131
180
  define_generic_accessor(name)
132
181
  end
133
182
  end
134
-
183
+
135
184
  # Wraps a single hash provided from the API in the given model
136
185
  def has_one(name, options = {})
137
- self.associations ||= {}
138
- self.associations[name] = :has_one
186
+ self._associations ||= {}
187
+ self._associations[name] = :has_one
139
188
 
140
189
  self.send(:define_method, name) do
141
190
  klass = klass_for_association(options)
@@ -151,12 +200,16 @@ module ActivePodio
151
200
  end
152
201
  instance
153
202
  end
203
+
204
+ self.send(:define_method, "clear_#{name}") do
205
+ self.instance_variable_set("@#{name}_has_one_instance", nil)
206
+ end
154
207
  end
155
-
208
+
156
209
  # Wraps a collection of hashes from the API to a collection of the given model
157
210
  def has_many(name, options = {})
158
- self.associations ||= {}
159
- self.associations[name] = :has_many
211
+ self._associations ||= {}
212
+ self._associations[name] = :has_many
160
213
 
161
214
  self.send(:define_method, name) do
162
215
  klass = klass_for_association(options)
@@ -172,23 +225,23 @@ module ActivePodio
172
225
  end
173
226
  instances
174
227
  end
175
-
228
+
176
229
  self.send(:define_method, "#{name}?") do
177
230
  self.send(name).length > 0
178
231
  end
179
232
  end
180
-
233
+
181
234
  # Returns a single instance of the model
182
235
  def member(response)
183
236
  new(response, :values_from_api => true)
184
237
  end
185
-
238
+
186
239
  # Returns a simple collection model instances
187
240
  def list(response)
188
241
  response.map! { |item| new(item, :values_from_api => true) }
189
242
  response
190
243
  end
191
-
244
+
192
245
  # Returns a struct that includes:
193
246
  # * all: A collection model instances
194
247
  # * count: The number of returned records
@@ -198,7 +251,7 @@ module ActivePodio
198
251
  result.all.map! { |item| new(item, :values_from_api => true) }
199
252
  result
200
253
  end
201
-
254
+
202
255
  def delegate_to_hash(hash_name, *attribute_names)
203
256
  options = attribute_names.extract_options!
204
257
  options.reverse_merge!(:prefix => false, :setter => false)
@@ -218,7 +271,7 @@ module ActivePodio
218
271
  end
219
272
  end
220
273
  end
221
-
274
+
222
275
  # Wraps the given methods in a begin/rescue block
223
276
  # If no error occurs, the return value of the method, or true if nil is returned, is returned
224
277
  # If a Podio::PodioError occurs, the method returns false and the error can be read from the error_message accessor
@@ -237,7 +290,7 @@ module ActivePodio
237
290
  parameters = ex.response_body["error_parameters"]
238
291
  propagate = ex.response_body["error_propagate"]
239
292
  end
240
-
293
+
241
294
  if success
242
295
  return result || true
243
296
  else
@@ -248,43 +301,46 @@ module ActivePodio
248
301
  return false
249
302
  end
250
303
  end
251
-
304
+
252
305
  alias_method_chain method_name, :api_errors_handled
253
306
  end
254
307
  end
255
-
308
+
256
309
  private
257
-
310
+
258
311
  def define_generic_accessor(name, options = {})
259
312
  options.reverse_merge!(:getter => true, :setter => true)
260
-
313
+
261
314
  if(options[:getter])
262
315
  self.send(:define_method, name) do
263
316
  self[name.to_sym]
264
317
  end
265
318
  end
266
-
319
+
267
320
  if(options[:setter])
268
321
  self.send(:define_method, "#{name}=") do |value|
269
322
  self[name.to_sym] = value
270
323
  end
271
324
  end
272
325
  end
273
-
326
+
274
327
  def define_datetime_accessor(name, options = {})
275
328
  self.send(:define_method, name) do
276
329
  options[:convert_timezone] == false ? self[name.to_sym].try(:to_datetime) : self[name.to_sym].try(:to_datetime).try(:in_time_zone)
277
330
  end
278
-
331
+
279
332
  self.send(:define_method, "#{name}=") do |value|
280
-
333
+
281
334
  # TODO: This should eventually be done on all date times
282
335
  # This option is a temporary fix while API transitions to UTC only
283
336
  if options[:convert_incoming_local_datetime_to_utc] && value.present? && !@values_from_api
284
- value = value.try(:to_datetime) unless value.is_a?(DateTime)
285
- value = Time.zone.local_to_utc(value)
337
+ value = if value.is_a?(DateTime)
338
+ Time.zone.local_to_utc(value)
339
+ else
340
+ Time.zone.parse(value).try(:utc).try(:to_datetime)
341
+ end
286
342
  end
287
-
343
+
288
344
  self[name.to_sym] = if value.is_a?(DateTime)
289
345
  value.try(:to_s, :db)
290
346
  else
@@ -292,12 +348,12 @@ module ActivePodio
292
348
  end
293
349
  end
294
350
  end
295
-
351
+
296
352
  def define_date_accessor(name)
297
353
  self.send(:define_method, name) do
298
354
  self[name.to_sym].try(:to_date)
299
355
  end
300
-
356
+
301
357
  self.send(:define_method, "#{name}=") do |value|
302
358
  self[name.to_sym] = if value.is_a?(Date)
303
359
  value.try(:to_s, :db)
@@ -312,7 +368,7 @@ module ActivePodio
312
368
  end
313
369
  end
314
370
  end
315
-
371
+
316
372
  def define_integer_accessor(name)
317
373
  self.send(:define_method, name) do
318
374
  if self[name.to_sym].present?
@@ -321,7 +377,7 @@ module ActivePodio
321
377
  nil
322
378
  end
323
379
  end
324
-
380
+
325
381
  self.send(:define_method, "#{name}=") do |value|
326
382
  if value.present?
327
383
  self[name.to_sym] = value.to_i
@@ -330,7 +386,7 @@ module ActivePodio
330
386
  end
331
387
  end
332
388
  end
333
-
389
+
334
390
  def define_boolean_accessors(name)
335
391
  self.send(:define_method, "#{name}?") do
336
392
  if self[name.to_sym].present?
@@ -339,7 +395,7 @@ module ActivePodio
339
395
  nil
340
396
  end
341
397
  end
342
-
398
+
343
399
  self.send(:define_method, "#{name}=") do |value|
344
400
  if value == true || value == false
345
401
  self[name.to_sym] = value
@@ -350,29 +406,40 @@ module ActivePodio
350
406
  end
351
407
  end
352
408
  end
353
-
409
+
354
410
  def define_array_accessors(name)
355
411
  unless name.to_s == name.to_s.pluralize
356
412
  self.send(:define_method, name) do
357
413
  self[name.to_sym] || []
358
414
  end
359
-
415
+
360
416
  self.send(:define_method, "#{name}=") do |array|
361
417
  self[name.to_sym] = array.respond_to?(:reject) ? array.reject(&:blank?) : array
362
418
  end
363
419
  end
364
-
420
+
365
421
  self.send(:define_method, name.to_s.pluralize) do
366
422
  self[name.to_sym] || []
367
423
  end
368
-
424
+
369
425
  self.send(:define_method, "#{name.to_s.pluralize}=") do |array|
370
426
  self[name.to_sym] = array.respond_to?(:reject) ? array.reject(&:blank?) : array
371
427
  end
372
-
428
+
373
429
  self.send(:define_method, "default_#{name.to_s.singularize}") do
374
430
  self[name.to_sym].try(:first).presence
375
431
  end
432
+
433
+ self.send(:define_method, "default_#{name.to_s.singularize}=") do |value|
434
+ if self[name.to_sym].try(:first).present?
435
+ self[name.to_sym][0] = value
436
+ else
437
+ self[name.to_sym] = [value]
438
+ end
439
+
440
+ self[name.to_sym].compact!
441
+
442
+ end
376
443
  end
377
444
  end
378
445
  end