podio 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/LICENSE +1 -1
- data/lib/podio.rb +7 -3
- data/lib/podio/active_podio/base.rb +121 -54
- data/lib/podio/client.rb +15 -2
- data/lib/podio/error.rb +1 -0
- data/lib/podio/middleware/error_response.rb +2 -0
- data/lib/podio/models/app_store_share.rb +8 -4
- data/lib/podio/models/application.rb +4 -0
- data/lib/podio/models/{calendar.rb → calendar_event.rb} +13 -1
- data/lib/podio/models/contact.rb +1 -0
- data/lib/podio/models/contract.rb +17 -1
- data/lib/podio/models/email_subscription_setting.rb +1 -0
- data/lib/podio/models/external_file.rb +30 -0
- data/lib/podio/models/file_attachment.rb +30 -12
- data/lib/podio/models/filter.rb +33 -2
- data/lib/podio/models/item.rb +23 -0
- data/lib/podio/models/item_field.rb +1 -1
- data/lib/podio/models/linked_account.rb +32 -0
- data/lib/podio/models/meeting.rb +55 -30
- data/lib/podio/models/meeting_participiant.rb +10 -4
- data/lib/podio/models/notification_group.rb +3 -4
- data/lib/podio/models/organization.rb +11 -9
- data/lib/podio/models/organization_member.rb +2 -1
- data/lib/podio/models/organization_profile.rb +1 -0
- data/lib/podio/models/question_answer.rb +3 -2
- data/lib/podio/models/referral.rb +25 -0
- data/lib/podio/models/reminder.rb +2 -2
- data/lib/podio/models/search.rb +34 -5
- data/lib/podio/models/space_invitation.rb +9 -4
- data/lib/podio/models/space_member.rb +8 -0
- data/lib/podio/models/subscription.rb +7 -3
- data/lib/podio/models/user_status.rb +2 -1
- data/lib/podio/models/widget.rb +2 -0
- data/lib/podio/version.rb +1 -1
- data/test/active_podio_test.rb +2 -2
- metadata +14 -11
data/.travis.yml
CHANGED
data/LICENSE
CHANGED
data/lib/podio.rb
CHANGED
@@ -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 :
|
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
|
-
|
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.
|
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
|
-
|
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.
|
138
|
-
self.
|
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.
|
159
|
-
self.
|
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 =
|
285
|
-
|
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
|