podio 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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