parse-stack 1.6.0 → 1.6.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.
- checksums.yaml +4 -4
- data/Changes.md +8 -0
- data/Gemfile.lock +6 -16
- data/lib/parse/client/batch.rb +3 -5
- data/lib/parse/client/caching.rb +8 -3
- data/lib/parse/model/core/actions.rb +51 -2
- data/lib/parse/query.rb +45 -29
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/webhooks/registration.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6f2fcd731c3e5c535c2fcd5b6ad7de0411ee3b8
|
4
|
+
data.tar.gz: d43b5c15e4ca7ff3528d53c09d9f333ea5cca2cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7521b1712daddb91201f06062a327443f65467c4098441b68e06de9a53b862c5e2fe36d5f66abb68d974aae098b5a9218315e9de0ab494b47032ad02a81c0a94
|
7
|
+
data.tar.gz: 75e194bbed8d4aebbb8c1ab2fd08f1d4b6c1b1c58191df19bcaf356bfceac761c40874321049e2a83b622b02b68cdf1b167652cac9f796c3f2a1a59aca062297
|
data/Changes.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## Parse-Stack Changelog
|
2
2
|
|
3
|
+
### 1.6.1
|
4
|
+
- NEW: Batch requests are now parallelized.
|
5
|
+
- `skip` in queries no longer capped to 10,000.
|
6
|
+
- `limit` in queries no longer capped at 1000.
|
7
|
+
- `all()` queries can now return as many results as possible.
|
8
|
+
- NEW: `each()` method on Parse::Object subclasses to iterate
|
9
|
+
over all records in the colleciton.
|
10
|
+
|
3
11
|
### 1.6.0
|
4
12
|
- NEW: Auto generate models based on your remote schema.
|
5
13
|
- The default server url is now 'http://localhost:1337/parse'.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
parse-stack (1.6.
|
4
|
+
parse-stack (1.6.1)
|
5
5
|
active_model_serializers (>= 0.9, < 1)
|
6
6
|
activemodel (>= 4.2.1, < 6)
|
7
7
|
activesupport (>= 4.2.1, < 6)
|
@@ -27,11 +27,10 @@ GEM
|
|
27
27
|
erubis (~> 2.7.0)
|
28
28
|
rails-dom-testing (~> 2.0)
|
29
29
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
30
|
-
active_model_serializers (0.10.
|
30
|
+
active_model_serializers (0.10.3)
|
31
31
|
actionpack (>= 4.1, < 6)
|
32
32
|
activemodel (>= 4.1, < 6)
|
33
|
-
jsonapi (
|
34
|
-
railties (>= 4.1, < 6)
|
33
|
+
jsonapi (= 0.1.1.beta2)
|
35
34
|
activemodel (5.0.0.1)
|
36
35
|
activesupport (= 5.0.0.1)
|
37
36
|
activesupport (5.0.0.1)
|
@@ -53,11 +52,9 @@ GEM
|
|
53
52
|
faraday_middleware (0.10.1)
|
54
53
|
faraday (>= 0.7.4, < 1.0)
|
55
54
|
i18n (0.7.0)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
jsonapi-parser (0.1.1.beta3)
|
60
|
-
jsonapi-renderer (0.1.1.beta1)
|
55
|
+
json (1.8.3)
|
56
|
+
jsonapi (0.1.1.beta2)
|
57
|
+
json (~> 1.8)
|
61
58
|
loofah (2.0.3)
|
62
59
|
nokogiri (>= 1.5.9)
|
63
60
|
method_source (0.8.2)
|
@@ -85,15 +82,8 @@ GEM
|
|
85
82
|
nokogiri (~> 1.6.0)
|
86
83
|
rails-html-sanitizer (1.0.3)
|
87
84
|
loofah (~> 2.0)
|
88
|
-
railties (5.0.0.1)
|
89
|
-
actionpack (= 5.0.0.1)
|
90
|
-
activesupport (= 5.0.0.1)
|
91
|
-
method_source
|
92
|
-
rake (>= 0.8.7)
|
93
|
-
thor (>= 0.18.1, < 2.0)
|
94
85
|
rake (11.3.0)
|
95
86
|
slop (3.6.0)
|
96
|
-
thor (0.19.1)
|
97
87
|
thread_safe (0.3.5)
|
98
88
|
tzinfo (1.2.2)
|
99
89
|
thread_safe (~> 0.1)
|
data/lib/parse/client/batch.rb
CHANGED
@@ -36,7 +36,7 @@ module Parse
|
|
36
36
|
# @see Array.destroy
|
37
37
|
class BatchOperation
|
38
38
|
include Enumerable
|
39
|
-
|
39
|
+
|
40
40
|
# @!attribute requests
|
41
41
|
# @return [Array] the set of requests in this batch.
|
42
42
|
|
@@ -130,10 +130,8 @@ module Parse
|
|
130
130
|
def submit(segment = 50)
|
131
131
|
@responses = []
|
132
132
|
@requests.uniq!(&:signature)
|
133
|
-
@requests.each_slice(segment) do |slice|
|
134
|
-
|
135
|
-
#throttle
|
136
|
-
# sleep (slice.count.to_f / MAX_REQ_SEC.to_f )
|
133
|
+
@responses = @requests.each_slice(segment).to_a.threaded_map(2) do |slice|
|
134
|
+
client.batch_request( BatchOperation.new(slice) )
|
137
135
|
end
|
138
136
|
@responses.flatten!
|
139
137
|
#puts "Requests: #{@requests.count} == Response: #{@responses.count}"
|
data/lib/parse/client/caching.rb
CHANGED
@@ -148,7 +148,7 @@ module Parse
|
|
148
148
|
@store.delete "mk:#{url.to_s}" # master key cache-key
|
149
149
|
@store.delete @cache_key # final key
|
150
150
|
end
|
151
|
-
rescue Errno::EINVAL, Redis::CannotConnectError => e
|
151
|
+
rescue Errno::EINVAL, Redis::CannotConnectError, Redis::TimeoutError => e
|
152
152
|
# if the cache store fails to connect, catch the exception but proceed
|
153
153
|
# with the regular request, but turn off caching for this request. It is possible
|
154
154
|
# that the cache connection resumes at a later point, so this is temporary.
|
@@ -158,10 +158,15 @@ module Parse
|
|
158
158
|
|
159
159
|
@app.call(env).on_complete do |response_env|
|
160
160
|
# Only cache GET requests with valid HTTP status codes whose content-length
|
161
|
-
# is
|
161
|
+
# is between 20 bytes and 1MB. Otherwise they could be errors, successes and empty result sets.
|
162
|
+
|
162
163
|
if @enabled && method == :get && CACHEABLE_HTTP_CODES.include?(response_env.status) &&
|
163
|
-
|
164
|
+
response_env.present? && response_env.response_headers[CONTENT_LENGTH_KEY].to_i.between?(20,1_000_000)
|
165
|
+
begin
|
164
166
|
@store.store(@cache_key, response_env, expires: @expires) # ||= response_env.body
|
167
|
+
rescue => e
|
168
|
+
puts "[Parse::Cache] Store Error: #{e}"
|
169
|
+
end
|
165
170
|
end # if
|
166
171
|
# do something with the response
|
167
172
|
# response_env[:response_headers].merge!(...)
|
@@ -169,6 +169,54 @@ module Parse
|
|
169
169
|
obj
|
170
170
|
end
|
171
171
|
|
172
|
+
# This methods allow you to efficiently iterate over all the records in the collection
|
173
|
+
# (lower memory cost) at a minor cost of performance. This method utilizes
|
174
|
+
# the `created_at` field of Parse records to order and iterate over all records,
|
175
|
+
# therefore you should not use this method if you want to perform a query
|
176
|
+
# with constraints against `created_at` field or need specific type of ordering.
|
177
|
+
# @param constraints [Hash] a set of query constraints.
|
178
|
+
# @yield a block which will iterate through each matching record.
|
179
|
+
# @example
|
180
|
+
#
|
181
|
+
# post = Post.first
|
182
|
+
# # iterate over all comments matching conditions
|
183
|
+
# Comments.each( post: post) do |comment|
|
184
|
+
# # ...
|
185
|
+
# end
|
186
|
+
# @return [Parse::Object] the last Parse::Object record processed.
|
187
|
+
def each(constraints = {}, **opts, &block)
|
188
|
+
#anchor_date = opts[:anchor_date] || Parse::Date.now
|
189
|
+
batch_size = 250
|
190
|
+
start_cursor = first( order: :created_at.asc, keys: :created_at )
|
191
|
+
constraints.merge! cache: false, limit: batch_size, order: :created_at.asc
|
192
|
+
all_query = query(constraints)
|
193
|
+
cursor = start_cursor
|
194
|
+
# the exclusion set is a set of ids not to include the next query.
|
195
|
+
exclusion_set = []
|
196
|
+
loop do
|
197
|
+
_q = query(constraints.dup)
|
198
|
+
_q.where(:created_at.on_or_after => cursor.created_at)
|
199
|
+
# set of ids not to include in the next query. non-performant, but accurate.
|
200
|
+
_q.where(:id.nin => exclusion_set) unless exclusion_set.empty?
|
201
|
+
results = _q.results # get results
|
202
|
+
|
203
|
+
break cursor if results.empty? # break if no results
|
204
|
+
results.each(&block)
|
205
|
+
next_cursor = results.last
|
206
|
+
# break if we got less than the maximum requested
|
207
|
+
break next_cursor if results.count < batch_size
|
208
|
+
# break if the next object is the same as the current object.
|
209
|
+
break next_cursor if cursor.id == next_cursor.id
|
210
|
+
# The exclusion set is used in the case where multiple records have the exact
|
211
|
+
# same created_at date (down to the microsecond). This prevents getting the same
|
212
|
+
# record in the next query request.
|
213
|
+
exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id)
|
214
|
+
results = nil
|
215
|
+
cursor = next_cursor
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
172
220
|
# Auto save all objects matching the query constraints. This method is
|
173
221
|
# meant to be used with a block. Any objects that are modified in the block
|
174
222
|
# will be batched for a save operation. This uses the `updated_at` field to
|
@@ -185,7 +233,7 @@ module Parse
|
|
185
233
|
# @return [Boolean] whether there were any errors.
|
186
234
|
def save_all(constraints = {})
|
187
235
|
force = false
|
188
|
-
|
236
|
+
batch_size = 250
|
189
237
|
iterator_block = nil
|
190
238
|
if block_given?
|
191
239
|
iterator_block = Proc.new
|
@@ -202,7 +250,7 @@ module Parse
|
|
202
250
|
constraints.merge! :updated_at.on_or_before => anchor_date
|
203
251
|
constraints.merge! cache: false
|
204
252
|
# oldest first, so we create a reduction-cycle
|
205
|
-
constraints.merge! order: :updated_at.asc, limit:
|
253
|
+
constraints.merge! order: :updated_at.asc, limit: batch_size
|
206
254
|
update_query = query(constraints)
|
207
255
|
#puts "Setting Anchor Date: #{anchor_date}"
|
208
256
|
cursor = nil
|
@@ -231,6 +279,7 @@ module Parse
|
|
231
279
|
# cursor_item = results.max_by(&updated_comparison_block).updated_at
|
232
280
|
# puts "[Parse::SaveAll] Updated #{results.count} records updated <= #{cursor.updated_at}"
|
233
281
|
|
282
|
+
break if results.count < batch_size # we didn't hit a cap on results.
|
234
283
|
if cursor.is_a?(Parse::Object)
|
235
284
|
update_query.where :updated_at.gte => cursor.updated_at
|
236
285
|
|
data/lib/parse/query.rb
CHANGED
@@ -386,16 +386,14 @@ module Parse
|
|
386
386
|
self #chaining
|
387
387
|
end #order
|
388
388
|
|
389
|
-
# Use with limit to paginate through results. Default is 0
|
390
|
-
# maximum value being 10,000.
|
389
|
+
# Use with limit to paginate through results. Default is 0.
|
391
390
|
# @example
|
392
391
|
# # get the next 3 songs after the first 10
|
393
392
|
# Song.all :limit => 3, :skip => 10
|
394
|
-
# @param
|
393
|
+
# @param amount [Integer] The number of records to skip.
|
395
394
|
# @return [self]
|
396
|
-
def skip(
|
397
|
-
|
398
|
-
@skip = [ 0, count.to_i, 10_000].sort[1]
|
395
|
+
def skip(amount)
|
396
|
+
@skip = [0,amount.to_i].max
|
399
397
|
@results = nil
|
400
398
|
self #chaining
|
401
399
|
end
|
@@ -403,21 +401,21 @@ module Parse
|
|
403
401
|
# Limit the number of objects returned by the query. The default is 100, with
|
404
402
|
# Parse allowing a maximum of 1000. The framework also allows a value of
|
405
403
|
# `:max`. Utilizing this will have the framework continually intelligently
|
406
|
-
# utilize `:skip` to continue to paginate through results until
|
407
|
-
#
|
408
|
-
#
|
404
|
+
# utilize `:skip` to continue to paginate through results until no more results
|
405
|
+
# match the query criteria. When utilizing `all()`, `:max` is the default
|
406
|
+
# option for `:limit`.
|
409
407
|
# @example
|
410
408
|
# Song.all :limit => 1 # same as Song.first
|
411
|
-
# Song.all :limit =>
|
412
|
-
# Song.all :limit => :max #
|
409
|
+
# Song.all :limit => 2025 # large limits supported.
|
410
|
+
# Song.all :limit => :max # as many records as possible.
|
413
411
|
# @param count [Integer,Symbol] The number of records to return. You may pass :max
|
414
412
|
# to get as many as 11_000 records with the aid if skipping.
|
415
413
|
# @return [self]
|
416
414
|
def limit(count)
|
417
|
-
if count
|
418
|
-
@limit =
|
419
|
-
elsif count
|
420
|
-
@limit =
|
415
|
+
if count.is_a?(Numeric)
|
416
|
+
@limit = [ 0, count.to_i ].max
|
417
|
+
elsif count == :max
|
418
|
+
@limit = :max
|
421
419
|
else
|
422
420
|
@limit = nil
|
423
421
|
end
|
@@ -643,32 +641,51 @@ module Parse
|
|
643
641
|
# max_results is used to iterate through as many API requests as possible using
|
644
642
|
# :skip and :limit paramter.
|
645
643
|
# @!visibility private
|
646
|
-
def max_results(raw: false)
|
644
|
+
def max_results(raw: false, on_batch: nil, discard_results: false)
|
647
645
|
compiled_query = compile
|
648
|
-
|
649
|
-
query_skip = compiled_query[:skip] ||= 0
|
650
|
-
compiled_query[:limit] = 1_000
|
651
|
-
iterations = (query_limit/1000.0).ceil
|
646
|
+
batch_size = 1_000
|
652
647
|
results = []
|
648
|
+
# determine if there is a user provided hard limit
|
649
|
+
_limit = (@limit.is_a?(Numeric) && @limit > 0) ? @limit : nil
|
650
|
+
compiled_query[:skip] ||= 0
|
651
|
+
|
652
|
+
loop do
|
653
|
+
# always reset the batch size
|
654
|
+
compiled_query[:limit] = batch_size
|
655
|
+
|
656
|
+
# if a hard limit was set by the user, then if the remaining amount
|
657
|
+
# is less than the batch size, set the new limit to the remaining amount.
|
658
|
+
unless _limit.nil?
|
659
|
+
compiled_query[:limit] = _limit if _limit < batch_size
|
660
|
+
end
|
653
661
|
|
654
|
-
iterations.times do |idx|
|
655
662
|
response = fetch!( compiled_query )
|
656
663
|
break if response.error? || response.results.empty?
|
657
664
|
|
658
665
|
items = response.results
|
659
666
|
items = decode(items) unless raw
|
660
|
-
|
667
|
+
# if a block is provided, we do not keep the results after processing.
|
661
668
|
if block_given?
|
662
669
|
items.each(&Proc.new)
|
663
670
|
else
|
664
|
-
results
|
671
|
+
# concat results unless discard_results is true
|
672
|
+
results += items unless discard_results
|
665
673
|
end
|
674
|
+
|
675
|
+
on_batch.call(items) if on_batch.present?
|
666
676
|
# if we get less than the maximum set of results, most likely the next
|
667
677
|
# query will return emtpy results - no need to perform it.
|
668
678
|
break if items.count < compiled_query[:limit]
|
679
|
+
|
680
|
+
# if we have a set limit, then subtract from the total amount the user requested
|
681
|
+
# from the total in the current result set. Break if we've reached our limit.
|
682
|
+
unless _limit.nil?
|
683
|
+
_limit -= items.count
|
684
|
+
break if _limit < 1
|
685
|
+
end
|
686
|
+
|
669
687
|
# add to the skip count for the next iteration
|
670
|
-
compiled_query[:skip] +=
|
671
|
-
break if compiled_query[:skip] > 10_000
|
688
|
+
compiled_query[:skip] += batch_size
|
672
689
|
end
|
673
690
|
results
|
674
691
|
end
|
@@ -722,14 +739,14 @@ module Parse
|
|
722
739
|
# @return [Array<Parse::Object>] if raw is set to false, a list of matching Parse::Object subclasses.
|
723
740
|
def results(raw: false)
|
724
741
|
if @results.nil?
|
725
|
-
if
|
742
|
+
if block_given?
|
743
|
+
max_results(raw: raw, &Proc.new)
|
744
|
+
elsif @limit.is_a?(Numeric)
|
726
745
|
response = fetch!( compile )
|
727
746
|
return [] if response.error?
|
728
747
|
items = raw ? response.results : decode(response.results)
|
729
748
|
return items.each(&Proc.new) if block_given?
|
730
749
|
@results = items
|
731
|
-
elsif block_given?
|
732
|
-
return max_results(raw: raw, &Proc.new)
|
733
750
|
else
|
734
751
|
@results = max_results(raw: raw)
|
735
752
|
end
|
@@ -780,7 +797,6 @@ module Parse
|
|
780
797
|
def compile(encode: true, includeClassName: false)
|
781
798
|
run_callbacks :prepare do
|
782
799
|
q = {} #query
|
783
|
-
q[:limit] = 11_000 if @limit == :max
|
784
800
|
q[:limit] = @limit if @limit.is_a?(Numeric) && @limit > 0
|
785
801
|
q[:skip] = @skip if @skip > 0
|
786
802
|
|
data/lib/parse/stack/version.rb
CHANGED
@@ -93,7 +93,7 @@ module Parse
|
|
93
93
|
classNames = routes[trigger].keys.dup
|
94
94
|
if include_wildcard && classNames.include?('*') #then create the list for all classes
|
95
95
|
classNames.delete '*' #delete the wildcard before we expand it
|
96
|
-
classNames = classNames + Parse.registered_classes
|
96
|
+
classNames = classNames + Parse.registered_classes
|
97
97
|
classNames.uniq!
|
98
98
|
end
|
99
99
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parse-stack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Persaud
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -274,7 +274,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
274
274
|
version: '0'
|
275
275
|
requirements: []
|
276
276
|
rubyforge_project:
|
277
|
-
rubygems_version: 2.
|
277
|
+
rubygems_version: 2.5.1
|
278
278
|
signing_key:
|
279
279
|
specification_version: 4
|
280
280
|
summary: Parse-Server Ruby Client and Relational Mapper
|