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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +5 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +40 -80
  5. data/lib/parse/api/all.rb +7 -0
  6. data/lib/parse/api/analytics.rb +8 -3
  7. data/lib/parse/api/apps.rb +29 -1
  8. data/lib/parse/api/batch.rb +14 -129
  9. data/lib/parse/api/cloud_functions.rb +9 -0
  10. data/lib/parse/api/config.rb +10 -1
  11. data/lib/parse/api/files.rb +7 -2
  12. data/lib/parse/api/hooks.rb +45 -2
  13. data/lib/parse/api/objects.rb +43 -6
  14. data/lib/parse/api/push.rb +6 -1
  15. data/lib/parse/api/schemas.rb +15 -1
  16. data/lib/parse/api/sessions.rb +5 -0
  17. data/lib/parse/api/users.rb +64 -5
  18. data/lib/parse/client/authentication.rb +25 -8
  19. data/lib/parse/client/batch.rb +206 -0
  20. data/lib/parse/client/body_builder.rb +12 -6
  21. data/lib/parse/client/caching.rb +42 -10
  22. data/lib/parse/client/protocol.rb +51 -46
  23. data/lib/parse/client/response.rb +1 -47
  24. data/lib/parse/client.rb +171 -42
  25. data/lib/parse/model/acl.rb +184 -39
  26. data/lib/parse/model/associations/belongs_to.rb +1 -0
  27. data/lib/parse/model/classes/role.rb +7 -1
  28. data/lib/parse/model/classes/session.rb +7 -3
  29. data/lib/parse/model/classes/user.rb +107 -0
  30. data/lib/parse/model/core/actions.rb +166 -115
  31. data/lib/parse/model/core/fetching.rb +105 -0
  32. data/lib/parse/model/core/properties.rb +40 -13
  33. data/lib/parse/model/core/querying.rb +123 -39
  34. data/lib/parse/model/core/schema.rb +22 -32
  35. data/lib/parse/model/object.rb +26 -20
  36. data/lib/parse/model/pointer.rb +1 -0
  37. data/lib/parse/query/constraint.rb +65 -27
  38. data/lib/parse/query/constraints.rb +0 -3
  39. data/lib/parse/query/operation.rb +33 -22
  40. data/lib/parse/query/ordering.rb +10 -5
  41. data/lib/parse/stack/generators/rails.rb +5 -1
  42. data/lib/parse/stack/generators/templates/model_installation.rb +1 -1
  43. data/lib/parse/stack/generators/templates/model_role.rb +1 -1
  44. data/lib/parse/stack/generators/templates/model_session.rb +2 -2
  45. data/lib/parse/stack/generators/templates/model_user.rb +1 -1
  46. data/lib/parse/stack/generators/templates/parse.rb +0 -1
  47. data/lib/parse/stack/railtie.rb +1 -0
  48. data/lib/parse/stack/tasks.rb +3 -1
  49. data/lib/parse/stack/version.rb +3 -1
  50. data/lib/parse/webhooks/registration.rb +3 -3
  51. data/lib/parse/webhooks.rb +88 -7
  52. 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
- # provide the column name of the field, polarity (true = add, false = remove) and the
23
- # list of objects.
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 = [objects].flatten.compact
35
+ @objects = Array.wrap(objects).compact
28
36
  end
29
37
 
30
- # generate the proper Parse hash-format operation
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
- # force only one result
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
- # not quite sure if I like the name of this API.
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
- # This creates a destroy_request for the current object.
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
- # Creates an array of all possible PUT operations that need to be performed
213
- # on this local object. The reason it is a list is because attribute operations,
214
- # relational add operations and relational remove operations are treated as separate
215
- # Parse requests.
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
- # save the updates on the objects, if any
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
- # create this object in Parse
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
- # shortcut for raising an exception of saving this object failed.
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
- # only destroy the object if it has an id. You can setup before and after
341
- #callback hooks on :destroy
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
- # this method is useful to generate an array of additions and removals to a relational
378
- # column.
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
- # update relations updates all the relational data that needs to be updated.
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
- # clears changes information on all collections (array and relations) and all
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
- # This returns the mapping of local to remote attribute names.
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