parse-stack 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
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