usergrid_ironhorse 0.0.2
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.
- 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
|