parse-stack 1.8.0 → 1.8.1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.solargraph.yml +23 -0
  3. data/.travis.yml +0 -1
  4. data/Gemfile +13 -12
  5. data/Gemfile.lock +88 -51
  6. data/README.md +2 -4
  7. data/Rakefile +14 -14
  8. data/lib/parse/api/aggregate.rb +4 -7
  9. data/lib/parse/api/all.rb +1 -1
  10. data/lib/parse/api/analytics.rb +0 -3
  11. data/lib/parse/api/batch.rb +3 -5
  12. data/lib/parse/api/cloud_functions.rb +0 -3
  13. data/lib/parse/api/config.rb +0 -4
  14. data/lib/parse/api/files.rb +3 -7
  15. data/lib/parse/api/hooks.rb +4 -8
  16. data/lib/parse/api/objects.rb +7 -12
  17. data/lib/parse/api/push.rb +0 -4
  18. data/lib/parse/api/schema.rb +2 -6
  19. data/lib/parse/api/server.rb +4 -7
  20. data/lib/parse/api/sessions.rb +2 -5
  21. data/lib/parse/api/users.rb +9 -14
  22. data/lib/parse/client.rb +54 -50
  23. data/lib/parse/client/authentication.rb +29 -33
  24. data/lib/parse/client/batch.rb +8 -11
  25. data/lib/parse/client/body_builder.rb +19 -20
  26. data/lib/parse/client/caching.rb +23 -28
  27. data/lib/parse/client/protocol.rb +11 -12
  28. data/lib/parse/client/request.rb +4 -6
  29. data/lib/parse/client/response.rb +5 -7
  30. data/lib/parse/model/acl.rb +14 -12
  31. data/lib/parse/model/associations/belongs_to.rb +14 -21
  32. data/lib/parse/model/associations/collection_proxy.rb +328 -329
  33. data/lib/parse/model/associations/has_many.rb +18 -25
  34. data/lib/parse/model/associations/has_one.rb +6 -11
  35. data/lib/parse/model/associations/pointer_collection_proxy.rb +5 -8
  36. data/lib/parse/model/associations/relation_collection_proxy.rb +5 -9
  37. data/lib/parse/model/bytes.rb +8 -10
  38. data/lib/parse/model/classes/installation.rb +2 -4
  39. data/lib/parse/model/classes/product.rb +2 -5
  40. data/lib/parse/model/classes/role.rb +3 -5
  41. data/lib/parse/model/classes/session.rb +2 -5
  42. data/lib/parse/model/classes/user.rb +20 -16
  43. data/lib/parse/model/core/actions.rb +31 -46
  44. data/lib/parse/model/core/builder.rb +6 -6
  45. data/lib/parse/model/core/errors.rb +0 -1
  46. data/lib/parse/model/core/fetching.rb +45 -50
  47. data/lib/parse/model/core/properties.rb +51 -66
  48. data/lib/parse/model/core/querying.rb +291 -294
  49. data/lib/parse/model/core/schema.rb +89 -92
  50. data/lib/parse/model/date.rb +16 -17
  51. data/lib/parse/model/file.rb +171 -174
  52. data/lib/parse/model/geopoint.rb +12 -16
  53. data/lib/parse/model/model.rb +31 -37
  54. data/lib/parse/model/object.rb +47 -53
  55. data/lib/parse/model/pointer.rb +177 -176
  56. data/lib/parse/model/push.rb +8 -10
  57. data/lib/parse/model/shortnames.rb +1 -2
  58. data/lib/parse/model/time_zone.rb +3 -5
  59. data/lib/parse/query.rb +34 -35
  60. data/lib/parse/query/constraint.rb +4 -6
  61. data/lib/parse/query/constraints.rb +21 -29
  62. data/lib/parse/query/operation.rb +8 -11
  63. data/lib/parse/query/ordering.rb +45 -49
  64. data/lib/parse/stack.rb +11 -12
  65. data/lib/parse/stack/generators/rails.rb +28 -30
  66. data/lib/parse/stack/generators/templates/model.erb +5 -6
  67. data/lib/parse/stack/generators/templates/model_installation.rb +0 -1
  68. data/lib/parse/stack/generators/templates/model_role.rb +0 -1
  69. data/lib/parse/stack/generators/templates/model_session.rb +0 -1
  70. data/lib/parse/stack/generators/templates/model_user.rb +0 -1
  71. data/lib/parse/stack/generators/templates/parse.rb +9 -9
  72. data/lib/parse/stack/generators/templates/webhooks.rb +1 -2
  73. data/lib/parse/stack/railtie.rb +2 -4
  74. data/lib/parse/stack/tasks.rb +70 -86
  75. data/lib/parse/stack/version.rb +1 -1
  76. data/lib/parse/webhooks.rb +19 -26
  77. data/lib/parse/webhooks/payload.rb +26 -28
  78. data/lib/parse/webhooks/registration.rb +23 -31
  79. data/parse-stack.gemspec +25 -25
  80. data/parse-stack.png +0 -0
  81. metadata +13 -7
  82. data/.github/parse-ruby-sdk.png +0 -0
@@ -1,333 +1,330 @@
1
1
  # encoding: UTF-8
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../../query'
4
+ require_relative "../../query"
5
5
 
6
6
  module Parse
7
7
  module Core
8
8
  # Defines the querying methods applied to a Parse::Object.
9
9
  module Querying
10
10
 
11
- # This feature is a small subset of the
12
- # {http://guides.rubyonrails.org/active_record_querying.html#scopes
13
- # ActiveRecord named scopes} feature. Scoping allows you to specify
14
- # commonly-used queries which can be referenced as class method calls and
15
- # are chainable with other scopes. You can use every {Parse::Query}
16
- # method previously covered such as `where`, `includes` and `limit`.
17
- #
18
- # class Article < Parse::Object
19
- # property :published, :boolean
20
- # scope :published, -> { query(published: true) }
21
- # end
22
- #
23
- # This is the same as defining your own class method for the query.
24
- #
25
- # class Article < Parse::Object
26
- # def self.published
27
- # query(published: true)
28
- # end
29
- # end
30
- #
31
- # You can also chain scopes and pass parameters. In addition, boolean and
32
- # enumerated properties have automatically generated scopes for you to use.
33
- #
34
- # class Article < Parse::Object
35
- # scope :published, -> { query(published: true) }
36
- #
37
- # property :comment_count, :integer
38
- # property :category
39
- # property :approved, :boolean
40
- #
41
- # scope :published_and_commented, -> { published.where :comment_count.gt => 0 }
42
- # scope :popular_topics, ->(name) { published_and_commented.where category: name }
43
- # end
44
- #
45
- # # simple scope
46
- # Article.published # => where published is true
47
- #
48
- # # chained scope
49
- # Article.published_and_commented # published is true and comment_count > 0
50
- #
51
- # # scope with parameters
52
- # Article.popular_topic("music") # => popular music articles
53
- # # equivalent: where(published: true, :comment_count.gt => 0, category: name)
54
- #
55
- # # automatically generated scope
56
- # Article.approved(category: "tour") # => where approved: true, category: 'tour'
57
- #
58
- # If you would like to turn off automatic scope generation for property types,
59
- # set the option `:scope` to false when declaring the property.
60
- # @param name [Symbol] the name of the scope.
61
- # @param body [Proc] the proc related to the scope.
62
- # @raise ArgumentError if body parameter does not respond to `call`
63
- # @return [Symbol] the name of the singleton method created.
64
- def scope(name, body)
65
- unless body.respond_to?(:call)
66
- raise ArgumentError, 'The scope body needs to be callable.'
67
- end
68
-
69
- name = name.to_sym
70
- if respond_to?(name, true)
71
- puts "Creating scope :#{name}. Will overwrite existing method #{self}.#{name}."
72
- end
11
+ # This feature is a small subset of the
12
+ # {http://guides.rubyonrails.org/active_record_querying.html#scopes
13
+ # ActiveRecord named scopes} feature. Scoping allows you to specify
14
+ # commonly-used queries which can be referenced as class method calls and
15
+ # are chainable with other scopes. You can use every {Parse::Query}
16
+ # method previously covered such as `where`, `includes` and `limit`.
17
+ #
18
+ # class Article < Parse::Object
19
+ # property :published, :boolean
20
+ # scope :published, -> { query(published: true) }
21
+ # end
22
+ #
23
+ # This is the same as defining your own class method for the query.
24
+ #
25
+ # class Article < Parse::Object
26
+ # def self.published
27
+ # query(published: true)
28
+ # end
29
+ # end
30
+ #
31
+ # You can also chain scopes and pass parameters. In addition, boolean and
32
+ # enumerated properties have automatically generated scopes for you to use.
33
+ #
34
+ # class Article < Parse::Object
35
+ # scope :published, -> { query(published: true) }
36
+ #
37
+ # property :comment_count, :integer
38
+ # property :category
39
+ # property :approved, :boolean
40
+ #
41
+ # scope :published_and_commented, -> { published.where :comment_count.gt => 0 }
42
+ # scope :popular_topics, ->(name) { published_and_commented.where category: name }
43
+ # end
44
+ #
45
+ # # simple scope
46
+ # Article.published # => where published is true
47
+ #
48
+ # # chained scope
49
+ # Article.published_and_commented # published is true and comment_count > 0
50
+ #
51
+ # # scope with parameters
52
+ # Article.popular_topic("music") # => popular music articles
53
+ # # equivalent: where(published: true, :comment_count.gt => 0, category: name)
54
+ #
55
+ # # automatically generated scope
56
+ # Article.approved(category: "tour") # => where approved: true, category: 'tour'
57
+ #
58
+ # If you would like to turn off automatic scope generation for property types,
59
+ # set the option `:scope` to false when declaring the property.
60
+ # @param name [Symbol] the name of the scope.
61
+ # @param body [Proc] the proc related to the scope.
62
+ # @raise ArgumentError if body parameter does not respond to `call`
63
+ # @return [Symbol] the name of the singleton method created.
64
+ def scope(name, body)
65
+ unless body.respond_to?(:call)
66
+ raise ArgumentError, "The scope body needs to be callable."
67
+ end
73
68
 
74
- define_singleton_method(name) do |*args, &block|
69
+ name = name.to_sym
70
+ if respond_to?(name, true)
71
+ puts "Creating scope :#{name}. Will overwrite existing method #{self}.#{name}."
72
+ end
75
73
 
76
- if body.arity.zero?
77
- res = body.call
78
- res.conditions(*args) if args.present?
79
- else
80
- res = body.call(*args)
81
- end
74
+ define_singleton_method(name) do |*args, &block|
75
+ if body.arity.zero?
76
+ res = body.call
77
+ res.conditions(*args) if args.present?
78
+ else
79
+ res = body.call(*args)
80
+ end
82
81
 
83
- _q = res || query
82
+ _q = res || query
84
83
 
85
- if _q.is_a?(Parse::Query)
86
- klass = self
87
- _q.define_singleton_method(:method_missing) do |m, *args, &chained_block|
88
- if klass.respond_to?(m, true)
89
- # must be a scope
90
- klass_scope = klass.send(m, *args)
91
- if klass_scope.is_a?(Parse::Query)
92
- # merge constraints
93
- add_constraints( klass_scope.constraints )
94
- # if a block was passed, execute the query, otherwise return the query
95
- return chained_block.present? ? results(&chained_block) : self
96
- end # if
97
- klass = nil # help clean up ruby gc
98
- return klass_scope
99
- end
84
+ if _q.is_a?(Parse::Query)
85
+ klass = self
86
+ _q.define_singleton_method(:method_missing) do |m, *args, &chained_block|
87
+ if klass.respond_to?(m, true)
88
+ # must be a scope
89
+ klass_scope = klass.send(m, *args)
90
+ if klass_scope.is_a?(Parse::Query)
91
+ # merge constraints
92
+ add_constraints(klass_scope.constraints)
93
+ # if a block was passed, execute the query, otherwise return the query
94
+ return chained_block.present? ? results(&chained_block) : self
95
+ end # if
100
96
  klass = nil # help clean up ruby gc
101
- return results.send(m, *args, &chained_block)
97
+ return klass_scope
102
98
  end
99
+ klass = nil # help clean up ruby gc
100
+ return results.send(m, *args, &chained_block)
103
101
  end
104
-
105
- Parse::Query.apply_auto_introspection!(_q)
106
-
107
- return _q if block.nil?
108
- _q.results(&block)
109
102
  end
110
103
 
111
- end
104
+ Parse::Query.apply_auto_introspection!(_q)
112
105
 
113
-
114
- # Creates a new {Parse::Query} with the given constraints for this class.
115
- # @example
116
- # # assume Post < Parse::Object
117
- # query = Post.query(:updated_at.before => DateTime.now)
118
- # @return [Parse::Query] a new query with the given constraints for this
119
- # Parse::Object subclass.
120
- def query(constraints = {})
121
- Parse::Query.new self.parse_class, constraints
122
- end; alias_method :where, :query
123
-
124
- # @param conditions (see Parse::Query#where)
125
- # @return (see Parse::Query#where)
126
- # @see Parse::Query#where
127
- def literal_where(conditions = {})
128
- query.where(conditions)
106
+ return _q if block.nil?
107
+ _q.results(&block)
129
108
  end
109
+ end
130
110
 
131
- # This methods allow you to efficiently iterate over all the records in the collection
132
- # (lower memory cost) at a minor cost of performance. This method utilizes
133
- # the `created_at` field of Parse records to order and iterate over *all* matching records,
134
- # therefore you should not use this method if you want to perform a query
135
- # with constraints against the `created_at` field or need specific type of ordering.
136
- # If you need to use `:created_at` in your constraints, consider using {Parse::Core::Querying#all} or
137
- # {Parse::Core::Actions::ClassMethods#save_all}
138
- # @param constraints [Hash] a set of query constraints.
139
- # @yield a block which will iterate through each matching record.
140
- # @example
141
- #
142
- # post = Post.first
143
- # # iterate over all comments matching conditions
144
- # Comment.each(post: post) do |comment|
145
- # # ...
146
- # end
147
- # @return [Parse::Object] the last Parse::Object record processed.
148
- # @note You cannot use *:created_at* as a constraint.
149
- # @raise ArgumentError if :created_at is detected in the constraints argument.
150
- # @see Parse::Core::Querying.all
151
- # @see Parse::Core::Actions.save_all
152
- def each(constraints = {}, &block)
153
- # verify we don't hvae created at as a constraint, otherwise this will not work
154
- invalid_constraints = constraints.keys.any? do |k|
155
- (k == :created_at || k == :createdAt) ||
156
- ( k.is_a?(Parse::Operation) && (k.operand == :created_at || k.operand == :createdAt) )
157
- end
158
- if invalid_constraints
159
- raise ArgumentError, "[#{self.class}.each] Special method each()" \
160
- "cannot be used with a :created_at constraint."
161
- end
162
- batch_size = 250
163
- start_cursor = first( order: :created_at.asc, keys: :created_at )
164
- constraints.merge! cache: false, limit: batch_size, order: :created_at.asc
165
- all_query = query(constraints)
166
- cursor = start_cursor
167
- # the exclusion set is a set of ids not to include the next query.
168
- exclusion_set = []
169
- loop do
170
- _q = query(constraints.dup)
171
- _q.where(:created_at.on_or_after => cursor.created_at)
172
- # set of ids not to include in the next query. non-performant, but accurate.
173
- _q.where(:id.nin => exclusion_set) unless exclusion_set.empty?
174
- results = _q.results # get results
111
+ # Creates a new {Parse::Query} with the given constraints for this class.
112
+ # @example
113
+ # # assume Post < Parse::Object
114
+ # query = Post.query(:updated_at.before => DateTime.now)
115
+ # @return [Parse::Query] a new query with the given constraints for this
116
+ # Parse::Object subclass.
117
+ def query(constraints = {})
118
+ Parse::Query.new self.parse_class, constraints
119
+ end;
120
+ alias_method :where, :query
175
121
 
176
- break cursor if results.empty? # break if no results
177
- results.each(&block)
178
- next_cursor = results.last
179
- # break if we got less than the maximum requested
180
- break next_cursor if results.count < batch_size
181
- # break if the next object is the same as the current object.
182
- break next_cursor if cursor.id == next_cursor.id
183
- # The exclusion set is used in the case where multiple records have the exact
184
- # same created_at date (down to the microsecond). This prevents getting the same
185
- # record in the next query request.
186
- exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id)
187
- results = nil
188
- cursor = next_cursor
189
- end
122
+ # @param conditions (see Parse::Query#where)
123
+ # @return (see Parse::Query#where)
124
+ # @see Parse::Query#where
125
+ def literal_where(conditions = {})
126
+ query.where(conditions)
127
+ end
190
128
 
129
+ # This methods allow you to efficiently iterate over all the records in the collection
130
+ # (lower memory cost) at a minor cost of performance. This method utilizes
131
+ # the `created_at` field of Parse records to order and iterate over *all* matching records,
132
+ # therefore you should not use this method if you want to perform a query
133
+ # with constraints against the `created_at` field or need specific type of ordering.
134
+ # If you need to use `:created_at` in your constraints, consider using {Parse::Core::Querying#all} or
135
+ # {Parse::Core::Actions::ClassMethods#save_all}
136
+ # @param constraints [Hash] a set of query constraints.
137
+ # @yield a block which will iterate through each matching record.
138
+ # @example
139
+ #
140
+ # post = Post.first
141
+ # # iterate over all comments matching conditions
142
+ # Comment.each(post: post) do |comment|
143
+ # # ...
144
+ # end
145
+ # @return [Parse::Object] the last Parse::Object record processed.
146
+ # @note You cannot use *:created_at* as a constraint.
147
+ # @raise ArgumentError if :created_at is detected in the constraints argument.
148
+ # @see Parse::Core::Querying.all
149
+ # @see Parse::Core::Actions.save_all
150
+ def each(constraints = {}, &block)
151
+ # verify we don't hvae created at as a constraint, otherwise this will not work
152
+ invalid_constraints = constraints.keys.any? do |k|
153
+ (k == :created_at || k == :createdAt) ||
154
+ (k.is_a?(Parse::Operation) && (k.operand == :created_at || k.operand == :createdAt))
191
155
  end
192
-
193
- # Fetch all matching objects in this collection matching the constraints.
194
- # This will be the most common way when querying Parse objects for a subclass.
195
- # When no block is passed, all objects are returned. Using a block is more memory
196
- # efficient as matching objects are fetched in batches and discarded after the iteration
197
- # is completed.
198
- # @param constraints [Hash] a set of {Parse::Query} constraints.
199
- # @yield a block to iterate with each matching object.
200
- # @example
201
- #
202
- # songs = Song.all( ... expressions ...) # => array of Parse::Objects
203
- # # memory efficient for large amounts of records.
204
- # Song.all( ... expressions ...) do |song|
205
- # # ... do something with song..
206
- # end
207
- #
208
- # @note This method will continually query for records by automatically
209
- # incrementing the *:skip* parameter until no more results are returned
210
- # by the server.
211
- # @return [Array<Parse::Object>] an array of matching objects. If a block is passed,
212
- # an empty array is returned.
213
- def all(constraints = {limit: :max})
214
- constraints = constraints.reverse_merge({limit: :max})
215
- prepared_query = query(constraints)
216
- return prepared_query.results(&Proc.new) if block_given?
217
- prepared_query.results
156
+ if invalid_constraints
157
+ raise ArgumentError, "[#{self.class}.each] Special method each()" \
158
+ "cannot be used with a :created_at constraint."
218
159
  end
160
+ batch_size = 250
161
+ start_cursor = first(order: :created_at.asc, keys: :created_at)
162
+ constraints.merge! cache: false, limit: batch_size, order: :created_at.asc
163
+ all_query = query(constraints)
164
+ cursor = start_cursor
165
+ # the exclusion set is a set of ids not to include the next query.
166
+ exclusion_set = []
167
+ loop do
168
+ _q = query(constraints.dup)
169
+ _q.where(:created_at.on_or_after => cursor.created_at)
170
+ # set of ids not to include in the next query. non-performant, but accurate.
171
+ _q.where(:id.nin => exclusion_set) unless exclusion_set.empty?
172
+ results = _q.results # get results
219
173
 
220
- # Returns the first item matching the constraint.
221
- # @overload first(count = 1)
222
- # @param count [Interger] The number of items to return.
223
- # @example
224
- # Object.first(2) # => an array of the first 2 objects in the collection.
225
- # @return [Parse::Object] if count == 1
226
- # @return [Array<Parse::Object>] if count > 1
227
- # @overload first(constraints = {})
228
- # @param constraints [Hash] a set of {Parse::Query} constraints.
229
- # @example
230
- # Object.first( :name => "Anthony" )
231
- # @return [Parse::Object] the first matching object.
232
- def first(constraints = {})
233
- fetch_count = 1
234
- if constraints.is_a?(Numeric)
235
- fetch_count = constraints.to_i
236
- constraints = {}
237
- end
238
- constraints.merge!( {limit: fetch_count} )
239
- res = query(constraints).results
240
- return res.first if fetch_count == 1
241
- return res.first fetch_count
174
+ break cursor if results.empty? # break if no results
175
+ results.each(&block)
176
+ next_cursor = results.last
177
+ # break if we got less than the maximum requested
178
+ break next_cursor if results.count < batch_size
179
+ # break if the next object is the same as the current object.
180
+ break next_cursor if cursor.id == next_cursor.id
181
+ # The exclusion set is used in the case where multiple records have the exact
182
+ # same created_at date (down to the microsecond). This prevents getting the same
183
+ # record in the next query request.
184
+ exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id)
185
+ results = nil
186
+ cursor = next_cursor
242
187
  end
188
+ end
243
189
 
244
- # Creates a count request which is more performant when counting objects.
245
- # @example
246
- # # number of songs with a like count greater than 20.
247
- # count = Song.count( :like_count.gt => 20 )
248
- # @param constraints (see #all)
249
- # @return [Interger] the number of records matching the query.
250
- # @see Parse::Query#count
251
- def count(constraints = {})
252
- query(constraints).count
253
- end
190
+ # Fetch all matching objects in this collection matching the constraints.
191
+ # This will be the most common way when querying Parse objects for a subclass.
192
+ # When no block is passed, all objects are returned. Using a block is more memory
193
+ # efficient as matching objects are fetched in batches and discarded after the iteration
194
+ # is completed.
195
+ # @param constraints [Hash] a set of {Parse::Query} constraints.
196
+ # @yield a block to iterate with each matching object.
197
+ # @example
198
+ #
199
+ # songs = Song.all( ... expressions ...) # => array of Parse::Objects
200
+ # # memory efficient for large amounts of records.
201
+ # Song.all( ... expressions ...) do |song|
202
+ # # ... do something with song..
203
+ # end
204
+ #
205
+ # @note This method will continually query for records by automatically
206
+ # incrementing the *:skip* parameter until no more results are returned
207
+ # by the server.
208
+ # @return [Array<Parse::Object>] an array of matching objects. If a block is passed,
209
+ # an empty array is returned.
210
+ def all(constraints = { limit: :max })
211
+ constraints = constraints.reverse_merge({ limit: :max })
212
+ prepared_query = query(constraints)
213
+ return prepared_query.results(&Proc.new) if block_given?
214
+ prepared_query.results
215
+ end
254
216
 
255
- # Finds the distinct values for a specified field across a single
256
- # collection or view and returns the results in an array.
257
- # @example
258
- # # get a list of unique city names for users who are older than 21.
259
- # cities = User.distinct(:city, :age.gt => 21 )
260
- # @param field The name of the field to use for unique aggregation.
261
- # @param constraints (see #all)
262
- # @return [Array] a list of distinct values
263
- # @see Parse::Query#distinct
264
- def distinct(field, constraints = {})
265
- query(constraints).distinct(field)
217
+ # Returns the first item matching the constraint.
218
+ # @overload first(count = 1)
219
+ # @param count [Interger] The number of items to return.
220
+ # @example
221
+ # Object.first(2) # => an array of the first 2 objects in the collection.
222
+ # @return [Parse::Object] if count == 1
223
+ # @return [Array<Parse::Object>] if count > 1
224
+ # @overload first(constraints = {})
225
+ # @param constraints [Hash] a set of {Parse::Query} constraints.
226
+ # @example
227
+ # Object.first( :name => "Anthony" )
228
+ # @return [Parse::Object] the first matching object.
229
+ def first(constraints = {})
230
+ fetch_count = 1
231
+ if constraints.is_a?(Numeric)
232
+ fetch_count = constraints.to_i
233
+ constraints = {}
266
234
  end
235
+ constraints.merge!({ limit: fetch_count })
236
+ res = query(constraints).results
237
+ return res.first if fetch_count == 1
238
+ return res.first fetch_count
239
+ end
267
240
 
268
- # Find objects matching the constraint ordered by the descending created_at date.
269
- # @param constraints (see #all)
270
- # @return [Array<Parse::Object>]
271
- def newest(constraints = {})
272
- constraints.merge!(order: :created_at.desc)
273
- _q = query(constraints)
274
- _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
275
- _q
276
- end
241
+ # Creates a count request which is more performant when counting objects.
242
+ # @example
243
+ # # number of songs with a like count greater than 20.
244
+ # count = Song.count( :like_count.gt => 20 )
245
+ # @param constraints (see #all)
246
+ # @return [Interger] the number of records matching the query.
247
+ # @see Parse::Query#count
248
+ def count(constraints = {})
249
+ query(constraints).count
250
+ end
277
251
 
278
- # Find objects matching the constraint ordered by the ascending created_at date.
279
- # @param constraints (see #all)
280
- # @return [Array<Parse::Object>]
281
- def oldest(constraints = {})
282
- constraints.merge!(order: :created_at.asc)
283
- _q = query(constraints)
284
- _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
285
- _q
286
- end
252
+ # Finds the distinct values for a specified field across a single
253
+ # collection or view and returns the results in an array.
254
+ # @example
255
+ # # get a list of unique city names for users who are older than 21.
256
+ # cities = User.distinct(:city, :age.gt => 21 )
257
+ # @param field The name of the field to use for unique aggregation.
258
+ # @param constraints (see #all)
259
+ # @return [Array] a list of distinct values
260
+ # @see Parse::Query#distinct
261
+ def distinct(field, constraints = {})
262
+ query(constraints).distinct(field)
263
+ end
287
264
 
288
- # Find objects for a given objectId in this collection.The result is a list
289
- # (or single item) of the objects that were successfully found.
290
- # @example
291
- # Object.find "<objectId>"
292
- # Object.find "<objectId>", "<objectId>"....
293
- # Object.find ["<objectId>", "<objectId>"]
294
- # @param parse_ids [String] the objectId to find.
295
- # @param type [Symbol] the fetching methodology to use if more than one id was passed.
296
- # - *:parallel* : Utilizes parrallel HTTP requests to fetch all objects requested.
297
- # - *:batch* : This uses a batch fetch request using a contained_in clause.
298
- # @param compact [Boolean] whether to remove nil items from the returned array for objects
299
- # that were not found.
300
- # @return [Parse::Object] if only one id was provided as a parameter.
301
- # @return [Array<Parse::Object>] if more than one id was provided as a parameter.
302
- def find(*parse_ids, type: :parallel, compact: true)
303
- # flatten the list of Object ids.
304
- parse_ids.flatten!
305
- parse_ids.compact!
306
- # determines if the result back to the call site is an array or a single result
307
- as_array = parse_ids.count > 1
308
- results = []
265
+ # Find objects matching the constraint ordered by the descending created_at date.
266
+ # @param constraints (see #all)
267
+ # @return [Array<Parse::Object>]
268
+ def newest(constraints = {})
269
+ constraints.merge!(order: :created_at.desc)
270
+ _q = query(constraints)
271
+ _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
272
+ _q
273
+ end
309
274
 
310
- if type == :batch
311
- # use a .in query with the given id as a list
312
- results = self.class.all(:id.in => parse_ids)
313
- else
314
- # use Parallel to make multiple threaded requests for finding these objects.
315
- # The benefit of using this as default is that each request goes to a specific URL
316
- # which is better than Query request (table scan). This in turn allows for caching of
317
- # individual objects.
318
- results = parse_ids.threaded_map do |parse_id|
319
- next nil unless parse_id.present?
320
- response = client.fetch_object(parse_class, parse_id)
321
- next nil if response.error?
322
- Parse::Object.build response.result, parse_class
323
- end
324
- end
325
- # removes any nil items in the array
326
- results.compact! if compact
275
+ # Find objects matching the constraint ordered by the ascending created_at date.
276
+ # @param constraints (see #all)
277
+ # @return [Array<Parse::Object>]
278
+ def oldest(constraints = {})
279
+ constraints.merge!(order: :created_at.asc)
280
+ _q = query(constraints)
281
+ _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) }
282
+ _q
283
+ end
327
284
 
328
- as_array ? results : results.first
329
- end; alias_method :get, :find
285
+ # Find objects for a given objectId in this collection.The result is a list
286
+ # (or single item) of the objects that were successfully found.
287
+ # @example
288
+ # Object.find "<objectId>"
289
+ # Object.find "<objectId>", "<objectId>"....
290
+ # Object.find ["<objectId>", "<objectId>"]
291
+ # @param parse_ids [String] the objectId to find.
292
+ # @param type [Symbol] the fetching methodology to use if more than one id was passed.
293
+ # - *:parallel* : Utilizes parrallel HTTP requests to fetch all objects requested.
294
+ # - *:batch* : This uses a batch fetch request using a contained_in clause.
295
+ # @param compact [Boolean] whether to remove nil items from the returned array for objects
296
+ # that were not found.
297
+ # @return [Parse::Object] if only one id was provided as a parameter.
298
+ # @return [Array<Parse::Object>] if more than one id was provided as a parameter.
299
+ def find(*parse_ids, type: :parallel, compact: true)
300
+ # flatten the list of Object ids.
301
+ parse_ids.flatten!
302
+ parse_ids.compact!
303
+ # determines if the result back to the call site is an array or a single result
304
+ as_array = parse_ids.count > 1
305
+ results = []
306
+
307
+ if type == :batch
308
+ # use a .in query with the given id as a list
309
+ results = self.class.all(:id.in => parse_ids)
310
+ else
311
+ # use Parallel to make multiple threaded requests for finding these objects.
312
+ # The benefit of using this as default is that each request goes to a specific URL
313
+ # which is better than Query request (table scan). This in turn allows for caching of
314
+ # individual objects.
315
+ results = parse_ids.threaded_map do |parse_id|
316
+ next nil unless parse_id.present?
317
+ response = client.fetch_object(parse_class, parse_id)
318
+ next nil if response.error?
319
+ Parse::Object.build response.result, parse_class
320
+ end
321
+ end
322
+ # removes any nil items in the array
323
+ results.compact! if compact
330
324
 
325
+ as_array ? results : results.first
326
+ end;
327
+ alias_method :get, :find
331
328
  end # Querying
332
329
  end
333
330
  end