podio 0.8.0 → 1.0.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 (106) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +9 -2
  4. data/README.md +1 -4
  5. data/examples/oauth_web_flow/Gemfile +4 -0
  6. data/examples/{sinatra.rb → oauth_web_flow/sinatra.rb} +7 -6
  7. data/lib/podio.rb +18 -3
  8. data/lib/podio/active_podio/base.rb +107 -62
  9. data/lib/podio/client.rb +30 -5
  10. data/lib/podio/error.rb +22 -10
  11. data/lib/podio/middleware/error_response.rb +23 -16
  12. data/lib/podio/middleware/json_response.rb +1 -1
  13. data/lib/podio/middleware/logger.rb +3 -0
  14. data/lib/podio/middleware/oauth2.rb +3 -0
  15. data/lib/podio/models/account_provider.rb +1 -0
  16. data/lib/podio/models/action.rb +19 -2
  17. data/lib/podio/models/activation_status.rb +8 -2
  18. data/lib/podio/models/app_store_category.rb +4 -1
  19. data/lib/podio/models/app_store_share.rb +29 -16
  20. data/lib/podio/models/application.rb +57 -2
  21. data/lib/podio/models/application_email.rb +5 -2
  22. data/lib/podio/models/application_field.rb +5 -2
  23. data/lib/podio/models/batch.rb +8 -0
  24. data/lib/podio/models/by_line.rb +2 -1
  25. data/lib/podio/models/calendar_event.rb +63 -3
  26. data/lib/podio/models/calendar_mute.rb +6 -1
  27. data/lib/podio/models/category.rb +1 -0
  28. data/lib/podio/models/comment.rb +29 -8
  29. data/lib/podio/models/condition.rb +8 -0
  30. data/lib/podio/models/condition_set.rb +40 -0
  31. data/lib/podio/models/contact.rb +13 -2
  32. data/lib/podio/models/contract.rb +85 -1
  33. data/lib/podio/models/contract_event.rb +17 -0
  34. data/lib/podio/models/contract_price.rb +3 -7
  35. data/lib/podio/models/contract_price_v2.rb +31 -0
  36. data/lib/podio/models/contract_user.rb +4 -0
  37. data/lib/podio/models/conversation.rb +114 -13
  38. data/lib/podio/models/conversation_event.rb +51 -0
  39. data/lib/podio/models/conversation_message.rb +2 -1
  40. data/lib/podio/models/conversation_participant.rb +3 -2
  41. data/lib/podio/models/date_election.rb +35 -0
  42. data/lib/podio/models/email_contact.rb +46 -0
  43. data/lib/podio/models/email_subscription_setting.rb +4 -23
  44. data/lib/podio/models/embed.rb +4 -4
  45. data/lib/podio/models/experiment.rb +71 -0
  46. data/lib/podio/models/external_file.rb +7 -2
  47. data/lib/podio/models/file_attachment.rb +23 -0
  48. data/lib/podio/models/form.rb +7 -0
  49. data/lib/podio/models/friend.rb +12 -0
  50. data/lib/podio/models/grant.rb +73 -0
  51. data/lib/podio/models/hook.rb +13 -6
  52. data/lib/podio/models/importer.rb +3 -0
  53. data/lib/podio/models/integration.rb +17 -6
  54. data/lib/podio/models/invoice.rb +30 -0
  55. data/lib/podio/models/item.rb +81 -14
  56. data/lib/podio/models/item_diff.rb +9 -5
  57. data/lib/podio/models/item_field.rb +11 -2
  58. data/lib/podio/models/item_revision.rb +5 -2
  59. data/lib/podio/models/linked_account.rb +1 -0
  60. data/lib/podio/models/live.rb +61 -0
  61. data/lib/podio/models/net_promoter_score.rb +28 -0
  62. data/lib/podio/models/notification.rb +9 -4
  63. data/lib/podio/models/notification_group.rb +5 -2
  64. data/lib/podio/models/o_auth.rb +4 -2
  65. data/lib/podio/models/o_auth_client.rb +1 -2
  66. data/lib/podio/models/organization.rb +22 -10
  67. data/lib/podio/models/organization_contact.rb +2 -1
  68. data/lib/podio/models/organization_member.rb +17 -3
  69. data/lib/podio/models/organization_profile.rb +8 -2
  70. data/lib/podio/models/pin.rb +27 -0
  71. data/lib/podio/models/profile.rb +19 -50
  72. data/lib/podio/models/promotion.rb +91 -0
  73. data/lib/podio/models/rating.rb +23 -5
  74. data/lib/podio/models/recurrence.rb +6 -3
  75. data/lib/podio/models/reference.rb +17 -1
  76. data/lib/podio/models/reminder.rb +8 -3
  77. data/lib/podio/models/search.rb +7 -1
  78. data/lib/podio/models/space.rb +41 -2
  79. data/lib/podio/models/space_contact.rb +1 -2
  80. data/lib/podio/models/space_invitation.rb +4 -5
  81. data/lib/podio/models/space_member.rb +37 -4
  82. data/lib/podio/models/status.rb +20 -8
  83. data/lib/podio/models/stream_activity_group.rb +40 -0
  84. data/lib/podio/models/stream_mute.rb +7 -8
  85. data/lib/podio/models/stream_object.rb +19 -11
  86. data/lib/podio/models/subscription.rb +7 -1
  87. data/lib/podio/models/tag.rb +6 -1
  88. data/lib/podio/models/tag_search.rb +2 -1
  89. data/lib/podio/models/task.rb +74 -18
  90. data/lib/podio/models/task_label.rb +10 -2
  91. data/lib/podio/models/user.rb +50 -6
  92. data/lib/podio/models/user_mail.rb +4 -0
  93. data/lib/podio/models/user_status.rb +7 -0
  94. data/lib/podio/models/view.rb +27 -7
  95. data/lib/podio/models/widget.rb +25 -3
  96. data/lib/podio/version.rb +1 -1
  97. data/podio.gemspec +13 -10
  98. data/script/config.example.rb +6 -0
  99. data/script/referenceGenerator.rb +87 -0
  100. data/test/active_podio_test.rb +44 -34
  101. data/test/client_test.rb +13 -2
  102. metadata +89 -36
  103. data/lib/podio/middleware/date_conversion.rb +0 -37
  104. data/lib/podio/models/bulletin.rb +0 -60
  105. data/lib/podio/models/connection.rb +0 -53
  106. data/lib/podio/models/news.rb +0 -85
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ddca0ab27daadb721f418559e369f9df19ff079b
4
+ data.tar.gz: 62879f0a0051374d7edf58585fb23fcdc1b8a65f
5
+ SHA512:
6
+ metadata.gz: 745846890f8a4a11a0ae2591db7d2921ab9fa33cb696b7573aedb7de87c46ddbf4966f16d97132e7da7e13756dff437eb48ae2dff15b34a6e7eed5509fd3550b
7
+ data.tar.gz: b1bd3c5b7177185fa91b198f88b05ebd51820c30a4fca4fb00f7f0feab151b2afa3ab2aa013f9881b623ba17eb5db3632be90a302585eeb9263c42a6f0c0297c
data/.gitignore CHANGED
@@ -2,3 +2,6 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  .DS_Store
5
+ Gemfile.lock
6
+ script/config.rb
7
+ *.swp
data/Gemfile CHANGED
@@ -1,5 +1,12 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
- gem 'rake'
3
+ platforms :jruby do
4
+ gem 'jruby-openssl', '~> 0.7'
5
+ end
6
+
7
+ platforms :rbx do
8
+ gem 'rubysl', '~> 2.0.15'
9
+ gem 'rubysl-test-unit', '~> 2.0.3'
10
+ end
4
11
 
5
12
  gemspec
data/README.md CHANGED
@@ -113,7 +113,7 @@ All unsuccessful responses returned by the API (everything that has a 4xx or 5xx
113
113
  puts exc.url # uri of the API request
114
114
 
115
115
  # you normally want this one, a human readable error description
116
- puts exc.response_body['error_description']
116
+ puts exc.message
117
117
  end
118
118
 
119
119
  On instance methods, however, exceptions are handled in a way similar to ActiveRecord. These methods returns a boolean indicating if the API request succeeded or not, and makes the code, description and parameters available when the request fails:
@@ -146,9 +146,6 @@ Full Example
146
146
  puts org.url
147
147
  end
148
148
 
149
- Note on Heroku Usage
150
- --------------------
151
- If you plan on using podio-rb on Heroku please note that only the 1.9.2 stack has been tested. Specifically, bamboo-mri-1.9.2 is recommended, while 1.8.7 is still stock on Heroku. Refer to their documentation for information on how to migrate your dynos
152
149
 
153
150
  Meta
154
151
  ----
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sinatra', '~> 1.4.4'
4
+ gem 'podio'
@@ -18,11 +18,12 @@ require 'sinatra'
18
18
  require 'podio'
19
19
 
20
20
 
21
- # CHANGE this this to make this example work
22
- Podio.setup(
23
- :api_key => 'YOUR_API_KEY',
24
- :api_secret => 'YOUR_API_SECRET'
25
- )
21
+ before do
22
+ Podio.setup(
23
+ :api_key => 'YOUR_API_KEY', # CHANGE this this to make this example work
24
+ :api_secret => 'YOUR_API_SECRET' # CHANGE this this to make this example work
25
+ )
26
+ end
26
27
 
27
28
  get '/' do
28
29
  %(<p>Update the <code>Podio.setup</code> call in the sinatra app and <a href="/auth/podio">try to authorize</a>.</p>)
@@ -50,6 +51,6 @@ end
50
51
 
51
52
  def redirect_uri(path='/auth/podio/callback')
52
53
  uri = URI.parse(request.url)
53
- uri.path = path
54
+ uri.path = path
54
55
  uri.to_s
55
56
  end
@@ -2,8 +2,8 @@ require 'faraday'
2
2
  require 'active_support/core_ext'
3
3
 
4
4
  require 'podio/error'
5
+ require 'podio/version'
5
6
  require 'podio/middleware/json_request'
6
- #require 'podio/middleware/date_conversion'
7
7
  require 'podio/middleware/logger'
8
8
  require 'podio/middleware/oauth2'
9
9
  require 'podio/middleware/json_response'
@@ -76,36 +76,48 @@ module Podio
76
76
  autoload :ApplicationField, 'podio/models/application_field'
77
77
  autoload :AccountProvider, 'podio/models/account_provider'
78
78
  autoload :Batch, 'podio/models/batch'
79
- autoload :Bulletin, 'podio/models/bulletin'
80
79
  autoload :ByLine, 'podio/models/by_line'
81
80
  autoload :CalendarEvent, 'podio/models/calendar_event'
82
81
  autoload :CalendarMute, 'podio/models/calendar_mute'
83
82
  autoload :Category, 'podio/models/category'
84
83
  autoload :Comment, 'podio/models/comment'
84
+ autoload :Condition, 'podio/models/condition'
85
+ autoload :ConditionSet, 'podio/models/condition_set'
85
86
  autoload :Connection, 'podio/models/connection'
86
87
  autoload :Contact, 'podio/models/contact'
87
88
  autoload :Contract, 'podio/models/contract'
88
89
  autoload :ContractAccounting, 'podio/models/contract_accounting'
90
+ autoload :ContractEvent, 'podio/models/contract_event'
89
91
  autoload :ContractPrice, 'podio/models/contract_price'
92
+ autoload :ContractPriceV2, 'podio/models/contract_price_v2'
93
+ autoload :ContractUser, 'podio/models/contract_user'
90
94
  autoload :Conversation, 'podio/models/conversation'
95
+ autoload :ConversationEvent, 'podio/models/conversation_event'
91
96
  autoload :ConversationMessage, 'podio/models/conversation_message'
92
97
  autoload :ConversationParticipant, 'podio/models/conversation_participant'
98
+ autoload :DateElection, 'podio/models/date_election'
93
99
  autoload :EmailSubscriptionSetting, 'podio/models/email_subscription_setting'
100
+ autoload :EmailContact, 'podio/models/email_contact'
94
101
  autoload :Embed, 'podio/models/embed'
102
+ autoload :Experiment, 'podio/models/experiment'
95
103
  autoload :ExternalFile, 'podio/models/external_file'
96
104
  autoload :FileAttachment, 'podio/models/file_attachment'
97
105
  autoload :Filter, 'podio/models/filter'
98
106
  autoload :Form, 'podio/models/form'
107
+ autoload :Friend, 'podio/models/friend'
108
+ autoload :Grant, 'podio/models/grant'
99
109
  autoload :Hook, 'podio/models/hook'
100
110
  autoload :Importer, 'podio/models/importer'
101
111
  autoload :Integration, 'podio/models/integration'
112
+ autoload :Invoice, 'podio/models/invoice'
102
113
  autoload :Item, 'podio/models/item'
103
114
  autoload :ItemDiff, 'podio/models/item_diff'
104
115
  autoload :ItemField, 'podio/models/item_field'
105
116
  autoload :ItemRevision, 'podio/models/item_revision'
106
117
  autoload :LinkedAccount, 'podio/models/linked_account'
107
118
  autoload :LinkedAccountData, 'podio/models/linked_account_data'
108
- autoload :News, 'podio/models/news'
119
+ autoload :Live, 'podio/models/live'
120
+ autoload :NetPromoterScore, 'podio/models/net_promoter_score'
109
121
  autoload :Notification, 'podio/models/notification'
110
122
  autoload :NotificationGroup, 'podio/models/notification_group'
111
123
  autoload :OAuth, 'podio/models/o_auth'
@@ -114,7 +126,9 @@ module Podio
114
126
  autoload :OrganizationContact, 'podio/models/organization_contact'
115
127
  autoload :OrganizationMember, 'podio/models/organization_member'
116
128
  autoload :OrganizationProfile, 'podio/models/organization_profile'
129
+ autoload :Pin, 'podio/models/pin'
117
130
  autoload :Profile, 'podio/models/profile'
131
+ autoload :Promotion, 'podio/models/promotion'
118
132
  autoload :Question, 'podio/models/question'
119
133
  autoload :QuestionAnswer, 'podio/models/question_answer'
120
134
  autoload :QuestionOption, 'podio/models/question_option'
@@ -131,6 +145,7 @@ module Podio
131
145
  autoload :Status, 'podio/models/status'
132
146
  autoload :StreamMute, 'podio/models/stream_mute'
133
147
  autoload :StreamObject, 'podio/models/stream_object'
148
+ autoload :StreamActivityGroup, 'podio/models/stream_activity_group'
134
149
  autoload :Subscription, 'podio/models/subscription'
135
150
  autoload :Tag, 'podio/models/tag'
136
151
  autoload :TagSearch, 'podio/models/tag_search'
@@ -6,12 +6,15 @@ module ActivePodio
6
6
  extend ActiveModel::Naming, ActiveModel::Callbacks
7
7
  include ActiveModel::Conversion
8
8
 
9
- class_attribute :valid_attributes, :_associations, :_properties, :json_attributes
10
- attr_accessor :attributes, :error_code, :error_message, :error_parameters, :error_propagate
11
- alias_method :propagate_error?, :error_propagate
9
+ class_attribute :valid_attributes, :json_attributes, :_associations
10
+
11
+ self.valid_attributes = []
12
+ self.json_attributes = []
13
+ self._associations = {}
14
+
15
+ attr_accessor :attributes
12
16
 
13
17
  def initialize(attributes = {}, options = {})
14
- self.valid_attributes ||= []
15
18
  attributes = {} if attributes.blank?
16
19
  self.attributes = Hash[*self.valid_attributes.collect { |n| [n.to_sym, nil] }.flatten].merge(attributes.symbolize_keys)
17
20
 
@@ -28,11 +31,20 @@ module ActivePodio
28
31
  if self.respond_to?("#{key}=".to_sym)
29
32
  self.send("#{key}=".to_sym, value)
30
33
  else
31
- 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) || self.send(key.to_sym).nil?)
34
+ is_association_hash = value.is_a?(Hash) && self._associations.has_key?(key.to_sym) && self._associations[key.to_sym] == :has_one && (self.send(key.to_sym).respond_to?(:attributes) || self.send(key.to_sym).nil?)
32
35
  if valid_attributes.include?(key.to_sym) || is_association_hash
36
+
33
37
  # Initialize nested object to get correctly casted values set back, unless the given values are all blank
34
38
  if is_association_hash
35
- self.send(:[]=, key.to_sym, value) if self.send(key.to_sym).nil? # If not set by constructor, set here to get typed values back
39
+ # Merge existing values with given values and do recursive initalize call to get values casted correctly
40
+ if self.send(key.to_sym).present?
41
+ existing_attributes = self.send(key.to_sym).try(:attributes) || {}
42
+ value.reverse_merge!(existing_attributes)
43
+ self.send(key.to_sym).initialize_attributes(value)
44
+ else
45
+ self.send(:[]=, key.to_sym, value)
46
+ end
47
+
36
48
  attributes = self.send(key.to_sym).try(:attributes)
37
49
  if attributes.present? && any_values_present_recursive?(attributes.values)
38
50
  value = attributes
@@ -68,7 +80,7 @@ module ActivePodio
68
80
  def []=(attribute, value)
69
81
  @attributes ||= {}
70
82
  @attributes[attribute.to_sym] = value
71
- if @belongs_to.present? && value.present?
83
+ if @belongs_to.present? && @belongs_to[:model].present? && @belongs_to[:as].present? && value.present?
72
84
  @belongs_to[:model][@belongs_to[:as]] ||= {}
73
85
  @belongs_to[:model][@belongs_to[:as]][attribute.to_sym] = value
74
86
  end
@@ -89,18 +101,27 @@ module ActivePodio
89
101
  result.merge!(:id => self.id) if self.respond_to?(:id)
90
102
 
91
103
  if options[:formatted]
92
- (self.valid_attributes + (self.json_attributes || [])).uniq.each do |name|
104
+ (self.valid_attributes + self.json_attributes).uniq.each do |name|
93
105
  result[name] = json_friendly_value(self.send(name), options)
94
106
  end
95
107
 
96
108
  unless options[:nested] == false
97
- if self._associations.respond_to?(:each)
98
- self._associations.each do |name, type|
109
+ self._associations.each do |name, type|
110
+ nested_value = self.send(name)
111
+ unless nested_value.nil?
112
+ nested_options = options.except(:methods)
113
+ if options[:methods].present? && options[:methods].respond_to?(:find)
114
+ methods_hash = options[:methods].find { |method| method.is_a?(Hash) }
115
+ if methods_hash.present?
116
+ nested_methods = methods_hash[name]
117
+ nested_options.merge!(:methods => nested_methods) if nested_methods.present?
118
+ end
119
+ end
99
120
  case type
100
121
  when :has_one
101
- result[name] = self.send(name).as_json(options.except(:methods))
122
+ result[name] = nested_value.as_json(nested_options)
102
123
  when :has_many
103
- result[name] = self.send(name).collect { |assoc| assoc.as_json(options.except(:methods)) }
124
+ result[name] = nested_value.collect { |assoc| assoc.as_json(nested_options) }
104
125
  end
105
126
  end
106
127
  end
@@ -111,7 +132,7 @@ module ActivePodio
111
132
 
112
133
  if options[:methods]
113
134
  options[:methods].each do |name|
114
- result[name] = json_friendly_value(self.send(name), options)
135
+ result[name] = json_friendly_value(self.send(name), options.except(:methods) ) unless name.is_a?(Hash)
115
136
  end
116
137
  end
117
138
 
@@ -120,17 +141,26 @@ module ActivePodio
120
141
 
121
142
  # Override this in models where the class name doesn't match the ref type
122
143
  def api_friendly_ref_type
123
- self.class.name.downcase
144
+ self.class.name.demodulize.parameterize
145
+ end
146
+
147
+ def parent_model
148
+ @belongs_to[:model] if @belongs_to.present?
124
149
  end
125
150
 
126
151
  private
127
152
 
128
153
  def klass_for_association(options)
129
154
  klass_name = options[:class]
155
+
156
+ if !klass_name.present? && options[:class_map].present?
157
+ class_property = options[:class_property] || :type
158
+ class_property_value = self.send(class_property).try(:to_sym)
159
+ klass_name = options[:class_map][class_property_value]
160
+ end
161
+
130
162
  raise "Missing class name of associated model. Provide with :class => 'MyClass'." unless klass_name.present?
131
- klass = klass_name.constantize rescue nil
132
- klass = "Podio::#{klass_name}".constantize unless klass.respond_to?(:ancestors) && klass.ancestors.include?(ActivePodio::Base)
133
- return klass
163
+ return self.class.klass_from_string(klass_name)
134
164
  end
135
165
 
136
166
  def any_values_present_recursive?(values)
@@ -145,7 +175,7 @@ module ActivePodio
145
175
 
146
176
  def json_friendly_value(ruby_value, options)
147
177
  if options[:formatted]
148
- json_value = case ruby_value.class.name
178
+ case ruby_value.class.name
149
179
  when "DateTime", "Time"
150
180
  ruby_value.iso8601
151
181
  when "Array"
@@ -153,9 +183,11 @@ module ActivePodio
153
183
  when "Hash"
154
184
  ruby_value.each { |key, value| ruby_value[key] = json_friendly_value(value, options) }
155
185
  ruby_value
186
+ when "BigDecimal"
187
+ ruby_value.to_f # No Decimal in Javascript, Float is better than String
156
188
  else
157
189
  if ruby_value.kind_of?(ActivePodio::Base)
158
- ruby_value.as_json(options)
190
+ ruby_value.as_json(options.except(:methods))
159
191
  else
160
192
  ruby_value
161
193
  end
@@ -163,6 +195,7 @@ module ActivePodio
163
195
  else
164
196
  ruby_value
165
197
  end
198
+
166
199
  end
167
200
 
168
201
  class << self
@@ -171,8 +204,7 @@ module ActivePodio
171
204
 
172
205
  # Defines the the supported attributes of the model
173
206
  def property(name, type = :string, options = {})
174
- self.valid_attributes ||= []
175
- self.valid_attributes << name
207
+ self.valid_attributes += [name]
176
208
 
177
209
  case type
178
210
  when :datetime
@@ -188,6 +220,10 @@ module ActivePodio
188
220
  define_boolean_accessors(name)
189
221
  when :array
190
222
  define_array_accessors(name)
223
+ when :float
224
+ define_float_accessor(name)
225
+ when :decimal
226
+ define_decimal_accessor(name)
191
227
  else
192
228
  define_generic_accessor(name)
193
229
  end
@@ -195,8 +231,7 @@ module ActivePodio
195
231
 
196
232
  # Wraps a single hash provided from the API in the given model
197
233
  def has_one(name, options = {})
198
- self._associations ||= {}
199
- self._associations[name] = :has_one
234
+ self._associations = self._associations.merge({name => :has_one})
200
235
 
201
236
  self.send(:define_method, name) do
202
237
  klass = klass_for_association(options)
@@ -220,8 +255,7 @@ module ActivePodio
220
255
 
221
256
  # Wraps a collection of hashes from the API to a collection of the given model
222
257
  def has_many(name, options = {})
223
- self._associations ||= {}
224
- self._associations[name] = :has_many
258
+ self._associations = self._associations.merge({name => :has_many})
225
259
 
226
260
  self.send(:define_method, name) do
227
261
  klass = klass_for_association(options)
@@ -229,7 +263,7 @@ module ActivePodio
229
263
  unless instances.present?
230
264
  property = options[:property] || name.to_sym
231
265
  if self[property].present? && self[property].respond_to?(:map)
232
- instances = self[property].map { |attributes| klass.new(attributes) }
266
+ instances = self[property].map { |attributes| klass.new(attributes, :belongs_to => { :model => self }) }
233
267
  self.instance_variable_set("@#{name}_has_many_instances", instances)
234
268
  else
235
269
  instances = []
@@ -284,45 +318,16 @@ module ActivePodio
284
318
  end
285
319
  end
286
320
 
287
- # Wraps the given methods in a begin/rescue block
288
- # If no error occurs, the return value of the method, or true if nil is returned, is returned
289
- # If a Podio::PodioError occurs, the method returns false and the error can be read from the error_message accessor
290
- # If another error occurs, it is still raised
291
- def handle_api_errors_for(*method_names)
292
- method_names.each do |method_name|
293
- self.send(:define_method, "#{method_name}_with_api_errors_handled") do |*args|
294
- success, code, message, parameters, result = nil
295
- begin
296
- result = self.send("#{method_name}_without_api_errors_handled", *args)
297
- success = true
298
- rescue Podio::PodioError => ex
299
- success = false
300
- code = ex.response_body["error"]
301
- message = ex.response_body["error_description"]
302
- parameters = ex.response_body["error_parameters"]
303
- propagate = ex.response_body["error_propagate"]
304
- end
305
-
306
- if success
307
- return result || true
308
- else
309
- @error_code = code
310
- @error_message = message
311
- @error_parameters = parameters || {}
312
- @error_propagate = propagate
313
- return false
314
- end
315
- end
316
-
317
- alias_method_chain method_name, :api_errors_handled
318
- end
319
- end
320
-
321
321
  def output_attribute_as_json(*attributes)
322
- self.json_attributes ||= []
323
322
  self.json_attributes += attributes
324
323
  end
325
324
 
325
+ def klass_from_string(klass_name)
326
+ klass = klass_name.constantize rescue nil
327
+ klass = "Podio::#{klass_name}".constantize unless klass.respond_to?(:ancestors) && klass.ancestors.include?(ActivePodio::Base)
328
+ return klass
329
+ end
330
+
326
331
  private
327
332
 
328
333
  def define_generic_accessor(name, options = {})
@@ -354,7 +359,11 @@ module ActivePodio
354
359
  value = if value.is_a?(DateTime)
355
360
  Time.zone.local_to_utc(value)
356
361
  else
357
- Time.zone.parse(value).try(:utc).try(:to_datetime)
362
+ begin
363
+ Time.zone.parse(value).try(:utc).try(:to_datetime)
364
+ rescue TZInfo::AmbiguousTime
365
+ nil
366
+ end
358
367
  end
359
368
  end
360
369
 
@@ -419,6 +428,42 @@ module ActivePodio
419
428
  end
420
429
  end
421
430
 
431
+ def define_float_accessor(name)
432
+ self.send(:define_method, name) do
433
+ if self[name.to_sym].present?
434
+ self[name.to_sym].to_f
435
+ else
436
+ nil
437
+ end
438
+ end
439
+
440
+ self.send(:define_method, "#{name}=") do |value|
441
+ if value.present?
442
+ self[name.to_sym] = value.to_f
443
+ else
444
+ self[name.to_sym] = nil
445
+ end
446
+ end
447
+ end
448
+
449
+ def define_decimal_accessor(name)
450
+ self.send(:define_method, name) do
451
+ if self[name.to_sym].present?
452
+ BigDecimal(self[name.to_sym], 2)
453
+ else
454
+ nil
455
+ end
456
+ end
457
+
458
+ self.send(:define_method, "#{name}=") do |value|
459
+ if value.present?
460
+ self[name.to_sym] = BigDecimal(value, 2)
461
+ else
462
+ self[name.to_sym] = nil
463
+ end
464
+ end
465
+ end
466
+
422
467
  def define_boolean_accessors(name)
423
468
  self.send(:define_method, "#{name}?") do
424
469
  if self[name.to_sym].present?