quo 0.3.1 → 0.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.
- checksums.yaml +4 -4
- data/README.md +18 -24
- data/Steepfile +1 -0
- data/lib/quo/eager_query.rb +11 -8
- data/lib/quo/loaded_query.rb +18 -0
- data/lib/quo/merged_query.rb +0 -18
- data/lib/quo/query.rb +42 -24
- data/lib/quo/query_composer.rb +36 -22
- data/lib/quo/{enumerator.rb → results.rb} +4 -3
- data/lib/quo/rspec/helpers.rb +2 -2
- data/lib/quo/utilities/callstack.rb +1 -1
- data/lib/quo/utilities/wrap.rb +1 -1
- data/lib/quo/version.rb +1 -1
- data/lib/quo/wrapped_query.rb +4 -16
- data/lib/quo.rb +9 -2
- data/sig/quo/eager_query.rbs +5 -3
- data/sig/quo/loaded_query.rbs +7 -0
- data/sig/quo/merged_query.rbs +3 -1
- data/sig/quo/query.rbs +5 -5
- data/sig/quo/query_composer.rbs +16 -2
- data/sig/quo/{enumerator.rbs → results.rbs} +2 -2
- data/sig/quo/wrapped_query.rbs +2 -2
- data/sig/quo.rbs +8 -2
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 071f43a86ac28245731999ddf6f605c89802b71e3d51139d44263214ad25dbd5
|
|
4
|
+
data.tar.gz: 5eaf5d72a701679c599e5d07f60e8f1c7c9eb1f2e00a7f8ad1503270bc9cda64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed91b9a82feb1c8f165672ffa4d2b1212ea7e8780cb85980f82353eca7fdfb247e67101fb6156ceaad65be94f27406daa4a5832e4950ab0204f7a9dc326329a7
|
|
7
|
+
data.tar.gz: 503525140bd10ed28817e61dc0a2de8ef4e60ca32805427b72763a039982fd069a147ce8eb18d91896ac83202645a12092a8b186b530c97322f465baeeba687a
|
data/README.md
CHANGED
|
@@ -201,50 +201,44 @@ Specify extra options to enable pagination:
|
|
|
201
201
|
* `page`: the current page number to fetch
|
|
202
202
|
* `page_size`: the number of elements to fetch in the page
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
### `Quo::EagerQuery` & `Quo::LoadedQuery` objects
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
For example, the Query below executes the query inside on the first call and memoises the resulting data. Note
|
|
211
|
-
however that this then means that this Query is not reusuable to `merge` with other ActiveRecord queries. If it is
|
|
212
|
-
`compose`d with other Query objects then it will be seen as an array-like, and concatenated to whatever it is being
|
|
213
|
-
joined to.
|
|
206
|
+
`Quo::EagerQuery` is a subclass of `Quo::Query` which can be used to create query objects which are 'eager loaded' by
|
|
207
|
+
default. This is useful for encapsulating data that doesn't come from an ActiveRecord query or queries that
|
|
208
|
+
execute immediately. Subclass EasyQuery and override `collection` to return the data you want to encapsulate.
|
|
214
209
|
|
|
215
210
|
```ruby
|
|
216
|
-
class
|
|
217
|
-
def
|
|
218
|
-
|
|
211
|
+
class MyEagerQuery < Quo::EagerQuery
|
|
212
|
+
def collection
|
|
213
|
+
[1, 2, 3]
|
|
219
214
|
end
|
|
220
215
|
end
|
|
221
|
-
|
|
222
|
-
q = CachedTags.new(active: false)
|
|
216
|
+
q = MyEagerQuery.new
|
|
223
217
|
q.eager? # is it 'eager'? Yes it is!
|
|
224
|
-
q.count #
|
|
218
|
+
q.count # '3'
|
|
225
219
|
```
|
|
226
220
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
`Quo::EagerQuery` is a subclass of `Quo::Query` which takes a data value on instantiation and returns it on calls to `query`
|
|
221
|
+
Sometimes it is useful to create similar Queries without needing to create a explicit subclass of your own. For this
|
|
222
|
+
use `Quo::LoadedQuery`:
|
|
230
223
|
|
|
231
224
|
```ruby
|
|
232
|
-
q = Quo::
|
|
225
|
+
q = Quo::LoadedQuery.new([1, 2, 3])
|
|
233
226
|
q.eager? # is it 'eager'? Yes it is!
|
|
234
227
|
q.count # '3'
|
|
235
228
|
```
|
|
236
229
|
|
|
237
|
-
This is useful to create eager loaded Queries without needing to create a explicit subclass of your own.
|
|
238
|
-
|
|
239
230
|
`Quo::EagerQuery` also uses `total_count` option value as the specified 'total count', useful when the data is
|
|
240
231
|
actually just a page of the data and not the total count.
|
|
241
232
|
|
|
242
233
|
Example of an EagerQuery used to wrap a page of enumerable data:
|
|
243
234
|
|
|
244
235
|
```ruby
|
|
245
|
-
Quo::
|
|
236
|
+
Quo::LoadedQuery.new(my_data, total_count: 100, page: current_page)
|
|
246
237
|
```
|
|
247
238
|
|
|
239
|
+
If a loaded query is `compose`d with other Query objects then it will be seen as an array-like, and concatenated to whatever
|
|
240
|
+
results are returned from the other queries. An loaded or eager query will force all other queries to be eager loaded.
|
|
241
|
+
|
|
248
242
|
### Composition
|
|
249
243
|
|
|
250
244
|
Examples of composition of eager loaded queries
|
|
@@ -262,7 +256,7 @@ composed.last
|
|
|
262
256
|
composed.first
|
|
263
257
|
# => #<Tag id: ...>
|
|
264
258
|
|
|
265
|
-
Quo::
|
|
259
|
+
Quo::LoadedQuery.new([3, 4]).compose(Quo::LoadedQuery.new([1, 2])).last
|
|
266
260
|
# => 2
|
|
267
261
|
Quo::Query.compose([1, 2], [3, 4]).last
|
|
268
262
|
# => 4
|
|
@@ -292,7 +286,7 @@ maybe desirable.
|
|
|
292
286
|
|
|
293
287
|
The spec helper method `stub_query(query_class, {results: ..., with: ...})` can do this for you.
|
|
294
288
|
|
|
295
|
-
It stubs `.new` on the Query object and returns instances of `
|
|
289
|
+
It stubs `.new` on the Query object and returns instances of `LoadedQuery` instead with the given `results`.
|
|
296
290
|
The `with` option is passed to the Query object on initialisation and used when setting up the method stub on the
|
|
297
291
|
query class.
|
|
298
292
|
|
data/Steepfile
CHANGED
data/lib/quo/eager_query.rb
CHANGED
|
@@ -2,25 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module Quo
|
|
4
4
|
class EagerQuery < Quo::Query
|
|
5
|
-
def initialize(**options)
|
|
6
|
-
@collection = Array.wrap(options[:collection])
|
|
7
|
-
super(**options.except(:collection))
|
|
8
|
-
end
|
|
9
|
-
|
|
10
5
|
# Optionally return the `total_count` option if it has been set.
|
|
11
6
|
# This is useful when the total count is known and not equal to size
|
|
12
7
|
# of wrapped collection.
|
|
13
8
|
def count
|
|
14
9
|
options[:total_count] || super
|
|
15
10
|
end
|
|
16
|
-
alias_method :total_count, :count
|
|
17
|
-
alias_method :size, :count
|
|
18
11
|
|
|
19
12
|
# Is this query object paged? (when no total count)
|
|
20
13
|
def paged?
|
|
21
14
|
options[:total_count].nil? && current_page.present?
|
|
22
15
|
end
|
|
23
16
|
|
|
17
|
+
def collection
|
|
18
|
+
raise NotImplementedError, "EagerQuery objects must define a 'collection' method"
|
|
19
|
+
end
|
|
20
|
+
|
|
24
21
|
def query
|
|
25
22
|
preload_includes(collection) if options[:includes]
|
|
26
23
|
collection
|
|
@@ -36,7 +33,13 @@ module Quo
|
|
|
36
33
|
|
|
37
34
|
private
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
def underlying_query
|
|
37
|
+
unwrap_relation(query)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def unwrap_relation(query)
|
|
41
|
+
query.is_a?(Quo::Query) ? query.unwrap : query
|
|
42
|
+
end
|
|
40
43
|
|
|
41
44
|
def preload_includes(records, preload = nil)
|
|
42
45
|
::ActiveRecord::Associations::Preloader.new(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Quo
|
|
4
|
+
class LoadedQuery < Quo::EagerQuery
|
|
5
|
+
def initialize(collection, **options)
|
|
6
|
+
@collection = collection
|
|
7
|
+
super(**options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def copy(**options)
|
|
11
|
+
self.class.new(@collection, **@options.merge(options))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
attr_reader :collection
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/quo/merged_query.rb
CHANGED
|
@@ -2,24 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module Quo
|
|
4
4
|
class MergedQuery < Quo::Query
|
|
5
|
-
class << self
|
|
6
|
-
def call(**options)
|
|
7
|
-
build_from_options(**options).first
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call!(**options)
|
|
11
|
-
build_from_options(**options).first!
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def build_from_options(**options)
|
|
15
|
-
merged_query = options[:merged_query]
|
|
16
|
-
left = options[:left]
|
|
17
|
-
right = options[:right]
|
|
18
|
-
raise ArgumentError "MergedQuery needs the merged result and operands" unless merged_query && left && right
|
|
19
|
-
new(merged_query, left, right, **options)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
5
|
def initialize(merged_query, left, right, **options)
|
|
24
6
|
@merged_query = merged_query
|
|
25
7
|
@left = left
|
data/lib/quo/query.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Quo
|
|
|
28
28
|
def initialize(**options)
|
|
29
29
|
@options = options
|
|
30
30
|
@current_page = options[:page]&.to_i || options[:current_page]&.to_i
|
|
31
|
-
@page_size = options[:page_size]&.to_i || 20
|
|
31
|
+
@page_size = options[:page_size]&.to_i || Quo.configuration.default_page_size || 20
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Returns a active record query, or a Quo::Query instance
|
|
@@ -93,57 +93,60 @@ module Quo
|
|
|
93
93
|
delegate :model, :klass, to: :underlying_query
|
|
94
94
|
|
|
95
95
|
# Get first elements
|
|
96
|
-
def first(
|
|
96
|
+
def first(limit = nil)
|
|
97
97
|
if transform?
|
|
98
|
-
res = query_with_logging.first(
|
|
98
|
+
res = query_with_logging.first(limit)
|
|
99
99
|
if res.is_a? Array
|
|
100
|
-
res.map.with_index { |r, i| transformer
|
|
100
|
+
res.map.with_index { |r, i| transformer&.call(r, i) }
|
|
101
101
|
elsif !res.nil?
|
|
102
|
-
transformer
|
|
102
|
+
transformer&.call(query_with_logging.first(limit))
|
|
103
103
|
end
|
|
104
|
+
elsif limit
|
|
105
|
+
query_with_logging.first(limit)
|
|
104
106
|
else
|
|
105
|
-
|
|
107
|
+
# Array#first will not take nil as a limit
|
|
108
|
+
query_with_logging.first
|
|
106
109
|
end
|
|
107
110
|
end
|
|
108
111
|
|
|
109
|
-
def first!(
|
|
110
|
-
item = first(
|
|
112
|
+
def first!(limit = nil)
|
|
113
|
+
item = first(limit)
|
|
111
114
|
raise ActiveRecord::RecordNotFound, "No item could be found!" unless item
|
|
112
115
|
item
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
# Get last elements
|
|
116
|
-
def last(
|
|
119
|
+
def last(limit = nil)
|
|
117
120
|
if transform?
|
|
118
|
-
res = query_with_logging.last(
|
|
121
|
+
res = query_with_logging.last(limit)
|
|
119
122
|
if res.is_a? Array
|
|
120
|
-
res.map.with_index { |r, i| transformer
|
|
123
|
+
res.map.with_index { |r, i| transformer&.call(r, i) }
|
|
121
124
|
elsif !res.nil?
|
|
122
|
-
transformer
|
|
125
|
+
transformer&.call(res)
|
|
123
126
|
end
|
|
127
|
+
elsif limit
|
|
128
|
+
query_with_logging.last(limit)
|
|
124
129
|
else
|
|
125
|
-
query_with_logging.last
|
|
130
|
+
query_with_logging.last
|
|
126
131
|
end
|
|
127
132
|
end
|
|
128
133
|
|
|
129
134
|
# Convert to array
|
|
130
135
|
def to_a
|
|
131
136
|
arr = query_with_logging.to_a
|
|
132
|
-
transform? ? arr.map.with_index { |r, i| transformer
|
|
137
|
+
transform? ? arr.map.with_index { |r, i| transformer&.call(r, i) } : arr
|
|
133
138
|
end
|
|
134
139
|
|
|
135
|
-
# Convert to EagerQuery, and load all data
|
|
136
140
|
def to_eager(more_opts = {})
|
|
137
|
-
Quo::
|
|
141
|
+
Quo::LoadedQuery.new(to_a, **options.merge(more_opts))
|
|
138
142
|
end
|
|
139
143
|
alias_method :load, :to_eager
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Quo::Enumerator.new(self, transformer: transformer)
|
|
145
|
+
def results
|
|
146
|
+
Quo::Results.new(self, transformer: transformer)
|
|
144
147
|
end
|
|
145
148
|
|
|
146
|
-
# Some convenience methods for
|
|
149
|
+
# Some convenience methods for working with results
|
|
147
150
|
delegate :each,
|
|
148
151
|
:map,
|
|
149
152
|
:flat_map,
|
|
@@ -151,8 +154,9 @@ module Quo
|
|
|
151
154
|
:reject,
|
|
152
155
|
:filter,
|
|
153
156
|
:find,
|
|
157
|
+
:include?,
|
|
154
158
|
:each_with_object,
|
|
155
|
-
to: :
|
|
159
|
+
to: :results
|
|
156
160
|
|
|
157
161
|
# Set a block used to transform data after query fetching
|
|
158
162
|
def transform(&block)
|
|
@@ -226,7 +230,11 @@ module Quo
|
|
|
226
230
|
|
|
227
231
|
def offset
|
|
228
232
|
per_page = sanitised_page_size
|
|
229
|
-
page = current_page&.positive?
|
|
233
|
+
page = if current_page && current_page&.positive?
|
|
234
|
+
current_page
|
|
235
|
+
else
|
|
236
|
+
1
|
|
237
|
+
end
|
|
230
238
|
per_page * (page - 1)
|
|
231
239
|
end
|
|
232
240
|
|
|
@@ -238,7 +246,17 @@ module Quo
|
|
|
238
246
|
end
|
|
239
247
|
|
|
240
248
|
def sanitised_page_size
|
|
241
|
-
|
|
249
|
+
if page_size && page_size.positive?
|
|
250
|
+
given_size = page_size.to_i
|
|
251
|
+
max_page_size = Quo.configuration.max_page_size || 200
|
|
252
|
+
if given_size > max_page_size
|
|
253
|
+
max_page_size
|
|
254
|
+
else
|
|
255
|
+
given_size
|
|
256
|
+
end
|
|
257
|
+
else
|
|
258
|
+
Quo.configuration.default_page_size || 20
|
|
259
|
+
end
|
|
242
260
|
end
|
|
243
261
|
|
|
244
262
|
def query_with_logging
|
|
@@ -268,7 +286,7 @@ module Quo
|
|
|
268
286
|
end
|
|
269
287
|
|
|
270
288
|
def test_eager(rel)
|
|
271
|
-
rel.is_a?(Enumerable) && !test_relation(rel)
|
|
289
|
+
rel.is_a?(Quo::LoadedQuery) || (rel.is_a?(Enumerable) && !test_relation(rel))
|
|
272
290
|
end
|
|
273
291
|
|
|
274
292
|
def test_relation(rel)
|
data/lib/quo/query_composer.rb
CHANGED
|
@@ -5,6 +5,10 @@ module Quo
|
|
|
5
5
|
def initialize(left, right, joins = nil)
|
|
6
6
|
@left = left
|
|
7
7
|
@right = right
|
|
8
|
+
@unwrapped_left = unwrap_relation(left)
|
|
9
|
+
@unwrapped_right = unwrap_relation(right)
|
|
10
|
+
@left_relation = @unwrapped_left.is_a?(::ActiveRecord::Relation)
|
|
11
|
+
@right_relation = @unwrapped_right.is_a?(::ActiveRecord::Relation)
|
|
8
12
|
@joins = joins
|
|
9
13
|
end
|
|
10
14
|
|
|
@@ -19,22 +23,28 @@ module Quo
|
|
|
19
23
|
|
|
20
24
|
private
|
|
21
25
|
|
|
22
|
-
attr_reader :left, :right, :joins
|
|
26
|
+
attr_reader :left, :right, :joins, :unwrapped_left, :unwrapped_right
|
|
23
27
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
def left_relation?
|
|
29
|
+
@left_relation
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def right_relation?
|
|
33
|
+
@right_relation
|
|
34
|
+
end
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
elsif
|
|
35
|
-
|
|
36
|
-
elsif
|
|
37
|
-
|
|
36
|
+
def merge_left_and_right
|
|
37
|
+
# FIXME: Skipping type checks here, as not sure how to make this type check with RBS
|
|
38
|
+
__skip__ = if both_relations?
|
|
39
|
+
apply_joins(unwrapped_left, joins).merge(unwrapped_right)
|
|
40
|
+
elsif left_relation_right_enumerable?
|
|
41
|
+
unwrapped_left.to_a + unwrapped_right
|
|
42
|
+
elsif left_enumerable_right_relation? && unwrapped_left.respond_to?(:+)
|
|
43
|
+
unwrapped_left + unwrapped_right.to_a
|
|
44
|
+
elsif unwrapped_left.respond_to?(:+)
|
|
45
|
+
unwrapped_left + unwrapped_right
|
|
46
|
+
else
|
|
47
|
+
raise ArgumentError, "Cannot merge #{left.class} with #{right.class}"
|
|
38
48
|
end
|
|
39
49
|
end
|
|
40
50
|
|
|
@@ -49,16 +59,20 @@ module Quo
|
|
|
49
59
|
query.is_a?(Quo::Query) ? query.unwrap : query
|
|
50
60
|
end
|
|
51
61
|
|
|
52
|
-
def relation_type?(query)
|
|
53
|
-
if query.is_a?(::Quo::Query)
|
|
54
|
-
query.relation?
|
|
55
|
-
else
|
|
56
|
-
query.is_a?(::ActiveRecord::Relation)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
62
|
def apply_joins(left_rel, joins)
|
|
61
63
|
joins.present? ? left_rel.joins(joins) : left_rel
|
|
62
64
|
end
|
|
65
|
+
|
|
66
|
+
def both_relations?
|
|
67
|
+
left_relation? && right_relation?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def left_relation_right_enumerable?
|
|
71
|
+
left_relation? && !right_relation?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def left_enumerable_right_relation?
|
|
75
|
+
!left_relation? && right_relation?
|
|
76
|
+
end
|
|
63
77
|
end
|
|
64
78
|
end
|
|
@@ -4,7 +4,7 @@ require "forwardable"
|
|
|
4
4
|
require_relative "./utilities/callstack"
|
|
5
5
|
|
|
6
6
|
module Quo
|
|
7
|
-
class
|
|
7
|
+
class Results
|
|
8
8
|
extend Forwardable
|
|
9
9
|
include Quo::Utilities::Callstack
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ module Quo
|
|
|
28
28
|
grouped = unwrapped.group_by do |*block_args|
|
|
29
29
|
x = block_args.first
|
|
30
30
|
transformed = transformer ? transformer.call(x) : x
|
|
31
|
-
block.call(transformed, *block_args[1..])
|
|
31
|
+
block ? block.call(transformed, *(block_args[1..] || [])) : transformed
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
grouped.tap do |groups|
|
|
@@ -51,7 +51,8 @@ module Quo
|
|
|
51
51
|
end
|
|
52
52
|
else
|
|
53
53
|
raw = unwrapped.send(method, *args, **kwargs)
|
|
54
|
-
|
|
54
|
+
# FIXME: consider how to handle applying a transformer to a Enumerator...
|
|
55
|
+
return raw if raw.is_a?(Quo::Results) || raw.is_a?(::Enumerator)
|
|
55
56
|
transform_results(raw)
|
|
56
57
|
end
|
|
57
58
|
else
|
data/lib/quo/rspec/helpers.rb
CHANGED
|
@@ -9,10 +9,10 @@ module Quo
|
|
|
9
9
|
unless with.nil?
|
|
10
10
|
return(
|
|
11
11
|
allow(query_class).to receive(:new)
|
|
12
|
-
.with(with) { ::Quo::
|
|
12
|
+
.with(with) { ::Quo::LoadedQuery.new(results) }
|
|
13
13
|
)
|
|
14
14
|
end
|
|
15
|
-
allow(query_class).to receive(:new) { ::Quo::
|
|
15
|
+
allow(query_class).to receive(:new) { ::Quo::LoadedQuery.new(results) }
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -12,7 +12,7 @@ module Quo
|
|
|
12
12
|
stack = Kernel.caller.grep_v(exclude).map { |l| l.gsub(working_dir + "/", "") }
|
|
13
13
|
stack_to_display = stack[0..callstack_size]
|
|
14
14
|
message = "\n[Query stack]: -> #{stack_to_display&.join("\n &> ")}\n"
|
|
15
|
-
message += " (truncated to #{callstack_size} most recent)" if stack.size > callstack_size
|
|
15
|
+
message += " (truncated to #{callstack_size} most recent)" if callstack_size && stack.size > callstack_size
|
|
16
16
|
Quo.configuration.logger&.info(message)
|
|
17
17
|
end
|
|
18
18
|
end
|
data/lib/quo/utilities/wrap.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Quo
|
|
|
15
15
|
if query_rel_or_data.is_a? ActiveRecord::Relation
|
|
16
16
|
Quo::WrappedQuery.new(query_rel_or_data, **options)
|
|
17
17
|
else
|
|
18
|
-
Quo::
|
|
18
|
+
Quo::LoadedQuery.new(query_rel_or_data, **options)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
data/lib/quo/version.rb
CHANGED
data/lib/quo/wrapped_query.rb
CHANGED
|
@@ -2,27 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Quo
|
|
4
4
|
class WrappedQuery < Quo::Query
|
|
5
|
-
class << self
|
|
6
|
-
def call(**options)
|
|
7
|
-
build_from_options(**options).first
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def call!(**options)
|
|
11
|
-
build_from_options(**options).first!
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def build_from_options(**options)
|
|
15
|
-
query = options[:wrapped_query]
|
|
16
|
-
raise ArgumentError "WrappedQuery needs a scope" unless query
|
|
17
|
-
new(query, **options)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
5
|
def initialize(wrapped_query, **options)
|
|
22
6
|
@wrapped_query = wrapped_query
|
|
23
7
|
super(**options)
|
|
24
8
|
end
|
|
25
9
|
|
|
10
|
+
def copy(**options)
|
|
11
|
+
self.class.new(query, **@options.merge(options))
|
|
12
|
+
end
|
|
13
|
+
|
|
26
14
|
def query
|
|
27
15
|
@wrapped_query
|
|
28
16
|
end
|
data/lib/quo.rb
CHANGED
|
@@ -4,10 +4,11 @@ require_relative "quo/version"
|
|
|
4
4
|
require_relative "quo/railtie" if defined?(Rails)
|
|
5
5
|
require_relative "quo/query"
|
|
6
6
|
require_relative "quo/eager_query"
|
|
7
|
+
require_relative "quo/loaded_query"
|
|
7
8
|
require_relative "quo/merged_query"
|
|
8
9
|
require_relative "quo/wrapped_query"
|
|
9
10
|
require_relative "quo/query_composer"
|
|
10
|
-
require_relative "quo/
|
|
11
|
+
require_relative "quo/results"
|
|
11
12
|
|
|
12
13
|
module Quo
|
|
13
14
|
class << self
|
|
@@ -22,12 +23,18 @@ module Quo
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
class Configuration
|
|
25
|
-
attr_accessor :formatted_query_log,
|
|
26
|
+
attr_accessor :formatted_query_log,
|
|
27
|
+
:query_show_callstack_size,
|
|
28
|
+
:logger,
|
|
29
|
+
:max_page_size,
|
|
30
|
+
:default_page_size
|
|
26
31
|
|
|
27
32
|
def initialize
|
|
28
33
|
@formatted_query_log = true
|
|
29
34
|
@query_show_callstack_size = 10
|
|
30
35
|
@logger = nil
|
|
36
|
+
@max_page_size = 200
|
|
37
|
+
@default_page_size = 20
|
|
31
38
|
end
|
|
32
39
|
end
|
|
33
40
|
end
|
data/sig/quo/eager_query.rbs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
module Quo
|
|
2
2
|
class EagerQuery < Quo::Query
|
|
3
|
-
def
|
|
4
|
-
def query: () ->
|
|
3
|
+
def collection: () -> loadedQueryOrEnumerable
|
|
4
|
+
def query: () -> loadedQueryOrEnumerable
|
|
5
|
+
|
|
5
6
|
def relation?: () -> false
|
|
6
7
|
def eager?: () -> true
|
|
7
|
-
attr_reader collection: enumerable
|
|
8
8
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def preload_includes: (untyped records, ?untyped? preload) -> untyped
|
|
12
|
+
def underlying_query: () -> enumerable
|
|
13
|
+
def unwrap_relation: (loadedQueryOrEnumerable collection) -> enumerable
|
|
12
14
|
end
|
|
13
15
|
end
|
data/sig/quo/merged_query.rbs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module Quo
|
|
2
2
|
class MergedQuery < Quo::Query
|
|
3
|
+
def self.build_from_options: (queryOptions) -> MergedQuery
|
|
4
|
+
|
|
3
5
|
def initialize: (relOrEnumerable merged, composable left, composable right, **untyped options) -> void
|
|
4
6
|
|
|
5
7
|
@merged_query: relOrEnumerable
|
|
6
8
|
|
|
7
|
-
def query: () ->
|
|
9
|
+
def query: () -> relOrEnumerable
|
|
8
10
|
|
|
9
11
|
def to_s: () -> ::String
|
|
10
12
|
|
data/sig/quo/query.rbs
CHANGED
|
@@ -40,13 +40,13 @@ module Quo
|
|
|
40
40
|
|
|
41
41
|
alias size count
|
|
42
42
|
def page_count: () -> Integer
|
|
43
|
-
def first: (
|
|
44
|
-
def first!: (
|
|
45
|
-
def last: (
|
|
43
|
+
def first: (?Integer? limit) -> untyped
|
|
44
|
+
def first!: (?Integer? limit) -> untyped
|
|
45
|
+
def last: (?Integer? limit) -> untyped
|
|
46
46
|
def to_a: () -> Array[untyped]
|
|
47
47
|
def to_eager: (?::Hash[untyped, untyped] more_opts) -> Quo::EagerQuery
|
|
48
48
|
alias load to_eager
|
|
49
|
-
def
|
|
49
|
+
def results: () -> Quo::Results
|
|
50
50
|
|
|
51
51
|
# Set a block used to transform data after query fetching
|
|
52
52
|
def transform: () ?{ () -> untyped } -> self
|
|
@@ -70,7 +70,7 @@ module Quo
|
|
|
70
70
|
def formatted_queries?: () -> bool
|
|
71
71
|
def trim_query: (String sql) -> String
|
|
72
72
|
def format_query: (String sql_str) -> String
|
|
73
|
-
def transformer: () -> (nil | ^(untyped) -> untyped)
|
|
73
|
+
def transformer: () -> (nil | ^(untyped, ?Integer) -> untyped)
|
|
74
74
|
def offset: () -> Integer
|
|
75
75
|
def configured_query: () -> ActiveRecord::Relation
|
|
76
76
|
def sanitised_page_size: () -> Integer
|
data/sig/quo/query_composer.rbs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module Quo
|
|
2
2
|
class QueryComposer
|
|
3
|
+
@left_relation: bool
|
|
4
|
+
@right_relation: bool
|
|
5
|
+
|
|
3
6
|
def initialize: (composable left, composable right, ?untyped? joins) -> void
|
|
4
7
|
def compose: () -> Quo::MergedQuery
|
|
5
8
|
|
|
@@ -9,10 +12,21 @@ module Quo
|
|
|
9
12
|
attr_reader right: composable
|
|
10
13
|
attr_reader joins: untyped
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
attr_reader unwrapped_left: relOrEnumerable
|
|
16
|
+
attr_reader unwrapped_right: relOrEnumerable
|
|
17
|
+
|
|
18
|
+
def left_relation?: -> bool
|
|
19
|
+
|
|
20
|
+
def merge_left_and_right: () -> relOrEnumerable
|
|
13
21
|
def merged_options: () -> ::Hash[untyped, untyped]
|
|
22
|
+
|
|
23
|
+
def right_relation?: -> bool
|
|
24
|
+
|
|
14
25
|
def unwrap_relation: (composable) -> relOrEnumerable
|
|
15
|
-
def relation_type?: (
|
|
26
|
+
def relation_type?: (relOrEnumerable) -> bool
|
|
16
27
|
def apply_joins: (ActiveRecord::Relation left_rel, untyped joins) -> ActiveRecord::Relation
|
|
28
|
+
def both_relations?: () -> bool
|
|
29
|
+
def left_relation_right_enumerable?: () -> bool
|
|
30
|
+
def left_enumerable_right_relation?: () -> bool
|
|
17
31
|
end
|
|
18
32
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Quo
|
|
2
|
-
class
|
|
2
|
+
class Results
|
|
3
3
|
extend Forwardable
|
|
4
4
|
|
|
5
5
|
include Quo::Utilities::Callstack
|
|
@@ -8,7 +8,7 @@ module Quo
|
|
|
8
8
|
|
|
9
9
|
@query: Quo::Query
|
|
10
10
|
|
|
11
|
-
def group_by: -> Hash[untyped, Array[untyped]]
|
|
11
|
+
def group_by: () { (untyped, *untyped) -> untyped } -> Hash[untyped, Array[untyped]]
|
|
12
12
|
|
|
13
13
|
def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
|
|
14
14
|
|
data/sig/quo/wrapped_query.rbs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module Quo
|
|
2
2
|
class WrappedQuery < Quo::Query
|
|
3
|
-
@wrapped_query: ActiveRecord::Relation
|
|
4
|
-
|
|
5
3
|
def self.build_from_options: (**untyped options) -> WrappedQuery
|
|
6
4
|
|
|
5
|
+
@wrapped_query: ActiveRecord::Relation
|
|
6
|
+
|
|
7
7
|
def initialize: (ActiveRecord::Relation query, **untyped options) -> void
|
|
8
8
|
|
|
9
9
|
def query: () -> ActiveRecord::Relation
|
data/sig/quo.rbs
CHANGED
|
@@ -11,11 +11,15 @@ module Quo
|
|
|
11
11
|
|
|
12
12
|
type query = Quo::Query
|
|
13
13
|
type queryOrRel = query | ActiveRecord::Relation
|
|
14
|
-
|
|
15
|
-
type enumerable = Array[untyped] | Set[untyped]
|
|
14
|
+
type enumerable = Object & Enumerable[untyped]
|
|
16
15
|
type relOrEnumerable = ActiveRecord::Relation | enumerable
|
|
16
|
+
type loadedQueryOrEnumerable = LoadedQuery | EagerQuery | enumerable
|
|
17
17
|
type composable = query | relOrEnumerable
|
|
18
18
|
|
|
19
|
+
# TODO: how can we do the known options, eg `page` and then allow anything else?
|
|
20
|
+
# Maybe we should separate out the known options from the unknown options
|
|
21
|
+
type queryOptions = Hash[Symbol, untyped]
|
|
22
|
+
|
|
19
23
|
interface _Logger
|
|
20
24
|
def info: (String) -> void
|
|
21
25
|
def error: (String) -> void
|
|
@@ -26,6 +30,8 @@ module Quo
|
|
|
26
30
|
attr_accessor formatted_query_log: bool?
|
|
27
31
|
attr_accessor query_show_callstack_size: Integer?
|
|
28
32
|
attr_accessor logger: _Logger?
|
|
33
|
+
attr_accessor max_page_size: Integer?
|
|
34
|
+
attr_accessor default_page_size: Integer?
|
|
29
35
|
|
|
30
36
|
def initialize: () -> void
|
|
31
37
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: quo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stephen Ierodiaconou
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-12-
|
|
11
|
+
date: 2022-12-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -66,11 +66,12 @@ files:
|
|
|
66
66
|
- Steepfile
|
|
67
67
|
- lib/quo.rb
|
|
68
68
|
- lib/quo/eager_query.rb
|
|
69
|
-
- lib/quo/
|
|
69
|
+
- lib/quo/loaded_query.rb
|
|
70
70
|
- lib/quo/merged_query.rb
|
|
71
71
|
- lib/quo/query.rb
|
|
72
72
|
- lib/quo/query_composer.rb
|
|
73
73
|
- lib/quo/railtie.rb
|
|
74
|
+
- lib/quo/results.rb
|
|
74
75
|
- lib/quo/rspec/helpers.rb
|
|
75
76
|
- lib/quo/utilities/callstack.rb
|
|
76
77
|
- lib/quo/utilities/compose.rb
|
|
@@ -82,10 +83,11 @@ files:
|
|
|
82
83
|
- rbs_collection.yaml
|
|
83
84
|
- sig/quo.rbs
|
|
84
85
|
- sig/quo/eager_query.rbs
|
|
85
|
-
- sig/quo/
|
|
86
|
+
- sig/quo/loaded_query.rbs
|
|
86
87
|
- sig/quo/merged_query.rbs
|
|
87
88
|
- sig/quo/query.rbs
|
|
88
89
|
- sig/quo/query_composer.rbs
|
|
90
|
+
- sig/quo/results.rbs
|
|
89
91
|
- sig/quo/utilities/callstack.rbs
|
|
90
92
|
- sig/quo/utilities/compose.rbs
|
|
91
93
|
- sig/quo/utilities/sanitize.rbs
|