usergrid_ironhorse 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.rvmrc +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +121 -0
- data/Rakefile +7 -0
- data/lib/extensions/hash.rb +7 -0
- data/lib/usergrid_ironhorse.rb +24 -0
- data/lib/usergrid_ironhorse/base.rb +360 -0
- data/lib/usergrid_ironhorse/query.rb +890 -0
- data/lib/usergrid_ironhorse/user_context.rb +79 -0
- data/lib/usergrid_ironhorse/version.rb +5 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/spec_settings.yaml +5 -0
- data/spec/support/active_model_lint.rb +17 -0
- data/spec/usergrid_ironhorse/base_spec.rb +283 -0
- data/usergrid_ironhorse.gemspec +29 -0
- metadata +186 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Scott Ganyo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# Usergrid_ironhorse
|
2
|
+
|
3
|
+
Usergrid_ironhorse is based on Usergrid_iron and enables Ruby or Rails applications
|
4
|
+
native Rails-style access to Apigee's App Services (aka Usergrid) REST API.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'usergrid_ironhorse'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install usergrid_ironhorse
|
19
|
+
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Not familiar with Usergrid / Apigee's App Services?
|
24
|
+
|
25
|
+
#### It's great stuff! Check it out, here:
|
26
|
+
|
27
|
+
Docs: <http://apigee.com/docs/usergrid/>
|
28
|
+
Open source: <https://github.com/apigee/usergrid-stack>
|
29
|
+
|
30
|
+
### Getting started with the Usergrid_ironhorse SDK is super simple!
|
31
|
+
|
32
|
+
* Add 'gem usergrid_ironhorse' to your Gemfile
|
33
|
+
* Create a 'config/usergrid.yml' file that looks something like this (the
|
34
|
+
auth_token is your application admin token):
|
35
|
+
<pre>
|
36
|
+
development:
|
37
|
+
:application_url: http://localhost:8080/my-organization/my-application
|
38
|
+
:auth_token: YWMtc4WjqhcbEeK6UhQQn9SVgQAAATpryjMnLy9oFaPbP-0qIxoUx_4vtaOmpmE
|
39
|
+
|
40
|
+
development:
|
41
|
+
:application_url: http://localhost:8080/my-organization/my-application
|
42
|
+
:auth_token: YWMtc4WjqhcbEeK6UhQQn9SVgQAAATpryjMnLy9oFaPbP-0qIxoUx_4vtaOmpmE
|
43
|
+
|
44
|
+
production:
|
45
|
+
:application_url: http://api.usergrid.com/my-organization/my-application
|
46
|
+
:auth_token: YWMtc4WjqhcbEeK6UhQQn9SVgQAAATpryjMnLy9oFaPbP-0qIxoUx_4vtaOmpmE
|
47
|
+
</pre>
|
48
|
+
* Your User model should subclass Usergrid::Ironhorse::Base and extend
|
49
|
+
Usergrid::Ironhorse::UserContext like so:
|
50
|
+
<pre>
|
51
|
+
class User < Usergrid::Ironhorse::Base
|
52
|
+
extend Usergrid::Ironhorse::UserContext
|
53
|
+
|
54
|
+
end
|
55
|
+
</pre>
|
56
|
+
* Set up your authentication
|
57
|
+
* Use `User.authenticate(username, password, session)` to login.
|
58
|
+
* Use `User.clear_authentication(session)` to log out.
|
59
|
+
* Propogate the authentication in your ApplicationController:
|
60
|
+
<pre>
|
61
|
+
before_filter :set_thread_context
|
62
|
+
def set_thread_context
|
63
|
+
User.set_thread_context session
|
64
|
+
end
|
65
|
+
</pre>
|
66
|
+
* Optionally, if you need to access the User from your view, you may add something
|
67
|
+
like the following to your ApplicationController:
|
68
|
+
<pre>
|
69
|
+
helper_method :current_user
|
70
|
+
def current_user
|
71
|
+
User.current_user
|
72
|
+
end
|
73
|
+
</pre>
|
74
|
+
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
We welcome your enhancements!
|
79
|
+
|
80
|
+
1. Fork it
|
81
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
82
|
+
3. Write some broken rspecs.
|
83
|
+
4. Fix the rspecs with your new code.
|
84
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
85
|
+
4. Push your changes to the upstream branch (`git push origin my-new-feature`)
|
86
|
+
5. Create new Pull Request
|
87
|
+
|
88
|
+
We've got 100% rspec coverage and we're looking to keep it that way!*
|
89
|
+
(*Not yet, but soon)
|
90
|
+
In order to run the tests, check out the Usergrid open source project
|
91
|
+
(https://github.com/apigee/usergrid-stack), build, and launch it locally.
|
92
|
+
|
93
|
+
(Note: If you change your local Usergrid settings from the default, be sure to update
|
94
|
+
usergrid_ironhorse/spec/spec_settings.yaml to match.)
|
95
|
+
|
96
|
+
|
97
|
+
## Release notes
|
98
|
+
|
99
|
+
### 0.0.2
|
100
|
+
* New Features
|
101
|
+
1. Authentication and user propagation features
|
102
|
+
### 0.0.1
|
103
|
+
* Initial commit
|
104
|
+
1. Support for most ActiveModel stuff including Validations
|
105
|
+
1. No scoping support
|
106
|
+
|
107
|
+
|
108
|
+
## Copyright
|
109
|
+
Copyright (c) 2012 Scott Ganyo
|
110
|
+
|
111
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
112
|
+
you may not use the included files except in compliance with the License.
|
113
|
+
|
114
|
+
You may obtain a copy of the License at
|
115
|
+
|
116
|
+
<http://www.apache.org/licenses/LICENSE-2.0>
|
117
|
+
|
118
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
119
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
120
|
+
either express or implied. See the License for the specific language governing permissions and
|
121
|
+
limitations under the License.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'active_model'
|
3
|
+
require 'rest-client'
|
4
|
+
require 'active_support'
|
5
|
+
require 'usergrid_iron'
|
6
|
+
require 'active_record/errors'
|
7
|
+
|
8
|
+
module Usergrid
|
9
|
+
module Ironhorse
|
10
|
+
|
11
|
+
Dir[Pathname.new(File.dirname(__FILE__)).join("extensions/**/*.rb")].each { |f| require f }
|
12
|
+
|
13
|
+
USERGRID_PATH = File.join File.dirname(__FILE__), 'usergrid_ironhorse'
|
14
|
+
|
15
|
+
def self.usergrid_path *path
|
16
|
+
File.join USERGRID_PATH, *path
|
17
|
+
end
|
18
|
+
|
19
|
+
require usergrid_path('base')
|
20
|
+
require usergrid_path('query')
|
21
|
+
|
22
|
+
autoload :UserContext, usergrid_path('user_context')
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,360 @@
|
|
1
|
+
require 'active_record/validations'
|
2
|
+
require 'active_record/errors'
|
3
|
+
require 'active_record/callbacks'
|
4
|
+
|
5
|
+
module Usergrid
|
6
|
+
module Ironhorse
|
7
|
+
|
8
|
+
class Base
|
9
|
+
include ActiveModel::AttributeMethods
|
10
|
+
include ActiveModel::Conversion
|
11
|
+
include ActiveModel::Validations
|
12
|
+
include ActiveModel::Dirty
|
13
|
+
include ActiveModel::Serialization
|
14
|
+
extend ActiveModel::Naming
|
15
|
+
extend ActiveModel::Callbacks
|
16
|
+
|
17
|
+
RESERVED_ATTRIBUTES = %w(metadata created modified uuid type uri)
|
18
|
+
|
19
|
+
define_model_callbacks :create, :destroy, :save, :update
|
20
|
+
|
21
|
+
# todo: determine the subset to support...
|
22
|
+
# unsupported: :group, :joins, :preload, :eager_load, :includes, :from, :lock,
|
23
|
+
# :having, :create_with, :uniq, :references, :none, :count,
|
24
|
+
# :average, :minimum, :maximum, :sum, :calculate, :ids
|
25
|
+
# :find_each, :find_in_batches, :offset, :readonly
|
26
|
+
|
27
|
+
#delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
|
28
|
+
#delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
|
29
|
+
#delegate :find_by, :find_by!, :to => :all
|
30
|
+
#delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
|
31
|
+
#delegate :find_each, :find_in_batches, :to => :all
|
32
|
+
#delegate :select, :group, :order, :except, :reorder, :limit, :offset,
|
33
|
+
# :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
|
34
|
+
# :having, :create_with, :uniq, :references, :none, :to => :all
|
35
|
+
#delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :all
|
36
|
+
|
37
|
+
@@settings ||= nil
|
38
|
+
|
39
|
+
attr_accessor :attributes
|
40
|
+
|
41
|
+
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
|
42
|
+
RecordNotSaved = ActiveRecord::RecordNotSaved
|
43
|
+
|
44
|
+
def initialize(attrs=nil)
|
45
|
+
attrs = HashWithIndifferentAccess.new attrs
|
46
|
+
unless attrs.has_key? :uuid
|
47
|
+
assign_attributes attrs
|
48
|
+
end
|
49
|
+
@attributes = attrs
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.configure!(application_url, auth_token)
|
53
|
+
@@settings = HashWithIndifferentAccess.new application_url: application_url, auth_token: auth_token
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.settings
|
57
|
+
return @@settings if @@settings
|
58
|
+
path = "config/usergrid.yml"
|
59
|
+
environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV['RACK_ENV']
|
60
|
+
@@settings = HashWithIndifferentAccess.new YAML.load(ERB.new(File.new(path).read).result)[environment]
|
61
|
+
end
|
62
|
+
|
63
|
+
# forward to all
|
64
|
+
def self.method_missing(method, *args, &block)
|
65
|
+
all.send method, *args, &block
|
66
|
+
end
|
67
|
+
|
68
|
+
# forward to all
|
69
|
+
def method_missing(method, *args, &block)
|
70
|
+
if args.size == 0
|
71
|
+
attributes[method]
|
72
|
+
elsif args.size == 1 && method[-1] == '='
|
73
|
+
attr = method[0..-2]
|
74
|
+
if attributes[attr] != args[0]
|
75
|
+
attribute_will_change!(attr)
|
76
|
+
attributes[attr] = args[0]
|
77
|
+
end
|
78
|
+
else
|
79
|
+
all.send method, *args, &block
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# todo: scopes
|
84
|
+
def self.all
|
85
|
+
unscoped
|
86
|
+
end
|
87
|
+
|
88
|
+
#def self.scope(symbol, scope)
|
89
|
+
# @scopes[symbol] = scope
|
90
|
+
#end
|
91
|
+
|
92
|
+
#def self.current_scope
|
93
|
+
# @current_scope ||= default_scope
|
94
|
+
#end
|
95
|
+
#
|
96
|
+
#def self.current_scope=(scope)
|
97
|
+
# @current_scope = scope
|
98
|
+
#end
|
99
|
+
#
|
100
|
+
#def self.default_scope
|
101
|
+
# @default_scope ||= unscoped
|
102
|
+
#end
|
103
|
+
|
104
|
+
def self.unscoped
|
105
|
+
Query.new(self)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.create(attributes=nil, options=nil, &block)
|
109
|
+
if attributes.is_a?(Array)
|
110
|
+
attributes.collect { |attr| create(attr, options, &block) }
|
111
|
+
else
|
112
|
+
object = new(attributes, &block)
|
113
|
+
object.save
|
114
|
+
object
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.create!(attributes=nil, options=nil, &block)
|
119
|
+
if attributes.is_a?(Array)
|
120
|
+
attributes.collect {|attr| create!(attr, options, &block)}
|
121
|
+
else
|
122
|
+
object = new(attributes)
|
123
|
+
yield(object) if block_given?
|
124
|
+
object.save!
|
125
|
+
object
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.class_attributes
|
130
|
+
@class_attributes ||= {}
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
134
|
+
# not destroyed, otherwise returns false.
|
135
|
+
def persisted?
|
136
|
+
!(new_record? || destroyed?)
|
137
|
+
end
|
138
|
+
|
139
|
+
def new_record?
|
140
|
+
!self.uuid
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.group
|
144
|
+
model_name.plural.downcase
|
145
|
+
end
|
146
|
+
|
147
|
+
# Creates a Usergrid::Resource
|
148
|
+
def self.resource
|
149
|
+
app = Usergrid::Application.new settings[:application_url]
|
150
|
+
if Thread.current[:auth_token]
|
151
|
+
app.auth_token = Thread.current[:auth_token]
|
152
|
+
else
|
153
|
+
app.auth_token = settings[:auth_token]
|
154
|
+
end
|
155
|
+
app[group]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Saves the model.
|
159
|
+
#
|
160
|
+
# If the model is new a record gets created in the database, otherwise
|
161
|
+
# the existing record gets updated.
|
162
|
+
#
|
163
|
+
# By default, save always run validations. If any of them fail the action
|
164
|
+
# is cancelled and +save+ returns +false+. However, if you supply
|
165
|
+
# :validate => false, validations are bypassed altogether. See
|
166
|
+
# ActiveRecord::Validations for more information.
|
167
|
+
#
|
168
|
+
# There's a series of callbacks associated with +save+. If any of the
|
169
|
+
# <tt>before_*</tt> callbacks return +false+ the action is cancelled and
|
170
|
+
# +save+ returns +false+. See ActiveRecord::Callbacks for further
|
171
|
+
# details.
|
172
|
+
def save
|
173
|
+
begin
|
174
|
+
create_or_update
|
175
|
+
rescue ActiveRecord::RecordInvalid
|
176
|
+
false
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Saves the model.
|
181
|
+
#
|
182
|
+
# If the model is new a record gets created in the database, otherwise
|
183
|
+
# the existing record gets updated.
|
184
|
+
#
|
185
|
+
# With <tt>save!</tt> validations always run. If any of them fail
|
186
|
+
# ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
|
187
|
+
# for more information.
|
188
|
+
#
|
189
|
+
# There's a series of callbacks associated with <tt>save!</tt>. If any of
|
190
|
+
# the <tt>before_*</tt> callbacks return +false+ the action is cancelled
|
191
|
+
# and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
|
192
|
+
# ActiveRecord::Callbacks for further details.
|
193
|
+
def save!
|
194
|
+
create_or_update or raise RecordNotSaved
|
195
|
+
end
|
196
|
+
|
197
|
+
# Deletes the record in the database and freezes this instance to
|
198
|
+
# reflect that no changes should be made (since they can't be
|
199
|
+
# persisted). Returns the frozen instance.
|
200
|
+
#
|
201
|
+
# The row is simply removed with an SQL +DELETE+ statement on the
|
202
|
+
# record's primary key, and no callbacks are executed.
|
203
|
+
#
|
204
|
+
# To enforce the object's +before_destroy+ and +after_destroy+
|
205
|
+
# callbacks, Observer methods, or any <tt>:dependent</tt> association
|
206
|
+
# options, use <tt>#destroy</tt>.
|
207
|
+
def delete
|
208
|
+
self.class.delete(id) if persisted?
|
209
|
+
@destroyed = true
|
210
|
+
freeze
|
211
|
+
end
|
212
|
+
|
213
|
+
# Deletes the record in the database and freezes this instance to reflect
|
214
|
+
# that no changes should be made (since they can't be persisted).
|
215
|
+
#
|
216
|
+
# There's a series of callbacks associated with <tt>destroy</tt>. If
|
217
|
+
# the <tt>before_destroy</tt> callback return +false+ the action is cancelled
|
218
|
+
# and <tt>destroy</tt> returns +false+. See
|
219
|
+
# ActiveRecord::Callbacks for further details.
|
220
|
+
def destroy
|
221
|
+
raise ReadOnlyRecord if readonly?
|
222
|
+
# todo: callbacks?
|
223
|
+
instance_resource.delete if persisted?
|
224
|
+
@destroyed = true
|
225
|
+
freeze
|
226
|
+
end
|
227
|
+
|
228
|
+
# Deletes the record in the database and freezes this instance to reflect
|
229
|
+
# that no changes should be made (since they can't be persisted).
|
230
|
+
#
|
231
|
+
# There's a series of callbacks associated with <tt>destroy!</tt>. If
|
232
|
+
# the <tt>before_destroy</tt> callback return +false+ the action is cancelled
|
233
|
+
# and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
|
234
|
+
# ActiveRecord::Callbacks for further details.
|
235
|
+
def destroy!
|
236
|
+
destroy || raise(ActiveRecord::RecordNotDestroyed)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Returns true if this object has been destroyed, otherwise returns false.
|
240
|
+
def destroyed?
|
241
|
+
!!@destroyed
|
242
|
+
end
|
243
|
+
|
244
|
+
# Reloads the attributes of this object from the database.
|
245
|
+
def reload
|
246
|
+
return false if !persisted?
|
247
|
+
fresh_object = self.class.find(id)
|
248
|
+
refresh_data fresh_object.instance_variable_get('@attributes')
|
249
|
+
self
|
250
|
+
end
|
251
|
+
|
252
|
+
# Updates the attributes of the model from the passed-in hash and saves the
|
253
|
+
# record, all wrapped in a transaction. If the object is invalid, the saving
|
254
|
+
# will fail and false will be returned.
|
255
|
+
def update_attributes(attributes)
|
256
|
+
assign_attributes attributes
|
257
|
+
save
|
258
|
+
end
|
259
|
+
|
260
|
+
# Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
|
261
|
+
# of +save+, so an exception is raised if the record is invalid.
|
262
|
+
def update_attributes!(attributes)
|
263
|
+
assign_attributes attributes
|
264
|
+
save!
|
265
|
+
end
|
266
|
+
|
267
|
+
# Note that whenever you include ActiveModel::AttributeMethods in your class,
|
268
|
+
# it requires you to implement an +attributes+ method which returns a hash
|
269
|
+
# with each attribute name in your model as hash key and the attribute value as
|
270
|
+
# hash value.
|
271
|
+
#
|
272
|
+
# Hash keys must be strings.
|
273
|
+
def attributes
|
274
|
+
@attributes ||= self.class.class_attributes.clone
|
275
|
+
end
|
276
|
+
|
277
|
+
def id; self.uuid end
|
278
|
+
def created_at; self.created end
|
279
|
+
def updated_at; self.modified end
|
280
|
+
|
281
|
+
|
282
|
+
protected
|
283
|
+
|
284
|
+
|
285
|
+
def assign_attributes(attrs)
|
286
|
+
attrs.each do |attr,value|
|
287
|
+
attr = attr.to_s
|
288
|
+
unless attributes[attr] == value
|
289
|
+
attribute_will_change!(attr) unless RESERVED_ATTRIBUTES.include? attr
|
290
|
+
attributes[attr] = value
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def create_or_update
|
296
|
+
raise ReadOnlyRecord if readonly?
|
297
|
+
if valid?
|
298
|
+
run_callbacks :save do
|
299
|
+
return new_record? ? do_create : do_update
|
300
|
+
end
|
301
|
+
end
|
302
|
+
false
|
303
|
+
end
|
304
|
+
|
305
|
+
def do_create
|
306
|
+
group_resource.post(unsaved_attributes) do |resp, req, res, &block|
|
307
|
+
if resp.code.to_s == "200" || resp.code.to_s == "201"
|
308
|
+
refresh_data resp.entity_data
|
309
|
+
return true
|
310
|
+
else
|
311
|
+
errors.add(resp.code.to_s, resp)
|
312
|
+
return false
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def do_update
|
318
|
+
return false unless changed?
|
319
|
+
|
320
|
+
instance_resource.put(unsaved_attributes) do |resp, req, res, &block|
|
321
|
+
if resp.code.to_s == "200" || resp.code.to_s == "201"
|
322
|
+
refresh_data resp.entity_data
|
323
|
+
return true
|
324
|
+
else
|
325
|
+
errors.add(resp.code, resp)
|
326
|
+
return false
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def unsaved_attributes
|
332
|
+
HashWithIndifferentAccess[changed.collect {|k| [k, attributes[k]]}]
|
333
|
+
end
|
334
|
+
|
335
|
+
def group_resource
|
336
|
+
self.class.resource
|
337
|
+
end
|
338
|
+
|
339
|
+
def instance_resource
|
340
|
+
self.class.resource["#{self.id}"]
|
341
|
+
end
|
342
|
+
|
343
|
+
def refresh_data(entity_data)
|
344
|
+
@previously_changed = changes
|
345
|
+
@changed_attributes.clear
|
346
|
+
@attributes = HashWithIndifferentAccess.new entity_data
|
347
|
+
end
|
348
|
+
|
349
|
+
def attribute_will_change!(attr)
|
350
|
+
begin
|
351
|
+
value = __send__(attr)
|
352
|
+
value = value.duplicable? ? value.clone : value
|
353
|
+
rescue TypeError, NoMethodError
|
354
|
+
end
|
355
|
+
|
356
|
+
changed_attributes[attr] = value unless changed_attributes.include?(attr)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|