parse-stack-next 4.5.0

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.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.env.sample +112 -0
  4. data/.env.test +10 -0
  5. data/.github/workflows/ruby.yml +36 -0
  6. data/.gitignore +49 -0
  7. data/.ruby-version +1 -0
  8. data/.solargraph.yml +22 -0
  9. data/CHANGELOG.md +5816 -0
  10. data/Gemfile +30 -0
  11. data/Gemfile.lock +175 -0
  12. data/LICENSE.txt +23 -0
  13. data/Makefile +63 -0
  14. data/README.md +5655 -0
  15. data/Rakefile +573 -0
  16. data/bin/console +38 -0
  17. data/bin/parse-console +136 -0
  18. data/bin/server +17 -0
  19. data/bin/setup +7 -0
  20. data/config/parse-config.json +12 -0
  21. data/docs/TEST_SERVER.md +271 -0
  22. data/docs/_config.yml +1 -0
  23. data/docs/mcp_guide.md +3484 -0
  24. data/docs/mongodb_direct_guide.md +1348 -0
  25. data/docs/mongodb_index_optimization_guide.md +631 -0
  26. data/examples/transaction_example.rb +219 -0
  27. data/lib/parse/acl_scope.rb +728 -0
  28. data/lib/parse/agent/cancellation_token.rb +80 -0
  29. data/lib/parse/agent/constraint_translator.rb +480 -0
  30. data/lib/parse/agent/describe.rb +420 -0
  31. data/lib/parse/agent/errors.rb +133 -0
  32. data/lib/parse/agent/mcp_client.rb +557 -0
  33. data/lib/parse/agent/mcp_dispatcher.rb +1023 -0
  34. data/lib/parse/agent/mcp_rack_app.rb +1143 -0
  35. data/lib/parse/agent/mcp_server.rb +376 -0
  36. data/lib/parse/agent/metadata_audit.rb +259 -0
  37. data/lib/parse/agent/metadata_dsl.rb +733 -0
  38. data/lib/parse/agent/metadata_registry.rb +794 -0
  39. data/lib/parse/agent/pipeline_validator.rb +82 -0
  40. data/lib/parse/agent/prompts.rb +351 -0
  41. data/lib/parse/agent/rate_limiter.rb +158 -0
  42. data/lib/parse/agent/relation_graph.rb +162 -0
  43. data/lib/parse/agent/result_formatter.rb +453 -0
  44. data/lib/parse/agent/tools.rb +5489 -0
  45. data/lib/parse/agent.rb +3249 -0
  46. data/lib/parse/api/aggregate.rb +79 -0
  47. data/lib/parse/api/all.rb +26 -0
  48. data/lib/parse/api/analytics.rb +18 -0
  49. data/lib/parse/api/batch.rb +33 -0
  50. data/lib/parse/api/cloud_functions.rb +58 -0
  51. data/lib/parse/api/config.rb +125 -0
  52. data/lib/parse/api/files.rb +29 -0
  53. data/lib/parse/api/hooks.rb +117 -0
  54. data/lib/parse/api/objects.rb +146 -0
  55. data/lib/parse/api/path_segment.rb +75 -0
  56. data/lib/parse/api/push.rb +20 -0
  57. data/lib/parse/api/schema.rb +49 -0
  58. data/lib/parse/api/server.rb +50 -0
  59. data/lib/parse/api/sessions.rb +24 -0
  60. data/lib/parse/api/users.rb +250 -0
  61. data/lib/parse/atlas_search/index_manager.rb +353 -0
  62. data/lib/parse/atlas_search/result.rb +204 -0
  63. data/lib/parse/atlas_search/search_builder.rb +604 -0
  64. data/lib/parse/atlas_search/session.rb +253 -0
  65. data/lib/parse/atlas_search.rb +995 -0
  66. data/lib/parse/client/authentication.rb +97 -0
  67. data/lib/parse/client/batch.rb +234 -0
  68. data/lib/parse/client/body_builder.rb +240 -0
  69. data/lib/parse/client/caching.rb +203 -0
  70. data/lib/parse/client/logging.rb +293 -0
  71. data/lib/parse/client/profiling.rb +181 -0
  72. data/lib/parse/client/protocol.rb +91 -0
  73. data/lib/parse/client/request.rb +233 -0
  74. data/lib/parse/client/response.rb +208 -0
  75. data/lib/parse/client.rb +1104 -0
  76. data/lib/parse/clp_scope.rb +361 -0
  77. data/lib/parse/live_query/circuit_breaker.rb +256 -0
  78. data/lib/parse/live_query/client.rb +1001 -0
  79. data/lib/parse/live_query/configuration.rb +224 -0
  80. data/lib/parse/live_query/event.rb +115 -0
  81. data/lib/parse/live_query/event_queue.rb +272 -0
  82. data/lib/parse/live_query/health_monitor.rb +214 -0
  83. data/lib/parse/live_query/logging.rb +149 -0
  84. data/lib/parse/live_query/subscription.rb +294 -0
  85. data/lib/parse/live_query.rb +163 -0
  86. data/lib/parse/lookup_rewriter.rb +445 -0
  87. data/lib/parse/model/acl.rb +968 -0
  88. data/lib/parse/model/associations/belongs_to.rb +275 -0
  89. data/lib/parse/model/associations/collection_proxy.rb +435 -0
  90. data/lib/parse/model/associations/has_many.rb +597 -0
  91. data/lib/parse/model/associations/has_one.rb +158 -0
  92. data/lib/parse/model/associations/pointer_collection_proxy.rb +134 -0
  93. data/lib/parse/model/associations/relation_collection_proxy.rb +177 -0
  94. data/lib/parse/model/bytes.rb +62 -0
  95. data/lib/parse/model/classes/audience.rb +262 -0
  96. data/lib/parse/model/classes/installation.rb +363 -0
  97. data/lib/parse/model/classes/job_schedule.rb +153 -0
  98. data/lib/parse/model/classes/job_status.rb +264 -0
  99. data/lib/parse/model/classes/product.rb +75 -0
  100. data/lib/parse/model/classes/push_status.rb +263 -0
  101. data/lib/parse/model/classes/role.rb +751 -0
  102. data/lib/parse/model/classes/session.rb +201 -0
  103. data/lib/parse/model/classes/user.rb +943 -0
  104. data/lib/parse/model/clp.rb +544 -0
  105. data/lib/parse/model/core/actions.rb +1268 -0
  106. data/lib/parse/model/core/builder.rb +139 -0
  107. data/lib/parse/model/core/create_lock.rb +386 -0
  108. data/lib/parse/model/core/describe.rb +382 -0
  109. data/lib/parse/model/core/enhanced_change_tracking.rb +159 -0
  110. data/lib/parse/model/core/errors.rb +38 -0
  111. data/lib/parse/model/core/fetching.rb +566 -0
  112. data/lib/parse/model/core/field_guards.rb +220 -0
  113. data/lib/parse/model/core/indexing.rb +382 -0
  114. data/lib/parse/model/core/parse_reference.rb +407 -0
  115. data/lib/parse/model/core/properties.rb +809 -0
  116. data/lib/parse/model/core/querying.rb +491 -0
  117. data/lib/parse/model/core/schema.rb +202 -0
  118. data/lib/parse/model/core/search_indexing.rb +174 -0
  119. data/lib/parse/model/date.rb +88 -0
  120. data/lib/parse/model/email.rb +213 -0
  121. data/lib/parse/model/file.rb +527 -0
  122. data/lib/parse/model/geojson.rb +271 -0
  123. data/lib/parse/model/geopoint.rb +261 -0
  124. data/lib/parse/model/model.rb +260 -0
  125. data/lib/parse/model/object.rb +2068 -0
  126. data/lib/parse/model/phone.rb +520 -0
  127. data/lib/parse/model/pointer.rb +443 -0
  128. data/lib/parse/model/polygon.rb +406 -0
  129. data/lib/parse/model/push.rb +975 -0
  130. data/lib/parse/model/shortnames.rb +8 -0
  131. data/lib/parse/model/time_zone.rb +141 -0
  132. data/lib/parse/model/validations/uniqueness_validator.rb +97 -0
  133. data/lib/parse/model/validations.rb +96 -0
  134. data/lib/parse/mongodb.rb +2300 -0
  135. data/lib/parse/pipeline_security.rb +554 -0
  136. data/lib/parse/query/constraint.rb +198 -0
  137. data/lib/parse/query/constraints.rb +3279 -0
  138. data/lib/parse/query/cursor.rb +434 -0
  139. data/lib/parse/query/n_plus_one_detector.rb +445 -0
  140. data/lib/parse/query/operation.rb +104 -0
  141. data/lib/parse/query/ordering.rb +66 -0
  142. data/lib/parse/query.rb +7028 -0
  143. data/lib/parse/schema/index_migrator.rb +291 -0
  144. data/lib/parse/schema/search_index_migrator.rb +289 -0
  145. data/lib/parse/schema.rb +494 -0
  146. data/lib/parse/stack/generators/rails.rb +40 -0
  147. data/lib/parse/stack/generators/templates/model.erb +51 -0
  148. data/lib/parse/stack/generators/templates/model_installation.rb +4 -0
  149. data/lib/parse/stack/generators/templates/model_role.rb +4 -0
  150. data/lib/parse/stack/generators/templates/model_session.rb +4 -0
  151. data/lib/parse/stack/generators/templates/model_user.rb +11 -0
  152. data/lib/parse/stack/generators/templates/parse.rb +12 -0
  153. data/lib/parse/stack/generators/templates/webhooks.rb +10 -0
  154. data/lib/parse/stack/railtie.rb +18 -0
  155. data/lib/parse/stack/tasks.rb +563 -0
  156. data/lib/parse/stack/version.rb +11 -0
  157. data/lib/parse/stack.rb +455 -0
  158. data/lib/parse/two_factor_auth/user_extension.rb +449 -0
  159. data/lib/parse/two_factor_auth.rb +310 -0
  160. data/lib/parse/webhooks/payload.rb +360 -0
  161. data/lib/parse/webhooks/registration.rb +199 -0
  162. data/lib/parse/webhooks/replay_protection.rb +189 -0
  163. data/lib/parse/webhooks.rb +510 -0
  164. data/lib/parse-stack-next.rb +5 -0
  165. data/lib/parse-stack.rb +5 -0
  166. data/parse-stack-next.gemspec +82 -0
  167. data/parse-stack.png +0 -0
  168. data/scripts/debug-ips.js +35 -0
  169. data/scripts/docker/Dockerfile.parse +13 -0
  170. data/scripts/docker/atlas-init.js +284 -0
  171. data/scripts/docker/docker-compose.atlas.yml +76 -0
  172. data/scripts/docker/docker-compose.test.yml +106 -0
  173. data/scripts/docker/mongo-init.js +21 -0
  174. data/scripts/eval_mcp_with_lm_studio.rb +274 -0
  175. data/scripts/start-parse.sh +90 -0
  176. data/scripts/start_mcp_server.rb +78 -0
  177. data/scripts/test_server_connection.rb +82 -0
  178. metadata +377 -0
@@ -0,0 +1,435 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require "active_model"
5
+ require "active_support"
6
+ require "active_support/inflector"
7
+ require "active_support/core_ext/object"
8
+ require_relative "../pointer"
9
+
10
+ module Parse
11
+ # We use a delegate pattern to send notifications to the parent whenever the content of the internal array changes.
12
+ # The main requirement to using the proxy is to provide the list of initial items if any,
13
+ # the owner to be notified and the name of the attribute 'key'. With that, anytime the array
14
+ # will change, we will notify the delegate by sending :'key'_will_change! . The proxy can also
15
+ # be lazy when fetching the contents of the collection. Whenever the collection is accessed and
16
+ # the list is in a "not loaded" state (empty and loaded == false), we will send :'key_fetch!' to the delegate in order to
17
+ # populate the collection.
18
+
19
+ # A CollectionProxy is a special type of array wrapper that notifies a delegate
20
+ # object about changes to the array in order to perform dirty tracking. This is
21
+ # used for all Array properties in Parse::Objects. Subclasses of {CollectionProxy} are
22
+ # also available for supporting different association types such as an array of Parse pointers
23
+ # and Parse relations.
24
+ # @see PointerCollectionProxy
25
+ # @see RelationCollectionProxy
26
+ class CollectionProxy
27
+ include ::ActiveModel::Model
28
+ include ::ActiveModel::Dirty
29
+ include ::Enumerable
30
+
31
+ # @!attribute [r] delegate
32
+ # The object to be notified of changes to the collection.
33
+ # @return [Object]
34
+
35
+ # @!attribute [rw] loaded
36
+ # @return [Boolean] true/false whether the collection has been loaded.
37
+
38
+ # @!attribute [r] parse_class
39
+ # For some subclasses, this helps typecast the items in the collection.
40
+ # @return [String]
41
+
42
+ # @!attribute [r] key
43
+ # the name of the property key to use when sending notifications for _will_change! and _fetch!
44
+ # @return [String]
45
+
46
+ # The writer for :collection is defined explicitly below with dirty-tracking
47
+ # notification; do not declare attr_writer :collection here.
48
+ attr_accessor :loaded, :parse_class
49
+ attr_reader :delegate, :key
50
+
51
+ # This is to use dirty tracking within the proxy
52
+ define_attribute_methods :collection
53
+
54
+ # Create a new CollectionProxy instance.
55
+ # @param collection [Array] the initial items to add to the collection.
56
+ # @param delegate [Object] the owner of the object that will receive the notifications.
57
+ # @param key [Symbol] the name of the key to use when sending notifications for _will_change! and _fetch!
58
+ # @param parse_class [String] (Optional) the Parse class type are the items of the collection.
59
+ # This is used to typecast the objects in the array to a particular Parse Object type.
60
+ # @see PointerCollectionProxy
61
+ # @see RelationCollectionProxy
62
+ def initialize(collection = nil, delegate: nil, key: nil, parse_class: nil)
63
+ @delegate = delegate
64
+ @key = key.to_sym if key.present?
65
+ @collection = collection.is_a?(Array) ? collection : []
66
+ @loaded = @collection.count > 0
67
+ @parse_class = parse_class
68
+ end
69
+
70
+ # true if the collection has been loaded
71
+ def loaded?
72
+ @loaded
73
+ end
74
+
75
+ # Forward a method call to the delegate.
76
+ # @param method [Symbol] the name of the method to forward
77
+ # @param params [Object] method parameters
78
+ # @return [Object] the return value from the forwarded method.
79
+ def forward(method, params = nil)
80
+ return unless @delegate && @delegate.respond_to?(method)
81
+ params.nil? ? @delegate.send(method) : @delegate.send(method, params)
82
+ end
83
+
84
+ # Reset the state of the collection.
85
+ def reset!
86
+ @loaded = false
87
+ clear
88
+ end
89
+
90
+ # @return [Boolean] true if two collection proxies have similar items.
91
+ def ==(other_list)
92
+ if other_list.is_a?(Array)
93
+ return @collection == other_list
94
+ elsif other_list.is_a?(Parse::CollectionProxy)
95
+ return @collection == other_list.instance_variable_get(:@collection)
96
+ end
97
+ end
98
+
99
+ # Reload and restore the collection to its original set of items.
100
+ def reload!
101
+ reset!
102
+ collection #force reload
103
+ end
104
+
105
+ # clear all items in the collection
106
+ def clear
107
+ @collection.clear
108
+ end
109
+
110
+ # @return [Array]
111
+ def to_a
112
+ collection.to_a
113
+ end
114
+
115
+ alias_method :to_ary, :to_a
116
+
117
+ # Set the internal collection of items *without* dirty tracking or
118
+ # change notifications.
119
+ # @return [Array] the collection
120
+ def set_collection!(list)
121
+ @collection = list
122
+ end
123
+
124
+ # @!attribute [rw] collection
125
+ # The internal backing store of the collection containing the content. This value is lazily
126
+ # loaded for some subclasses.
127
+ # @note If you modify this directly, it is highly recommended that you
128
+ # call {CollectionProxy#notify_will_change!} to notify the dirty tracking system.
129
+ # @return [Array] contents of the collection.
130
+ def collection
131
+ if @collection.empty? && @loaded == false
132
+ @collection = forward(:"#{@key}_fetch!") || @collection || []
133
+ @loaded = true
134
+ end
135
+
136
+ @collection
137
+ end
138
+
139
+ def collection=(c)
140
+ notify_will_change!
141
+ @collection = c
142
+ end
143
+
144
+ # Add items to the collection
145
+ # @param items [Array] items to add
146
+ def add(*items)
147
+ notify_will_change! if items.count > 0
148
+ items.each do |item|
149
+ collection.push item
150
+ end
151
+ @collection
152
+ end
153
+
154
+ alias_method :push, :add
155
+
156
+ # Add items to the collection if they don't already exist
157
+ # @param items [Array] items to uniquely add
158
+ # @return [Array] the collection.
159
+ def add_unique(*items)
160
+ return unless items.count > 0
161
+ notify_will_change!
162
+ @collection = collection | items.flatten
163
+ @collection
164
+ end
165
+
166
+ alias_method :push_unique, :add_unique
167
+
168
+ # Set Union - Returns a new array by joining two arrays, excluding
169
+ # any duplicates and preserving the order from the original array.
170
+ # It compares elements using their hash and eql? methods for efficiency.
171
+ # See {https://docs.ruby-lang.org/en/2.0.0/Array.html#method-i-hash Array#|}
172
+ # @example
173
+ # [ "a", "b", "c" ] | [ "c", "d", "a" ] #=> [ "a", "b", "c", "d" ]
174
+ # @param items [Array] items to uniquely add
175
+ # @see #add_unique
176
+ # @return [Array] array with unique items
177
+ def |(items)
178
+ collection | [items].flatten
179
+ end
180
+
181
+ # Set Intersection - Returns a new array containing unique elements common
182
+ # to the two arrays. The order is preserved from the original array.
183
+ #
184
+ # It compares elements using their hash and eql? methods for efficiency.
185
+ # See {https://ruby-doc.org/core-2.4.1/Array.html#method-i-26 Array#&}
186
+ # @example
187
+ # [ 1, 1, 3, 5 ] & [ 3, 2, 1 ] #=> [ 1, 3 ]
188
+ # [ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
189
+ # @param other_ary [Array]
190
+ # @return [Array] intersection array
191
+ def &(other_ary)
192
+ collection & [other_ary].flatten
193
+ end
194
+
195
+ # Alias {https://ruby-doc.org/core-2.4.1/Array.html#method-i-2D Array Difference}.
196
+ # Returns a new array that is a copy of the original array, removing any
197
+ # items that also appear in other_ary. The order is preserved from the
198
+ # original array.
199
+ # @example
200
+ # [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
201
+ # @param other_ary [Array]
202
+ # @return [Array] delta array
203
+ def -(other_ary)
204
+ collection - [other_ary].flatten
205
+ end
206
+
207
+ # Alias {https://ruby-doc.org/core-2.4.1/Array.html#method-i-2B Array Concatenation}.
208
+ # Returns a new array built by concatenating the two arrays together to
209
+ # produce a third array.
210
+ # @example
211
+ # [ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ]
212
+ # @param other_ary [Array]
213
+ # @return [Array] concatenated array
214
+ def +(other_ary)
215
+ collection + [other_ary].flatten.to_a
216
+ end
217
+
218
+ # Alias {https://ruby-doc.org/core-2.4.1/Array.html#method-i-flatten Array flattening}.
219
+ # @return [Array] a flattened one-dimensional array
220
+ def flatten
221
+ collection.flatten
222
+ end
223
+
224
+ # Remove items from the collection
225
+ # @param items [Array] items to remove
226
+ def remove(*items)
227
+ notify_will_change! if items.count > 0
228
+ items.each do |item|
229
+ collection.delete item
230
+ end
231
+ @collection
232
+ end
233
+
234
+ alias_method :delete, :remove
235
+
236
+ # Atomically adds all items from the array.
237
+ # This request is sent directly to the Parse backend.
238
+ # @param items [Array] items to uniquely add
239
+ # @note Parse objects are automatically converted to pointer format
240
+ # @see #add_unique!
241
+ def add!(*items)
242
+ return false unless @delegate.respond_to?(:op_add!)
243
+ @delegate.send :op_add!, @key, items_to_pointers(items.flatten)
244
+ reset!
245
+ end
246
+
247
+ # Atomically adds all items from the array that are not already part of the collection.
248
+ # This request is sent directly to the Parse backend.
249
+ # @param items [Array] items to uniquely add
250
+ # @note Parse objects are automatically converted to pointer format
251
+ # @see #add!
252
+ def add_unique!(*items)
253
+ return false unless @delegate.respond_to?(:op_add_unique!)
254
+ @delegate.send :op_add_unique!, @key, items_to_pointers(items.flatten)
255
+ reset!
256
+ end
257
+
258
+ # Atomically deletes all items from the array. This request is sent
259
+ # directly to the Parse backend.
260
+ # @param items [Array] items to remove
261
+ # @note Parse objects are automatically converted to pointer format
262
+ def remove!(*items)
263
+ return false unless @delegate.respond_to?(:op_remove!)
264
+ @delegate.send :op_remove!, @key, items_to_pointers(items.flatten)
265
+ reset!
266
+ end
267
+
268
+ # Atomically deletes all items in the array, and marks the field as `undefined` directly
269
+ # with the Parse server. This request is sent directly to the Parse backend.
270
+ def destroy!
271
+ return false unless @delegate.respond_to?(:op_destroy!)
272
+ @delegate.send :op_destroy!, @key
273
+ collection_will_change!
274
+ @collection.clear
275
+ reset!
276
+ end
277
+
278
+ # Locally restores previous attributes (not from the persistent store)
279
+ def rollback!
280
+ restore_attributes
281
+ end
282
+
283
+ # clears all dirty tracked information.
284
+ def clear_changes!
285
+ clear_changes_information
286
+ end
287
+
288
+ # mark that collection changes where applied, which clears dirty tracking.
289
+ def changes_applied!
290
+ changes_applied
291
+ end
292
+
293
+ # @param args [Hash] arguments to pass to Array#first.
294
+ # @return [Object] the first item in the collection
295
+ def first(*args)
296
+ collection.first(*args)
297
+ end
298
+
299
+ # @return [Object] the second item in the collection
300
+ def second
301
+ collection.second
302
+ end
303
+
304
+ # @param args [Hash] arguments to pass to Array#last.
305
+ # @return [Object] the last item in the collection
306
+ def last(*args)
307
+ collection.last(*args)
308
+ end
309
+
310
+ # @return [Integer] number of items in the collection.
311
+ def count
312
+ collection.count
313
+ end
314
+
315
+ # @return [Array] a JSON representation
316
+ # @param opts [Hash] options for serialization
317
+ # @option opts [Boolean] :pointers_only (false) When true, converts all Parse objects
318
+ # to pointer format. Use this when sending data to Parse Server (saves, webhooks).
319
+ # When false (default), full objects are serialized for API responses.
320
+ # @example Default - full objects for API responses
321
+ # team.members.as_json
322
+ # # => [{"objectId"=>"abc", "name"=>"Alice", ...}, ...]
323
+ # @example Pointers only for storage
324
+ # team.members.as_json(pointers_only: true)
325
+ # # => [{"__type"=>"Pointer", "className"=>"Member", "objectId"=>"abc"}, ...]
326
+ def as_json(opts = nil)
327
+ opts ||= {}
328
+ pointers_only = opts.delete(:pointers_only) || opts.delete("pointers_only")
329
+
330
+ collection.map do |item|
331
+ if pointers_only && item.respond_to?(:pointer)
332
+ # Convert Parse objects/pointers to pointer format for storage
333
+ ptr = item.pointer
334
+ {
335
+ Parse::Model::TYPE_FIELD => Parse::Model::TYPE_POINTER,
336
+ Parse::Model::KEY_CLASS_NAME => ptr.parse_class,
337
+ Parse::Model::OBJECT_ID => ptr.id,
338
+ }
339
+ elsif item.respond_to?(:as_json)
340
+ item.as_json(opts)
341
+ else
342
+ item
343
+ end
344
+ end
345
+ end
346
+
347
+ # true if the collection is empty.
348
+ def empty?
349
+ collection.empty?
350
+ end
351
+
352
+ # Append items to the collection
353
+ def <<(*list)
354
+ if list.count > 0
355
+ notify_will_change!
356
+ list.flatten.each { |e| collection.push(e) }
357
+ end
358
+ end
359
+
360
+ # Notifies the delegate that the collection changed.
361
+ def notify_will_change!
362
+ collection_will_change!
363
+ forward "#{@key}_will_change!"
364
+ end
365
+
366
+ # Alias for Array#each
367
+ def each(&block)
368
+ return collection.enum_for(:each) unless block_given?
369
+ collection.each(&block)
370
+ end
371
+
372
+ # Alias for Array#map
373
+ def map(&block)
374
+ return collection.enum_for(:map) unless block_given?
375
+ collection.map(&block)
376
+ end
377
+
378
+ # Alias for Array#select
379
+ def select(&block)
380
+ return collection.enum_for(:select) unless block_given?
381
+ collection.select(&block)
382
+ end
383
+
384
+ # Alias for Array#uniq
385
+ def uniq(&block)
386
+ return collection.uniq(&block) if block_given?
387
+ return collection.uniq
388
+ end
389
+
390
+ # Alias for Array#uniq!
391
+ def uniq!(&block)
392
+ notify_will_change!
393
+ return collection.uniq!(&block) if block_given?
394
+ return collection.uniq!
395
+ end
396
+
397
+ # @!visibility private
398
+ def inspect
399
+ "#<#{self.class} changed?=#{changed?} @collection=#{@collection.inspect} >"
400
+ end
401
+
402
+ # Alias to `to_a.parse_objects` from Array#parse_objects
403
+ # @return [Array<Parse::Object>] an array of Parse Object subclasses representing this collection.
404
+ def parse_objects
405
+ collection.to_a.parse_objects
406
+ end
407
+
408
+ # Alias to `to_a.parse_pointers` from Array#parse_pointers
409
+ # @return [Array<Parse::Pointer>] an array of pointers representing this collection.
410
+ def parse_pointers
411
+ collection.to_a.parse_pointers
412
+ end
413
+
414
+ private
415
+
416
+ # Convert items to pointer format for atomic operations.
417
+ # Parse objects/pointers are converted to pointer hashes, other items pass through.
418
+ # @param items [Array] items to convert
419
+ # @return [Array] items with Parse objects converted to pointer format
420
+ def items_to_pointers(items)
421
+ items.map do |item|
422
+ if item.respond_to?(:pointer)
423
+ ptr = item.pointer
424
+ {
425
+ Parse::Model::TYPE_FIELD => Parse::Model::TYPE_POINTER,
426
+ Parse::Model::KEY_CLASS_NAME => ptr.parse_class,
427
+ Parse::Model::OBJECT_ID => ptr.id,
428
+ }
429
+ else
430
+ item
431
+ end
432
+ end
433
+ end
434
+ end
435
+ end