parse-stack 1.5.2 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +40 -80
- data/lib/parse/api/all.rb +7 -0
- data/lib/parse/api/analytics.rb +8 -3
- data/lib/parse/api/apps.rb +29 -1
- data/lib/parse/api/batch.rb +14 -129
- data/lib/parse/api/cloud_functions.rb +9 -0
- data/lib/parse/api/config.rb +10 -1
- data/lib/parse/api/files.rb +7 -2
- data/lib/parse/api/hooks.rb +45 -2
- data/lib/parse/api/objects.rb +43 -6
- data/lib/parse/api/push.rb +6 -1
- data/lib/parse/api/schemas.rb +15 -1
- data/lib/parse/api/sessions.rb +5 -0
- data/lib/parse/api/users.rb +64 -5
- data/lib/parse/client/authentication.rb +25 -8
- data/lib/parse/client/batch.rb +206 -0
- data/lib/parse/client/body_builder.rb +12 -6
- data/lib/parse/client/caching.rb +42 -10
- data/lib/parse/client/protocol.rb +51 -46
- data/lib/parse/client/response.rb +1 -47
- data/lib/parse/client.rb +171 -42
- data/lib/parse/model/acl.rb +184 -39
- data/lib/parse/model/associations/belongs_to.rb +1 -0
- data/lib/parse/model/classes/role.rb +7 -1
- data/lib/parse/model/classes/session.rb +7 -3
- data/lib/parse/model/classes/user.rb +107 -0
- data/lib/parse/model/core/actions.rb +166 -115
- data/lib/parse/model/core/fetching.rb +105 -0
- data/lib/parse/model/core/properties.rb +40 -13
- data/lib/parse/model/core/querying.rb +123 -39
- data/lib/parse/model/core/schema.rb +22 -32
- data/lib/parse/model/object.rb +26 -20
- data/lib/parse/model/pointer.rb +1 -0
- data/lib/parse/query/constraint.rb +65 -27
- data/lib/parse/query/constraints.rb +0 -3
- data/lib/parse/query/operation.rb +33 -22
- data/lib/parse/query/ordering.rb +10 -5
- data/lib/parse/stack/generators/rails.rb +5 -1
- data/lib/parse/stack/generators/templates/model_installation.rb +1 -1
- data/lib/parse/stack/generators/templates/model_role.rb +1 -1
- data/lib/parse/stack/generators/templates/model_session.rb +2 -2
- data/lib/parse/stack/generators/templates/model_user.rb +1 -1
- data/lib/parse/stack/generators/templates/parse.rb +0 -1
- data/lib/parse/stack/railtie.rb +1 -0
- data/lib/parse/stack/tasks.rb +3 -1
- data/lib/parse/stack/version.rb +3 -1
- data/lib/parse/webhooks/registration.rb +3 -3
- data/lib/parse/webhooks.rb +88 -7
- metadata +5 -3
@@ -8,26 +8,34 @@ require 'active_support/core_ext'
|
|
8
8
|
require 'time'
|
9
9
|
require 'parallel'
|
10
10
|
require_relative '../../client/request'
|
11
|
+
require_relative 'fetching'
|
11
12
|
|
12
|
-
|
13
|
-
# A Parse::RelationAction is special operation that adds one object to a relational
|
14
|
-
# table as to another. Depending on the polarity of the action, the objects are
|
15
|
-
# either added or removed from the relation. This class is used to generate the proper
|
16
|
-
# hash request format Parse needs in order to modify relational information for classes.
|
17
13
|
module Parse
|
14
|
+
# A Parse::RelationAction is special operation that adds one object to a relational
|
15
|
+
# table as to another. Depending on the polarity of the action, the objects are
|
16
|
+
# either added or removed from the relation. This class is used to generate the proper
|
17
|
+
# hash request formats Parse needs in order to modify relational information for classes.
|
18
18
|
class RelationAction
|
19
19
|
ADD = "AddRelation"
|
20
20
|
REMOVE = "RemoveRelation"
|
21
|
+
# @!attribute polarity
|
22
|
+
# @return [Boolean] whether it is an addition (true) or removal (false) action.
|
23
|
+
# @!attribute key
|
24
|
+
# @return [String] the name of the Parse field (column).
|
25
|
+
# @!attribute objects
|
26
|
+
# @return [Array<Parse::Object>] the set of objects in this relation action.
|
21
27
|
attr_accessor :polarity, :key, :objects
|
22
|
-
|
23
|
-
#
|
28
|
+
|
29
|
+
# @param field [String] the name of the Parse field tied to this relation.
|
30
|
+
# @param polarity [Boolean] whether this is an addition (true) or removal (false) action.
|
31
|
+
# @param objects [Array<Parse::Object>] the set of objects tied to this relation action.
|
24
32
|
def initialize(field, polarity: true, objects: [])
|
25
33
|
@key = field.to_s
|
26
34
|
self.polarity = polarity
|
27
|
-
@objects =
|
35
|
+
@objects = Array.wrap(objects).compact
|
28
36
|
end
|
29
37
|
|
30
|
-
#
|
38
|
+
# @return [Hash] a hash representing a relation operation.
|
31
39
|
def as_json(*args)
|
32
40
|
{ @key =>
|
33
41
|
{
|
@@ -57,13 +65,36 @@ module Parse
|
|
57
65
|
end
|
58
66
|
end
|
59
67
|
|
68
|
+
# Defines some of the save, update and destroy operations for Parse objects.
|
60
69
|
module Actions
|
61
|
-
|
70
|
+
# @!visibility private
|
62
71
|
def self.included(base)
|
63
72
|
base.extend(ClassMethods)
|
64
73
|
end
|
65
74
|
|
75
|
+
# Class methods applied to Parse::Object subclasses.
|
66
76
|
module ClassMethods
|
77
|
+
# @!attribute raise_on_save_failure
|
78
|
+
# By default, we return `true` or `false` for save and destroy operations.
|
79
|
+
# If you prefer to have `Parse::Object` raise an exception instead, you
|
80
|
+
# can tell to do so either globally or on a per-model basis. When a save
|
81
|
+
# fails, it will raise a {Parse::SaveFailureError}.
|
82
|
+
#
|
83
|
+
# When enabled, if an error is returned by Parse due to saving or
|
84
|
+
# destroying a record, due to your `before_save` or `before_delete`
|
85
|
+
# validation cloud code triggers, `Parse::Object` will return the a
|
86
|
+
# {Parse::SaveFailureError} exception type. This exception has an instance
|
87
|
+
# method of `#object` which contains the object that failed to save.
|
88
|
+
# @example
|
89
|
+
# # globally across all models
|
90
|
+
# Parse::Model.raise_on_save_failure = true
|
91
|
+
# Song.raise_on_save_failure = true # per-model
|
92
|
+
#
|
93
|
+
# # or per-instance raise on failure
|
94
|
+
# song.save!
|
95
|
+
#
|
96
|
+
# @return [Boolean] whether to raise a {Parse::SaveFailureError}
|
97
|
+
# when an object fails to save.
|
67
98
|
attr_accessor :raise_on_save_failure
|
68
99
|
|
69
100
|
def raise_on_save_failure
|
@@ -71,8 +102,14 @@ module Parse
|
|
71
102
|
Parse::Model.raise_on_save_failure
|
72
103
|
end
|
73
104
|
|
105
|
+
# Finds the first object matching the query conditions, or creates a new
|
106
|
+
# unsaved object with the attributes.
|
107
|
+
# the provided query attributes, otherwise create
|
108
|
+
# @param query_attrs [Hash] a set of query constraints that also are applied.
|
109
|
+
# @param resource_attrs [Hash] a set of attribute values to be applied if an object was not found.
|
110
|
+
# @return [Parse::Object] a Parse::Object, whether found by the query or newly created.
|
74
111
|
def first_or_create(query_attrs = {}, resource_attrs = {})
|
75
|
-
|
112
|
+
|
76
113
|
query_attrs.symbolize_keys!
|
77
114
|
resource_attrs.symbolize_keys!
|
78
115
|
obj = query(query_attrs).first
|
@@ -85,7 +122,20 @@ module Parse
|
|
85
122
|
obj
|
86
123
|
end
|
87
124
|
|
88
|
-
#
|
125
|
+
# Auto save all objects matching the query constraints. This method is
|
126
|
+
# meant to be used with a block. Any objects that are modified in the block
|
127
|
+
# will be batched for a save operation. This uses the `updated_at` field to
|
128
|
+
# continue to query for all matching objects that have not been updated.
|
129
|
+
# @param constraints [Hash] a set of query constraints.
|
130
|
+
# @yield a block which will iterate through each matching object.
|
131
|
+
# @example
|
132
|
+
#
|
133
|
+
# post = Post.first
|
134
|
+
# Comments.save_all( post: post) do |comment|
|
135
|
+
# # .. modify comment ...
|
136
|
+
# # it will automatically be saved
|
137
|
+
# end
|
138
|
+
# @return [Boolean] whether there were any errors.
|
89
139
|
def save_all(constraints = {})
|
90
140
|
force = false
|
91
141
|
|
@@ -151,6 +201,15 @@ module Parse
|
|
151
201
|
|
152
202
|
end # ClassMethods
|
153
203
|
|
204
|
+
# Perform an atomic operation on this field. This operation is done on the
|
205
|
+
# Parse server which guarantees the atomicity of the operation. This is the low-level
|
206
|
+
# API on performing atomic operations on properties for classes. These methods do not
|
207
|
+
# update the current instance with any changes the server may have made to satisfy this
|
208
|
+
# operation.
|
209
|
+
#
|
210
|
+
# @param field [String] the name of the field in the Parse collection.
|
211
|
+
# @param op_hash [Hash] The operation hash. It may also be of type {Parse::RelationAction}.
|
212
|
+
# @return [Boolean] whether the operation was successful.
|
154
213
|
def operate_field!(field, op_hash)
|
155
214
|
field = field.to_sym
|
156
215
|
field = self.field_map[field] || field
|
@@ -167,22 +226,47 @@ module Parse
|
|
167
226
|
response.success?
|
168
227
|
end
|
169
228
|
|
229
|
+
# Perform an atomic add operation to the array field.
|
230
|
+
# @param field [String] the name of the field in the Parse collection.
|
231
|
+
# @param objects [Array] the set of items to add to this field.
|
232
|
+
# @return [Boolean] whether it was successful
|
233
|
+
# @see #operate_field!
|
170
234
|
def op_add!(field,objects)
|
171
235
|
operate_field! field, { __op: :Add, objects: objects }
|
172
236
|
end
|
173
237
|
|
238
|
+
# Perform an atomic add unique operation to the array field. The objects will
|
239
|
+
# only be added if they don't already exists in the array for that particular field.
|
240
|
+
# @param field [String] the name of the field in the Parse collection.
|
241
|
+
# @param objects [Array] the set of items to add uniquely to this field.
|
242
|
+
# @return [Boolean] whether it was successful
|
243
|
+
# @see #operate_field!
|
174
244
|
def op_add_unique!(field,objects)
|
175
245
|
operate_field! field, { __op: :AddUnique, objects: objects }
|
176
246
|
end
|
177
247
|
|
248
|
+
# Perform an atomic remove operation to the array field.
|
249
|
+
# @param field [String] the name of the field in the Parse collection.
|
250
|
+
# @param objects [Array] the set of items to remove to this field.
|
251
|
+
# @return [Boolean] whether it was successful
|
252
|
+
# @see #operate_field!
|
178
253
|
def op_remove!(field, objects)
|
179
254
|
operate_field! field, { __op: :Remove, objects: objects }
|
180
255
|
end
|
181
256
|
|
257
|
+
# Perform an atomic delete operation on this field.
|
258
|
+
# @param field [String] the name of the field in the Parse collection.
|
259
|
+
# @return [Boolean] whether it was successful
|
260
|
+
# @see #operate_field!
|
182
261
|
def op_destroy!(field)
|
183
|
-
operate_field! field, { __op: :Delete }
|
262
|
+
operate_field! field, { __op: :Delete }.freeze
|
184
263
|
end
|
185
264
|
|
265
|
+
# Perform an atomic add operation on this relational field.
|
266
|
+
# @param field [String] the name of the field in the Parse collection.
|
267
|
+
# @param objects [Array<Parse::Object>] the set of objects to add to this relational field.
|
268
|
+
# @return [Boolean] whether it was successful
|
269
|
+
# @see #operate_field!
|
186
270
|
def op_add_relation!(field, objects = [])
|
187
271
|
objects = [objects] unless objects.is_a?(Array)
|
188
272
|
return false if objects.empty?
|
@@ -190,6 +274,11 @@ module Parse
|
|
190
274
|
operate_field! field, relation_action
|
191
275
|
end
|
192
276
|
|
277
|
+
# Perform an atomic remove operation on this relational field.
|
278
|
+
# @param field [String] the name of the field in the Parse collection.
|
279
|
+
# @param objects [Array<Parse::Object>] the set of objects to remove to this relational field.
|
280
|
+
# @return [Boolean] whether it was successful
|
281
|
+
# @see #operate_field!
|
193
282
|
def op_remove_relation!(field, objects = [])
|
194
283
|
objects = [objects] unless objects.is_a?(Array)
|
195
284
|
return false if objects.empty?
|
@@ -197,7 +286,18 @@ module Parse
|
|
197
286
|
operate_field! field, relation_action
|
198
287
|
end
|
199
288
|
|
200
|
-
#
|
289
|
+
# Atomically increment or decrement a specific field.
|
290
|
+
# @param field [String] the name of the field in the Parse collection.
|
291
|
+
# @param amount [Integer] the amoun to increment. Use negative values to decrement.
|
292
|
+
# @see #operate_field!
|
293
|
+
def op_increment!(field, amount = 1)
|
294
|
+
unless amount.is_a?(Numeric)
|
295
|
+
raise ArgumentError, "Amount should be numeric"
|
296
|
+
end
|
297
|
+
operate_field! field, { __op: :Increment, amount: amount.to_i }.freeze
|
298
|
+
end
|
299
|
+
|
300
|
+
# @return [Parse::Request] a destroy_request for the current object.
|
201
301
|
def destroy_request
|
202
302
|
return nil unless @id.present?
|
203
303
|
uri = self.uri_path
|
@@ -206,13 +306,16 @@ module Parse
|
|
206
306
|
r
|
207
307
|
end
|
208
308
|
|
309
|
+
# @return [String] the API uri path for this class.
|
209
310
|
def uri_path
|
210
311
|
self.client.url_prefix.path + Client.uri_path(self)
|
211
312
|
end
|
212
|
-
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
313
|
+
|
314
|
+
# Creates an array of all possible operations that need to be performed
|
315
|
+
# on this object. This includes all property and relational operation changes.
|
316
|
+
# @param force [Boolean] whether this object should be saved even if does not have
|
317
|
+
# pending changes.
|
318
|
+
# @return [Array<Parse::Request>] the list of API requests.
|
216
319
|
def change_requests(force = false)
|
217
320
|
requests = []
|
218
321
|
# get the URI path for this object.
|
@@ -244,6 +347,9 @@ module Parse
|
|
244
347
|
# information based on its local attributes. The bang implies that it will send
|
245
348
|
# the request even though it is possible no changes were performed. This is useful
|
246
349
|
# in kicking-off an beforeSave / afterSave hooks
|
350
|
+
# Save the object regardless of whether there are changes. This would call
|
351
|
+
# any beforeSave and afterSave cloud code hooks you have registered for this class.
|
352
|
+
# @return [Boolean] true/false whether it was successful.
|
247
353
|
def update!(raw: false)
|
248
354
|
if valid? == false
|
249
355
|
errors.full_messages.each do |msg|
|
@@ -262,13 +368,15 @@ module Parse
|
|
262
368
|
response.success?
|
263
369
|
end
|
264
370
|
|
265
|
-
#
|
371
|
+
# Save all the changes related to this object.
|
372
|
+
# @return [Boolean] true/false whether it was successful.
|
266
373
|
def update
|
267
374
|
return true unless attribute_changes?
|
268
375
|
update!
|
269
376
|
end
|
270
377
|
|
271
|
-
#
|
378
|
+
# Save the object as a new record, running all callbacks.
|
379
|
+
# @return [Boolean] true/false whether it was successful.
|
272
380
|
def create
|
273
381
|
run_callbacks :create do
|
274
382
|
res = client.create_object(parse_class, attribute_updates, session_token: _session_token)
|
@@ -287,6 +395,7 @@ module Parse
|
|
287
395
|
end
|
288
396
|
end
|
289
397
|
|
398
|
+
# @!visibility private
|
290
399
|
def _session_token
|
291
400
|
if @_session_token.respond_to?(:session_token)
|
292
401
|
@_session_token = @_session_token.session_token
|
@@ -298,6 +407,10 @@ module Parse
|
|
298
407
|
# we will create the object. If the object has an id, we will update the record.
|
299
408
|
# You can define before and after :save callbacks
|
300
409
|
# autoraise: set to true will automatically raise an exception if the save fails
|
410
|
+
# @raise Parse::SaveFailureError if the save fails
|
411
|
+
# @param autoraise [Boolean] whether to raise an exception if the save fails.
|
412
|
+
# @param session [String] a session token in order to apply ACLs to this operation.
|
413
|
+
# @return [Boolean] whether the save was successful.
|
301
414
|
def save(autoraise: false, session: nil)
|
302
415
|
return true unless changed?
|
303
416
|
success = false
|
@@ -332,13 +445,19 @@ module Parse
|
|
332
445
|
success
|
333
446
|
end
|
334
447
|
|
335
|
-
#
|
448
|
+
# Save this object and raise an exception if it fails.
|
449
|
+
# @raise Parse::SaveFailureError if the save fails
|
450
|
+
# @param session (see #save)
|
451
|
+
# @return (see #save)
|
336
452
|
def save!(session: nil)
|
337
453
|
save(autoraise: true, session: session)
|
338
454
|
end
|
339
455
|
|
340
|
-
|
341
|
-
#
|
456
|
+
|
457
|
+
# Delete this record from the Parse collection. Only valid if this object has an `id`.
|
458
|
+
# This will run all the `destroy` callbacks.
|
459
|
+
# @param session [String] a session token if you want to apply ACLs for a user in this operation.
|
460
|
+
# @return [Boolean] whether the operation was successful.
|
342
461
|
def destroy(session: nil)
|
343
462
|
return false if new?
|
344
463
|
@_session_token = session
|
@@ -358,10 +477,12 @@ module Parse
|
|
358
477
|
success
|
359
478
|
end
|
360
479
|
|
480
|
+
# Runs all the registered `before_save` related callbacks.
|
361
481
|
def prepare_save!
|
362
482
|
run_callbacks(:save) { false }
|
363
483
|
end
|
364
484
|
|
485
|
+
# @return [Hash] a hash of the list of changes made to this instance.
|
365
486
|
def changes_payload
|
366
487
|
h = attribute_updates
|
367
488
|
if relation_changes?
|
@@ -371,11 +492,14 @@ module Parse
|
|
371
492
|
h.merge!(className: parse_class) unless h.empty?
|
372
493
|
h.as_json
|
373
494
|
end
|
374
|
-
|
375
495
|
alias_method :update_payload, :changes_payload
|
376
496
|
|
377
|
-
#
|
378
|
-
#
|
497
|
+
# Generates an array with two entries for addition and removal operations. The first entry
|
498
|
+
# of the array will contain a hash of all the change operations regarding adding new relational
|
499
|
+
# objects. The second entry in the array is a hash of all the change operations regarding removing
|
500
|
+
# relation objects from this field.
|
501
|
+
# @return [Array] an array with two hashes; the first is a hash of all the addition operations and
|
502
|
+
# the second hash, all the remove operations.
|
379
503
|
def relation_change_operations
|
380
504
|
return [{},{}] unless relation_changes?
|
381
505
|
|
@@ -397,7 +521,8 @@ module Parse
|
|
397
521
|
[additions, removals]
|
398
522
|
end
|
399
523
|
|
400
|
-
#
|
524
|
+
# Saves and updates all the relational changes for made to this object.
|
525
|
+
# @return [Boolean] whether all the save or update requests were successful.
|
401
526
|
def update_relations
|
402
527
|
# relational saves require an id
|
403
528
|
return false unless @id.present?
|
@@ -425,6 +550,19 @@ module Parse
|
|
425
550
|
has_error == false
|
426
551
|
end
|
427
552
|
|
553
|
+
# Performs mass assignment using a hash with the ability to modify dirty tracking.
|
554
|
+
# This is an internal method used to set properties on the object while controlling
|
555
|
+
# whether they are dirty tracked. Each defined property has a method defined with the
|
556
|
+
# suffix `_set_attribute!` that can will be called if it is contained in the hash.
|
557
|
+
# @example
|
558
|
+
# object.set_attributes!( {"myField" => value}, false)
|
559
|
+
#
|
560
|
+
# # equivalent to calling the specific method.
|
561
|
+
# object.myField_set_attribute!(value, false)
|
562
|
+
# @param hash [Hash] the hash containing all the attribute names and values.
|
563
|
+
# @param dirty_track [Boolean] whether the assignment should be tracked in the change tracking
|
564
|
+
# system.
|
565
|
+
# @return [Hash]
|
428
566
|
def set_attributes!(hash, dirty_track = false)
|
429
567
|
return unless hash.is_a?(Hash)
|
430
568
|
hash.each do |k,v|
|
@@ -434,7 +572,7 @@ module Parse
|
|
434
572
|
end
|
435
573
|
end
|
436
574
|
|
437
|
-
#
|
575
|
+
# Clears changes information on all collections (array and relations) and all
|
438
576
|
# local attributes.
|
439
577
|
def changes_applied!
|
440
578
|
# find all fields that are of type :array
|
@@ -456,91 +594,4 @@ module Parse
|
|
456
594
|
|
457
595
|
end
|
458
596
|
|
459
|
-
module Fetching
|
460
|
-
|
461
|
-
# force fetches the current object with the data contained in Parse.
|
462
|
-
def fetch!(opts = {})
|
463
|
-
response = client.fetch_object(parse_class, id, opts)
|
464
|
-
if response.error?
|
465
|
-
puts "[Fetch Error] #{response.code}: #{response.error}"
|
466
|
-
end
|
467
|
-
# take the result hash and apply it to the attributes.
|
468
|
-
apply_attributes!(response.result, dirty_track: false)
|
469
|
-
clear_changes!
|
470
|
-
self
|
471
|
-
end
|
472
|
-
|
473
|
-
# fetches the object if needed
|
474
|
-
def fetch
|
475
|
-
# if it is a pointer, then let's go fetch the rest of the content
|
476
|
-
pointer? ? fetch! : self
|
477
|
-
end
|
478
|
-
|
479
|
-
# autofetches the object based on a key. If the key is not a Parse standard
|
480
|
-
# key, the current object is a pointer, then fetch the object - but only if
|
481
|
-
# the current object is currently autofetching.
|
482
|
-
def autofetch!(key)
|
483
|
-
key = key.to_sym
|
484
|
-
@fetch_lock ||= false
|
485
|
-
if @fetch_lock != true && pointer? && key != :acl && Parse::Properties::BASE_KEYS.include?(key) == false && respond_to?(:fetch)
|
486
|
-
#puts "AutoFetching Triggerd by: #{self.class}.#{key} (#{id})"
|
487
|
-
@fetch_lock = true
|
488
|
-
send :fetch
|
489
|
-
@fetch_lock = false
|
490
|
-
end
|
491
|
-
|
492
|
-
end
|
493
|
-
|
494
|
-
end
|
495
|
-
|
496
|
-
end
|
497
|
-
|
498
|
-
class Array
|
499
|
-
|
500
|
-
# Support for threaded operations on array items
|
501
|
-
def threaded_each(threads = 2)
|
502
|
-
Parallel.each(self, {in_threads: threads}, &Proc.new)
|
503
|
-
end
|
504
|
-
|
505
|
-
def threaded_map(threads = 2)
|
506
|
-
Parallel.map(self, {in_threads: threads}, &Proc.new)
|
507
|
-
end
|
508
|
-
|
509
|
-
def self.threaded_select(threads = 2)
|
510
|
-
Parallel.select(self, {in_threads: threads}, &Proc.new)
|
511
|
-
end
|
512
|
-
|
513
|
-
# fetches all the objects in the array (force)
|
514
|
-
# a parameter symbol can be passed indicating the lookup methodology. Default
|
515
|
-
# is parallel which fetches all objects in parallel HTTP requests.
|
516
|
-
# If nil is passed in, then all the fetching happens sequentially.
|
517
|
-
def fetch_objects!(lookup = :parallel)
|
518
|
-
# this gets all valid parse objects from the array
|
519
|
-
items = valid_parse_objects
|
520
|
-
|
521
|
-
# make parallel requests.
|
522
|
-
unless lookup == :parallel
|
523
|
-
# force fetch all objects
|
524
|
-
items.threaded_each { |o| o.fetch! }
|
525
|
-
else
|
526
|
-
# serially fetch each object
|
527
|
-
items.each { |o| o.fetch! }
|
528
|
-
end
|
529
|
-
self #return for chaining.
|
530
|
-
end
|
531
|
-
|
532
|
-
# fetches all pointer objects in the array. You can pass a symbol argument
|
533
|
-
# that provides the lookup methodology, default is :parallel. Objects that have
|
534
|
-
# already been fetched (not in a pointer state) are skipped.
|
535
|
-
def fetch_objects(lookup = :parallel)
|
536
|
-
items = valid_parse_objects
|
537
|
-
if lookup == :parallel
|
538
|
-
items.threaded_each { |o| o.fetch }
|
539
|
-
else
|
540
|
-
items.each { |e| e.fetch }
|
541
|
-
end
|
542
|
-
#self.replace items
|
543
|
-
self
|
544
|
-
end
|
545
|
-
|
546
597
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
require 'time'
|
3
|
+
require 'parallel'
|
4
|
+
|
5
|
+
module Parse
|
6
|
+
# Defines the record fetching interface for instances of Parse::Object.
|
7
|
+
module Fetching
|
8
|
+
|
9
|
+
# Force fetches and updates the current object with the data contained in the Parse collection.
|
10
|
+
# The changes applied to the object are not dirty tracked.
|
11
|
+
# @param opts [Hash] a set of options to pass to the client request.
|
12
|
+
# @return [self] the current object, useful for chaining.
|
13
|
+
def fetch!(opts = {})
|
14
|
+
response = client.fetch_object(parse_class, id, opts)
|
15
|
+
if response.error?
|
16
|
+
puts "[Fetch Error] #{response.code}: #{response.error}"
|
17
|
+
end
|
18
|
+
# take the result hash and apply it to the attributes.
|
19
|
+
apply_attributes!(response.result, dirty_track: false)
|
20
|
+
clear_changes!
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Fetches the object from the Parse data store if the object is in a Pointer
|
25
|
+
# state. This is similar to the `fetchIfNeeded` action in the standard Parse client SDK.
|
26
|
+
# @return [self] the current object.
|
27
|
+
def fetch
|
28
|
+
# if it is a pointer, then let's go fetch the rest of the content
|
29
|
+
pointer? ? fetch! : self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Autofetches the object based on a key that is not part {Parse::Properties::BASE_KEYS}.
|
33
|
+
# If the key is not a Parse standard key, and the current object is in a
|
34
|
+
# Pointer state, then fetch the data related to this record from the Parse
|
35
|
+
# data store.
|
36
|
+
# @param key [String] the name of the attribute being accessed.
|
37
|
+
# @return [Boolean]
|
38
|
+
def autofetch!(key)
|
39
|
+
key = key.to_sym
|
40
|
+
@fetch_lock ||= false
|
41
|
+
if @fetch_lock != true && pointer? && key != :acl && Parse::Properties::BASE_KEYS.include?(key) == false && respond_to?(:fetch)
|
42
|
+
#puts "AutoFetching Triggerd by: #{self.class}.#{key} (#{id})"
|
43
|
+
@fetch_lock = true
|
44
|
+
send :fetch
|
45
|
+
@fetch_lock = false
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
class Array
|
57
|
+
|
58
|
+
# Perform a threaded each iteration on a set of array items.
|
59
|
+
# @param threads [Integer] the maximum number of threads to spawn/
|
60
|
+
# @yield the block for the each iteration.
|
61
|
+
# @return [self]
|
62
|
+
# @see Array#each
|
63
|
+
# @see https://github.com/grosser/parallel Parallel
|
64
|
+
def threaded_each(threads = 2, &block)
|
65
|
+
Parallel.each(self, {in_threads: threads}, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Perform a threaded map operation on a set of array items.
|
69
|
+
# @param threads [Integer] the maximum number of threads to spawn
|
70
|
+
# @yield the block for the map iteration.
|
71
|
+
# @return [Array] the resultant array from the map.
|
72
|
+
# @see Array#map
|
73
|
+
# @see https://github.com/grosser/parallel Parallel
|
74
|
+
def threaded_map(threads = 2, &block)
|
75
|
+
Parallel.map(self, {in_threads: threads}, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Fetches all the objects in the array even if they are not in a Pointer state.
|
79
|
+
# @param lookup [Symbol] The methodology to use for HTTP requests. Use :parallel
|
80
|
+
# to fetch all objects in parallel HTTP requests. Set to anything else to
|
81
|
+
# perform requests serially.
|
82
|
+
# @return [Array<Parse::Object>] an array of fetched Parse::Objects.
|
83
|
+
# @see Array#fetch_objects
|
84
|
+
def fetch_objects!(lookup = :parallel)
|
85
|
+
# this gets all valid parse objects from the array
|
86
|
+
items = valid_parse_objects
|
87
|
+
lookup == :parallel ? items.threaded_each(2,&:fetch!) : items.each(&:fetch!)
|
88
|
+
#self.replace items
|
89
|
+
self #return for chaining.
|
90
|
+
end
|
91
|
+
|
92
|
+
# Fetches all the objects in the array that are in Pointer state.
|
93
|
+
# @param lookup [Symbol] The methodology to use for HTTP requests. Use :parallel
|
94
|
+
# to fetch all objects in parallel HTTP requests. Set to anything else to
|
95
|
+
# perform requests serially.
|
96
|
+
# @return [Array<Parse::Object>] an array of fetched Parse::Objects.
|
97
|
+
# @see Array#fetch_objects!
|
98
|
+
def fetch_objects(lookup = :parallel)
|
99
|
+
items = valid_parse_objects
|
100
|
+
lookup == :parallel ? items.threaded_each(2,&:fetch) : items.each(&:fetch)
|
101
|
+
#self.replace items
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -14,18 +14,10 @@ require 'active_support/hash_with_indifferent_access'
|
|
14
14
|
require 'time'
|
15
15
|
|
16
16
|
|
17
|
-
# This module provides support for handling all the different types of column data types
|
18
|
-
# supported in Parse and mapping them between their remote names with their local ruby named attributes.
|
19
|
-
# By default, the convention used for naming parameters is that the remote column should be in lower-first-camelcase, (ex. myField, eventAddress), except for
|
20
|
-
# a few special columns like "id" and "acl".
|
21
|
-
# Properties are defined when creating subclasses of Parse::Object and using the `property` class method.
|
22
|
-
#
|
23
|
-
# By defining properties, dynamic methods are created in order to allow getters and setters to be used. We will go into detail below.
|
24
|
-
#
|
25
|
-
# Each class will have a different copy of attribute mapping and field mappings.
|
26
|
-
|
27
17
|
module Parse
|
28
18
|
|
19
|
+
# This module provides support for handling all the different types of column data types
|
20
|
+
# supported in Parse and mapping them between their remote names with their local ruby named attributes.
|
29
21
|
module Properties
|
30
22
|
# This is an exception that is thrown if there is an issue when creating a specific property for a class.
|
31
23
|
class DefinitionError < StandardError; end;
|
@@ -49,7 +41,10 @@ module Parse
|
|
49
41
|
module ClassMethods
|
50
42
|
|
51
43
|
# The fields method returns a mapping of all local attribute names and their data type.
|
52
|
-
# if type is passed, we return only the fields that matched that data type
|
44
|
+
# if type is passed, we return only the fields that matched that data type. If `type`
|
45
|
+
# is provided, it will only return the fields that match the data type.
|
46
|
+
# @param type [Symbol] a property type.
|
47
|
+
# @return [Hash] the defined fields for this Parse collection with their data type.
|
53
48
|
def fields(type = nil)
|
54
49
|
@fields ||= {id: :string, created_at: :date, updated_at: :date, acl: :acl}
|
55
50
|
if type.present?
|
@@ -59,11 +54,12 @@ module Parse
|
|
59
54
|
@fields
|
60
55
|
end
|
61
56
|
|
62
|
-
#
|
57
|
+
# @return [Hash] the field map for this subclass.
|
63
58
|
def field_map
|
64
59
|
@field_map ||= BASE_FIELD_MAP.dup
|
65
60
|
end
|
66
61
|
|
62
|
+
# @return [Hash] the fields that are marked as enums.
|
67
63
|
def enums
|
68
64
|
@enums ||= {}
|
69
65
|
end
|
@@ -73,6 +69,7 @@ module Parse
|
|
73
69
|
@attributes = BASE.merge(hash)
|
74
70
|
end
|
75
71
|
|
72
|
+
# @return [Hash] the fields that are marked as enums.
|
76
73
|
def attributes
|
77
74
|
@attributes ||= BASE.dup
|
78
75
|
end
|
@@ -303,7 +300,7 @@ module Parse
|
|
303
300
|
# support question mark methods for boolean
|
304
301
|
if data_type == :boolean
|
305
302
|
if self.method_defined?("#{key}?")
|
306
|
-
puts "Creating boolean helper :#{key}?. Will overwrite existing method #{self}##{key}
|
303
|
+
puts "Creating boolean helper :#{key}?. Will overwrite existing method #{self}##{key}_increment!."
|
307
304
|
end
|
308
305
|
|
309
306
|
# returns true if set to true, false otherwise
|
@@ -311,6 +308,36 @@ module Parse
|
|
311
308
|
unless opts[:scopes] == false
|
312
309
|
scope key, ->(opts = {}){ query( opts.merge(key => true) ) }
|
313
310
|
end
|
311
|
+
elsif data_type == :integer || data_type == :float
|
312
|
+
if self.method_defined?("#{key}_increment!")
|
313
|
+
puts "Creating increment helper :#{key}_increment!. Will overwrite existing method #{self}##{key}_increment!."
|
314
|
+
end
|
315
|
+
|
316
|
+
define_method("#{key}_increment!") do |amount = 1|
|
317
|
+
unless amount.is_a?(Numeric)
|
318
|
+
raise ArgumentError, "Amount needs to be an integer"
|
319
|
+
end
|
320
|
+
result = self.op_increment!(key, amount)
|
321
|
+
if result
|
322
|
+
new_value = send(key).to_i + amount
|
323
|
+
# set the updated value, with no dirty tracking
|
324
|
+
self.send set_attribute_method, new_value, false
|
325
|
+
end
|
326
|
+
result
|
327
|
+
end
|
328
|
+
|
329
|
+
if self.method_defined?("#{key}_decrement!")
|
330
|
+
puts "Creating decrement helper :#{key}_decrement!. Will overwrite existing method #{self}##{key}_decrement!."
|
331
|
+
end
|
332
|
+
|
333
|
+
define_method("#{key}_decrement!") do |amount = -1|
|
334
|
+
unless amount.is_a?(Numeric)
|
335
|
+
raise ArgumentError, "Amount needs to be an integer"
|
336
|
+
end
|
337
|
+
amount = -amount if amount > 0
|
338
|
+
send("#{key}_increment!", amount)
|
339
|
+
end
|
340
|
+
|
314
341
|
end
|
315
342
|
|
316
343
|
# The second method to be defined is a setter method. This is done by
|