parse_resource 1.7.1 → 1.7.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/Gemfile +0 -2
- data/README.md +76 -5
- data/VERSION +1 -1
- data/lib/.DS_Store +0 -0
- data/lib/parse_resource.rb +7 -6
- data/lib/parse_resource/base.rb +459 -0
- data/lib/{parse_error.rb → parse_resource/parse_error.rb} +1 -1
- data/lib/parse_resource/parse_exceptions.rb +2 -0
- data/lib/parse_resource/parse_user.rb +61 -0
- data/lib/{parse_user_validator.rb → parse_resource/parse_user_validator.rb} +0 -0
- data/lib/{query.rb → parse_resource/query.rb} +10 -2
- data/parse_resource.gemspec +11 -8
- data/test/test_associations.rb +22 -5
- data/test/test_parse_resource.rb +22 -1
- data/test/test_query_options.rb +33 -0
- metadata +31 -28
- data/lib/base.rb +0 -406
- data/lib/parse_user.rb +0 -26
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -26,7 +26,7 @@ Installation
|
|
26
26
|
Include in your `Gemfile`:
|
27
27
|
|
28
28
|
```ruby
|
29
|
-
gem "parse_resource", "~> 1.7.
|
29
|
+
gem "parse_resource", "~> 1.7.1"
|
30
30
|
```
|
31
31
|
|
32
32
|
Or just gem install:
|
@@ -124,6 +124,10 @@ posts.map {|p| p.title} #=> ["Unpaid blogger", "Uncrunched"]
|
|
124
124
|
id = "DjiH4Qffke"
|
125
125
|
p = Post.find(id) #simple find by id
|
126
126
|
|
127
|
+
# ActiveRecord style find commands
|
128
|
+
Post.find_by_title("Uncrunched") #=> A Post object
|
129
|
+
Post.find_all_by_author("Arrington") #=> An Array of Posts
|
130
|
+
|
127
131
|
# you can chain method calls, just like in ActiveRecord
|
128
132
|
Post.where(:param1 => "foo").where(:param2 => "bar").all
|
129
133
|
|
@@ -138,16 +142,76 @@ posts.length #=> 5
|
|
138
142
|
Post.where(:bar => "foo").count #=> 1337
|
139
143
|
```
|
140
144
|
|
145
|
+
Users
|
146
|
+
|
147
|
+
Note: Because users are special in the Parse API, you must name your class User if you want to subclass ParseUser.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
# app/models/user.rb
|
151
|
+
class User < ParseUser
|
152
|
+
# no validations included, but feel free to add your own
|
153
|
+
validates_presence_of :username
|
154
|
+
|
155
|
+
# you can add fields, like any other kind of Object...
|
156
|
+
fields :name, :bio
|
157
|
+
|
158
|
+
# but note that email is a special field in the Parse API.
|
159
|
+
fields :email
|
160
|
+
end
|
161
|
+
|
162
|
+
# create a user
|
163
|
+
user = User.new(:username => "adelevie")
|
164
|
+
user.password = "asecretpassword"
|
165
|
+
user.save
|
166
|
+
# after saving, the password is automatically hashed by Parse's server
|
167
|
+
# user.password will return the unhashed password when the original object is in memory
|
168
|
+
# from a new session, User.where(:username => "adelevie").first.password will return nil
|
169
|
+
|
170
|
+
# check if a user is logged in
|
171
|
+
User.authenticate("adelevie", "foooo") #=> false
|
172
|
+
User.authenticate("adelevie", "asecretpassword") #=> #<User...>
|
173
|
+
|
174
|
+
|
175
|
+
# A simple controller to authenticate users
|
176
|
+
class SessionsController < ApplicationController
|
177
|
+
def new
|
178
|
+
end
|
179
|
+
|
180
|
+
def create
|
181
|
+
user = User.authenticate(params[:username], params[:password])
|
182
|
+
if user
|
183
|
+
session[:user_id] = user.id
|
184
|
+
redirect_to root_url, :notice => "logged in !"
|
185
|
+
else
|
186
|
+
flash.now.alert = "Invalid username or password"
|
187
|
+
render "new"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def destroy
|
192
|
+
session[:user_id] = nil
|
193
|
+
redirect_to root_url, :notice => "Logged out!"
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
```
|
199
|
+
|
200
|
+
If you want to use parse_resource to back a simple authentication system for a Rails app, follow this [tutorial](http://asciicasts.com/episodes/250-authentication-from-scratch), and make some simple modifications.
|
201
|
+
|
202
|
+
|
141
203
|
Associations
|
142
204
|
|
143
205
|
```ruby
|
144
206
|
class Post < ParseResource::Base
|
145
|
-
|
207
|
+
# As with ActiveRecord, associations names can differ from class names...
|
208
|
+
belongs_to :author, :class_name => 'User'
|
146
209
|
fields :title, :body
|
147
210
|
end
|
148
211
|
|
149
|
-
class
|
150
|
-
|
212
|
+
class User < ParseUser
|
213
|
+
# ... but on the other end, use :inverse_of to complete the link.
|
214
|
+
has_many :posts, :inverse_of => :author
|
151
215
|
field :name
|
152
216
|
end
|
153
217
|
|
@@ -163,8 +227,15 @@ author.posts << post2
|
|
163
227
|
post3 = Post.create(:title => "Goosebumps 3")
|
164
228
|
post3.author = author
|
165
229
|
post3.save
|
166
|
-
```
|
167
230
|
|
231
|
+
# relational queries
|
232
|
+
posts = Post.include_object(:author).all
|
233
|
+
posts.each do |post|
|
234
|
+
puts post.author.name
|
235
|
+
# because you used Post#include_object, calling post.title won't execute a new query
|
236
|
+
# this is similar to ActiveRecord's eager loading
|
237
|
+
end
|
238
|
+
```
|
168
239
|
|
169
240
|
Documentation
|
170
241
|
-------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.7.
|
1
|
+
1.7.2
|
data/lib/.DS_Store
ADDED
Binary file
|
data/lib/parse_resource.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
require 'base'
|
2
|
-
require 'query'
|
3
|
-
require 'parse_user'
|
4
|
-
require 'parse_user_validator'
|
5
|
-
require 'parse_error'
|
1
|
+
require 'parse_resource/base'
|
2
|
+
require 'parse_resource/query'
|
3
|
+
require 'parse_resource/parse_user'
|
4
|
+
require 'parse_resource/parse_user_validator'
|
5
|
+
require 'parse_resource/parse_error'
|
6
|
+
|
6
7
|
|
7
8
|
module ParseResource
|
8
|
-
end
|
9
|
+
end
|
@@ -0,0 +1,459 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require "active_model"
|
4
|
+
require "erb"
|
5
|
+
require "rest-client"
|
6
|
+
require "json"
|
7
|
+
require "active_support/hash_with_indifferent_access"
|
8
|
+
require "parse_resource/query"
|
9
|
+
require "parse_resource/parse_error"
|
10
|
+
require "parse_resource/parse_exceptions"
|
11
|
+
|
12
|
+
module ParseResource
|
13
|
+
|
14
|
+
|
15
|
+
class Base
|
16
|
+
# ParseResource::Base provides an easy way to use Ruby to interace with a Parse.com backend
|
17
|
+
# Usage:
|
18
|
+
# class Post < ParseResource::Base
|
19
|
+
# fields :title, :author, :body
|
20
|
+
# end
|
21
|
+
|
22
|
+
include ActiveModel::Validations
|
23
|
+
include ActiveModel::Conversion
|
24
|
+
include ActiveModel::AttributeMethods
|
25
|
+
extend ActiveModel::Naming
|
26
|
+
extend ActiveModel::Callbacks
|
27
|
+
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
|
28
|
+
|
29
|
+
define_model_callbacks :save, :create, :update, :destroy
|
30
|
+
|
31
|
+
# Instantiates a ParseResource::Base object
|
32
|
+
#
|
33
|
+
# @params [Hash], [Boolean] a `Hash` of attributes and a `Boolean` that should be false only if the object already exists
|
34
|
+
# @return [ParseResource::Base] an object that subclasses `Parseresource::Base`
|
35
|
+
def initialize(attributes = {}, new=true)
|
36
|
+
#attributes = HashWithIndifferentAccess.new(attributes)
|
37
|
+
|
38
|
+
if new
|
39
|
+
@unsaved_attributes = attributes
|
40
|
+
else
|
41
|
+
@unsaved_attributes = {}
|
42
|
+
end
|
43
|
+
self.attributes = {}
|
44
|
+
|
45
|
+
self.attributes.merge!(attributes)
|
46
|
+
self.attributes unless self.attributes.empty?
|
47
|
+
create_setters_and_getters!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Explicitly adds a field to the model.
|
51
|
+
#
|
52
|
+
# @param [Symbol] name the name of the field, eg `:author`.
|
53
|
+
# @param [Boolean] val the return value of the field. Only use this within the class.
|
54
|
+
def self.field(name, val=nil)
|
55
|
+
class_eval do
|
56
|
+
define_method(name) do
|
57
|
+
@attributes[name] ? @attributes[name] : @unsaved_attributes[name]
|
58
|
+
end
|
59
|
+
define_method("#{name}=") do |val|
|
60
|
+
val = val.to_pointer if val.respond_to?(:to_pointer)
|
61
|
+
|
62
|
+
@attributes[name] = val
|
63
|
+
@unsaved_attributes[name] = val
|
64
|
+
|
65
|
+
val
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Add multiple fields in one line. Same as `#field`, but accepts multiple args.
|
71
|
+
#
|
72
|
+
# @param [Array] *args an array of `Symbol`s, `eg :author, :body, :title`.
|
73
|
+
def self.fields(*args)
|
74
|
+
args.each {|f| field(f)}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Similar to its ActiveRecord counterpart.
|
78
|
+
#
|
79
|
+
# @param [Hash] options Added so that you can specify :class_name => '...'. It does nothing at all, but helps you write self-documenting code.
|
80
|
+
def self.belongs_to(parent, options = {})
|
81
|
+
field(parent)
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_pointer
|
85
|
+
klass_name = self.class.model_name
|
86
|
+
klass_name = "_User" if klass_name == "User"
|
87
|
+
{"__type" => "Pointer", "className" => klass_name, "objectId" => self.id}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates setter methods for model fields
|
91
|
+
def create_setters!(k,v)
|
92
|
+
self.class.send(:define_method, "#{k}=") do |val|
|
93
|
+
val = val.to_pointer if val.respond_to?(:to_pointer)
|
94
|
+
|
95
|
+
@attributes[k.to_s] = val
|
96
|
+
@unsaved_attributes[k.to_s] = val
|
97
|
+
|
98
|
+
val
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.method_missing(name, *args)
|
103
|
+
name = name.to_s
|
104
|
+
if name.start_with?("find_by_")
|
105
|
+
attribute = name.gsub(/^find_by_/,"")
|
106
|
+
finder_name = "find_all_by_#{attribute}"
|
107
|
+
|
108
|
+
define_singleton_method(finder_name) do |target_value|
|
109
|
+
where({attribute.to_sym => target_value}).first
|
110
|
+
end
|
111
|
+
|
112
|
+
send(finder_name, args[0])
|
113
|
+
|
114
|
+
elsif name.start_with?("find_all_by_")
|
115
|
+
attribute = name.gsub(/^find_all_by_/,"")
|
116
|
+
finder_name = "find_all_by_#{attribute}"
|
117
|
+
|
118
|
+
define_singleton_method(finder_name) do |target_value|
|
119
|
+
where({attribute.to_sym => target_value}).all
|
120
|
+
end
|
121
|
+
|
122
|
+
send(finder_name, args[0])
|
123
|
+
else
|
124
|
+
super(name.to_sym, *args)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Creates getter methods for model fields
|
129
|
+
def create_getters!(k,v)
|
130
|
+
self.class.send(:define_method, "#{k}") do
|
131
|
+
|
132
|
+
case @attributes[k]
|
133
|
+
when Hash
|
134
|
+
|
135
|
+
klass_name = @attributes[k]["className"]
|
136
|
+
klass_name = "User" if klass_name == "_User"
|
137
|
+
|
138
|
+
case @attributes[k]["__type"]
|
139
|
+
when "Pointer"
|
140
|
+
result = klass_name.constantize.find(@attributes[k]["objectId"])
|
141
|
+
when "Object"
|
142
|
+
result = klass_name.constantize.new(@attributes[k], false)
|
143
|
+
end #todo: support Dates and other types https://www.parse.com/docs/rest#objects-types
|
144
|
+
|
145
|
+
else
|
146
|
+
result = @attributes[k]
|
147
|
+
end
|
148
|
+
|
149
|
+
result
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def create_setters_and_getters!
|
154
|
+
@attributes.each_pair do |k,v|
|
155
|
+
create_setters!(k,v)
|
156
|
+
create_getters!(k,v)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.has_many(children, options = {})
|
161
|
+
options.stringify_keys!
|
162
|
+
|
163
|
+
parent_klass_name = model_name
|
164
|
+
lowercase_parent_klass_name = parent_klass_name.downcase
|
165
|
+
parent_klass = model_name.constantize
|
166
|
+
child_klass_name = options['class_name'] || children.to_s.singularize.camelize
|
167
|
+
child_klass = child_klass_name.constantize
|
168
|
+
|
169
|
+
if parent_klass_name == "User"
|
170
|
+
parent_klass_name = "_User"
|
171
|
+
end
|
172
|
+
|
173
|
+
@@parent_klass_name = parent_klass_name
|
174
|
+
@@options ||= {}
|
175
|
+
@@options[children] ||= {}
|
176
|
+
@@options[children].merge!(options)
|
177
|
+
|
178
|
+
send(:define_method, children) do
|
179
|
+
@@parent_id = self.id
|
180
|
+
@@parent_instance = self
|
181
|
+
|
182
|
+
parent_klass_name = case
|
183
|
+
when @@options[children]['inverse_of'] then @@options[children]['inverse_of'].downcase
|
184
|
+
when @@parent_klass_name == "User" then "_User"
|
185
|
+
else @@parent_klass_name.downcase
|
186
|
+
end
|
187
|
+
|
188
|
+
query = child_klass.where(parent_klass_name.to_sym => @@parent_instance.to_pointer)
|
189
|
+
singleton = query.all
|
190
|
+
|
191
|
+
class << singleton
|
192
|
+
def <<(child)
|
193
|
+
parent_klass_name = case
|
194
|
+
when @@options[children]['inverse_of'] then @@options[children]['inverse_of'].downcase
|
195
|
+
when @@parent_klass_name == "User" then @@parent_klass_name
|
196
|
+
else @@parent_klass_name.downcase
|
197
|
+
end
|
198
|
+
if @@parent_instance.respond_to?(:to_pointer)
|
199
|
+
child.send("#{parent_klass_name}=", @@parent_instance.to_pointer)
|
200
|
+
child.save
|
201
|
+
end
|
202
|
+
super(child)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
singleton
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
@@settings ||= nil
|
212
|
+
|
213
|
+
# Explicitly set Parse.com API keys.
|
214
|
+
#
|
215
|
+
# @param [String] app_id the Application ID of your Parse database
|
216
|
+
# @param [String] master_key the Master Key of your Parse database
|
217
|
+
def self.load!(app_id, master_key)
|
218
|
+
@@settings = {"app_id" => app_id, "master_key" => master_key}
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.settings
|
222
|
+
if @@settings.nil?
|
223
|
+
path = "config/parse_resource.yml"
|
224
|
+
#environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV["RACK_ENV"]
|
225
|
+
environment = ENV["RACK_ENV"]
|
226
|
+
@@settings = YAML.load(ERB.new(File.new(path).read).result)[environment]
|
227
|
+
end
|
228
|
+
@@settings
|
229
|
+
end
|
230
|
+
|
231
|
+
# Creates a RESTful resource
|
232
|
+
# sends requests to [base_uri]/[classname]
|
233
|
+
#
|
234
|
+
def self.resource
|
235
|
+
if @@settings.nil?
|
236
|
+
path = "config/parse_resource.yml"
|
237
|
+
environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV["RACK_ENV"]
|
238
|
+
@@settings = YAML.load(ERB.new(File.new(path).read).result)[environment]
|
239
|
+
end
|
240
|
+
|
241
|
+
if model_name == "User" #https://parse.com/docs/rest#users-signup
|
242
|
+
base_uri = "https://api.parse.com/1/users"
|
243
|
+
else
|
244
|
+
base_uri = "https://api.parse.com/1/classes/#{model_name}"
|
245
|
+
end
|
246
|
+
|
247
|
+
#refactor to settings['app_id'] etc
|
248
|
+
app_id = @@settings['app_id']
|
249
|
+
master_key = @@settings['master_key']
|
250
|
+
RestClient::Resource.new(base_uri, app_id, master_key)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Find a ParseResource::Base object by ID
|
254
|
+
#
|
255
|
+
# @param [String] id the ID of the Parse object you want to find.
|
256
|
+
# @return [ParseResource] an object that subclasses ParseResource.
|
257
|
+
def self.find(id)
|
258
|
+
raise RecordNotFound if id.blank?
|
259
|
+
where(:objectId => id).first
|
260
|
+
end
|
261
|
+
|
262
|
+
# Find a ParseResource::Base object by chaining #where method calls.
|
263
|
+
#
|
264
|
+
def self.where(*args)
|
265
|
+
Query.new(self).where(*args)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Include the attributes of a parent ojbect in the results
|
269
|
+
# Similar to ActiveRecord eager loading
|
270
|
+
#
|
271
|
+
def self.include_object(parent)
|
272
|
+
Query.new(self).include_object(parent)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Add this at the end of a method chain to get the count of objects, instead of an Array of objects
|
276
|
+
def self.count
|
277
|
+
#https://www.parse.com/docs/rest#queries-counting
|
278
|
+
Query.new(self).count(1)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Find all ParseResource::Base objects for that model.
|
282
|
+
#
|
283
|
+
# @return [Array] an `Array` of objects that subclass `ParseResource`.
|
284
|
+
def self.all
|
285
|
+
Query.new(self).all
|
286
|
+
end
|
287
|
+
|
288
|
+
# Find the first object. Fairly random, not based on any specific condition.
|
289
|
+
#
|
290
|
+
def self.first
|
291
|
+
Query.new(self).limit(1).first
|
292
|
+
end
|
293
|
+
|
294
|
+
# Limits the number of objects returned
|
295
|
+
#
|
296
|
+
def self.limit(n)
|
297
|
+
Query.new(self).limit(n)
|
298
|
+
end
|
299
|
+
|
300
|
+
def self.order(attribute)
|
301
|
+
Query.new(self).order(attribute)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Create a ParseResource::Base object.
|
305
|
+
#
|
306
|
+
# @param [Hash] attributes a `Hash` of attributes
|
307
|
+
# @return [ParseResource] an object that subclasses `ParseResource`. Or returns `false` if object fails to save.
|
308
|
+
def self.create(attributes = {})
|
309
|
+
attributes = HashWithIndifferentAccess.new(attributes)
|
310
|
+
new(attributes).save
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.destroy_all
|
314
|
+
all.each do |object|
|
315
|
+
object.destroy
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def self.class_attributes
|
320
|
+
@class_attributes ||= {}
|
321
|
+
end
|
322
|
+
|
323
|
+
def persisted?
|
324
|
+
if id
|
325
|
+
true
|
326
|
+
else
|
327
|
+
false
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def new?
|
332
|
+
!persisted?
|
333
|
+
end
|
334
|
+
|
335
|
+
# delegate from Class method
|
336
|
+
def resource
|
337
|
+
self.class.resource
|
338
|
+
end
|
339
|
+
|
340
|
+
# create RESTful resource for the specific Parse object
|
341
|
+
# sends requests to [base_uri]/[classname]/[objectId]
|
342
|
+
def instance_resource
|
343
|
+
self.class.resource["#{self.id}"]
|
344
|
+
end
|
345
|
+
|
346
|
+
def create
|
347
|
+
opts = {:content_type => "application/json"}
|
348
|
+
attrs = @unsaved_attributes.to_json
|
349
|
+
result = self.resource.post(attrs, opts) do |resp, req, res, &block|
|
350
|
+
|
351
|
+
case resp.code
|
352
|
+
when 400
|
353
|
+
|
354
|
+
# https://www.parse.com/docs/ios/api/Classes/PFConstants.html
|
355
|
+
error_response = JSON.parse(resp)
|
356
|
+
pe = ParseError.new(error_response["code"]).to_array
|
357
|
+
self.errors.add(pe[0], pe[1])
|
358
|
+
|
359
|
+
else
|
360
|
+
@attributes.merge!(JSON.parse(resp))
|
361
|
+
@attributes.merge!(@unsaved_attributes)
|
362
|
+
attributes = HashWithIndifferentAccess.new(attributes)
|
363
|
+
@unsaved_attributes = {}
|
364
|
+
create_setters_and_getters!
|
365
|
+
end
|
366
|
+
|
367
|
+
self
|
368
|
+
end
|
369
|
+
|
370
|
+
result
|
371
|
+
end
|
372
|
+
|
373
|
+
def save
|
374
|
+
if valid?
|
375
|
+
run_callbacks :save do
|
376
|
+
new? ? create : update
|
377
|
+
end
|
378
|
+
else
|
379
|
+
false
|
380
|
+
end
|
381
|
+
rescue false
|
382
|
+
end
|
383
|
+
|
384
|
+
def update(attributes = {})
|
385
|
+
|
386
|
+
attributes = HashWithIndifferentAccess.new(attributes)
|
387
|
+
|
388
|
+
@unsaved_attributes.merge!(attributes)
|
389
|
+
|
390
|
+
put_attrs = @unsaved_attributes
|
391
|
+
put_attrs.delete('objectId')
|
392
|
+
put_attrs.delete('createdAt')
|
393
|
+
put_attrs.delete('updatedAt')
|
394
|
+
put_attrs = put_attrs.to_json
|
395
|
+
|
396
|
+
opts = {:content_type => "application/json"}
|
397
|
+
result = self.instance_resource.put(put_attrs, opts) do |resp, req, res, &block|
|
398
|
+
|
399
|
+
case resp.code
|
400
|
+
when 400
|
401
|
+
|
402
|
+
# https://www.parse.com/docs/ios/api/Classes/PFConstants.html
|
403
|
+
error_response = JSON.parse(resp)
|
404
|
+
pe = ParseError.new(error_response["code"], error_response["error"]).to_array
|
405
|
+
self.errors.add(pe[0], pe[1])
|
406
|
+
|
407
|
+
else
|
408
|
+
|
409
|
+
@attributes.merge!(JSON.parse(resp))
|
410
|
+
@attributes.merge!(@unsaved_attributes)
|
411
|
+
@unsaved_attributes = {}
|
412
|
+
create_setters_and_getters!
|
413
|
+
|
414
|
+
self
|
415
|
+
end
|
416
|
+
|
417
|
+
result
|
418
|
+
end
|
419
|
+
|
420
|
+
end
|
421
|
+
|
422
|
+
def update_attributes(attributes = {})
|
423
|
+
self.update(attributes)
|
424
|
+
end
|
425
|
+
|
426
|
+
def destroy
|
427
|
+
self.instance_resource.delete
|
428
|
+
@attributes = {}
|
429
|
+
@unsaved_attributes = {}
|
430
|
+
nil
|
431
|
+
end
|
432
|
+
|
433
|
+
# provides access to @attributes for getting and setting
|
434
|
+
def attributes
|
435
|
+
@attributes ||= self.class.class_attributes
|
436
|
+
@attributes
|
437
|
+
end
|
438
|
+
|
439
|
+
def attributes=(n)
|
440
|
+
@attributes = n
|
441
|
+
@attributes
|
442
|
+
end
|
443
|
+
|
444
|
+
# aliasing for idiomatic Ruby
|
445
|
+
def id; self.objectId rescue nil; end
|
446
|
+
|
447
|
+
def created_at; self.createdAt; end
|
448
|
+
|
449
|
+
def updated_at; self.updatedAt rescue nil; end
|
450
|
+
|
451
|
+
def self.included(base)
|
452
|
+
base.extend(ClassMethods)
|
453
|
+
end
|
454
|
+
|
455
|
+
module ClassMethods
|
456
|
+
end
|
457
|
+
|
458
|
+
end
|
459
|
+
end
|