quo 0.6.0 → 1.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.standard.yml +4 -1
- data/Appraisals +11 -0
- data/CHANGELOG.md +78 -0
- data/Gemfile +6 -4
- data/LICENSE.txt +1 -1
- data/README.md +37 -36
- data/Steepfile +0 -2
- data/gemfiles/rails_7.0.gemfile +15 -0
- data/gemfiles/rails_7.1.gemfile +15 -0
- data/gemfiles/rails_7.2.gemfile +15 -0
- data/lib/quo/collection_backed_query.rb +87 -0
- data/lib/quo/collection_results.rb +44 -0
- data/lib/quo/composed_query.rb +168 -0
- data/lib/quo/engine.rb +11 -0
- data/lib/quo/minitest/helpers.rb +41 -0
- data/lib/quo/preloadable.rb +46 -0
- data/lib/quo/query.rb +97 -214
- data/lib/quo/relation_backed_query.rb +177 -0
- data/lib/quo/relation_results.rb +58 -0
- data/lib/quo/results.rb +48 -44
- data/lib/quo/rspec/helpers.rb +31 -9
- data/lib/quo/testing/collection_backed_fake.rb +29 -0
- data/lib/quo/testing/relation_backed_fake.rb +52 -0
- data/lib/quo/version.rb +3 -1
- data/lib/quo.rb +22 -30
- data/rbs_collection.yaml +0 -2
- data/sig/generated/quo/collection_backed_query.rbs +39 -0
- data/sig/generated/quo/collection_results.rbs +30 -0
- data/sig/generated/quo/composed_query.rbs +83 -0
- data/sig/generated/quo/engine.rbs +6 -0
- data/sig/generated/quo/preloadable.rbs +29 -0
- data/sig/generated/quo/query.rbs +98 -0
- data/sig/generated/quo/relation_backed_query.rbs +90 -0
- data/sig/generated/quo/relation_results.rbs +38 -0
- data/sig/generated/quo/results.rbs +39 -0
- data/sig/generated/quo/version.rbs +5 -0
- data/sig/generated/quo.rbs +9 -0
- metadata +67 -30
- data/lib/quo/eager_query.rb +0 -51
- data/lib/quo/loaded_query.rb +0 -18
- data/lib/quo/merged_query.rb +0 -36
- data/lib/quo/query_composer.rb +0 -78
- data/lib/quo/railtie.rb +0 -7
- data/lib/quo/utilities/callstack.rb +0 -21
- data/lib/quo/utilities/compose.rb +0 -18
- data/lib/quo/utilities/sanitize.rb +0 -19
- data/lib/quo/utilities/wrap.rb +0 -23
- data/lib/quo/wrapped_query.rb +0 -18
- data/sig/quo/eager_query.rbs +0 -15
- data/sig/quo/loaded_query.rbs +0 -7
- data/sig/quo/merged_query.rbs +0 -19
- data/sig/quo/query.rbs +0 -83
- data/sig/quo/query_composer.rbs +0 -32
- data/sig/quo/results.rbs +0 -22
- data/sig/quo/utilities/callstack.rbs +0 -7
- data/sig/quo/utilities/compose.rbs +0 -8
- data/sig/quo/utilities/sanitize.rbs +0 -9
- data/sig/quo/utilities/wrap.rbs +0 -11
- data/sig/quo/wrapped_query.rbs +0 -11
- data/sig/quo.rbs +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dcdc5faff0797696ddb8db26baebe37f388a020943f7764e4857aaab8ef4c76
|
4
|
+
data.tar.gz: 550da1aaaa2bc4d24d82f3e7f78908f22ccd28913b8d9c6358cbc9bbc6ee8fa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69a94d4d6c4844e6b27e409a662dd76a9ca0dee26fd229a2e861ad377ef2a247ec8499633565d119aa5bc6adeca8930015f33b6ea69e4673d9609f906a1029e5
|
7
|
+
data.tar.gz: d511d04d2212029428e74b2771407e7a3adf566c193a3075fffee3d1c3de7d3e9c8aaca8d39330198264be2ead5df7e574ebd90581600d27d5576eef8718cd0b
|
data/.standard.yml
CHANGED
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,83 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
|
4
|
+
## [1.0.0.rc1] - Unreleased
|
5
|
+
|
6
|
+
### Breaking Changes
|
7
|
+
|
8
|
+
Nearly everything has had changes. Porting will require some effort.
|
9
|
+
|
10
|
+
- Quo now depends on `literal`, meaning attributes (options) to queries are typed and explicit
|
11
|
+
- Composing query objects now allows you to compose query classes rather than just instances of query objects
|
12
|
+
- `MergedQuery`, `EagerQuery` & `LoadedQuery` have been removed
|
13
|
+
- `Query` is now an abstract base class for `RelationBackedQuery` and `CollectionBackedQuery`
|
14
|
+
- The API of `Query` has been reduced/simplified significantly
|
15
|
+
- `Query` classes only build queries, to actually execute/take actions on them you need to call `#results` and get a `Results` object
|
16
|
+
- `preload`ing behaviour is now a separate concern from `Query` and is handled by `Preloadable` module.
|
17
|
+
- Drop support for Ruby <= 3.1 and Rails < 7.0
|
18
|
+
- Gem is now a Rails engine and relies on autoloading
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
|
22
|
+
- Update docs, dependencies, and tests
|
23
|
+
- Use appraisals for testing
|
24
|
+
|
25
|
+
### Added
|
26
|
+
|
27
|
+
- Helpers `stub_query` and `mock_query` for Minitest
|
28
|
+
|
29
|
+
## [0.5.0] - 2022-12-23
|
30
|
+
|
31
|
+
### Changed
|
32
|
+
|
33
|
+
- Merged and Wrapped queries should not have factory methods as they are not meant to be constructed directly
|
34
|
+
- Create new LoadedQuery which separates the concern of "preloaded" Query from EagerQuery which represents a query which is loaded and memoized
|
35
|
+
|
36
|
+
## [0.4.0] - 2022-12-23
|
37
|
+
|
38
|
+
### Changed
|
39
|
+
|
40
|
+
- Some redundant nil checks (either safe navigation operator or conditionals) to make type check pass
|
41
|
+
- Fix for type of transform method which takes optional index as second arg
|
42
|
+
- group_by can take a block
|
43
|
+
- Change last and first methods to just take a limit value
|
44
|
+
- Add new configuration options for page size limit and default and fix typing for enumerable
|
45
|
+
- Rename Enumerator to Results and Query#enumerator to #results
|
46
|
+
- Change EagerQuery initializer to take collection as positional param
|
47
|
+
|
48
|
+
## [0.3.1] - 2022-12-22
|
49
|
+
|
50
|
+
### Changed
|
51
|
+
|
52
|
+
- Convenience methods on Query
|
53
|
+
- Implement group_by on enumerator to transform values in resulting groups
|
54
|
+
- Add WrappedQuery instead of Query taking a scope param
|
55
|
+
- Change `initialize` method of MergedQuery
|
56
|
+
|
57
|
+
## [0.3.0] - 2022-12-20
|
58
|
+
|
59
|
+
### Changed
|
60
|
+
|
61
|
+
- Make `joins` on compose a kwarg
|
62
|
+
|
63
|
+
## [0.2.0] - 2022-12-20
|
64
|
+
|
65
|
+
### Added
|
66
|
+
|
67
|
+
- Railtie for rake task
|
68
|
+
- Rake task which hackily looks for qo in the app and displays a list
|
69
|
+
- Prepare to add RBS types
|
70
|
+
|
71
|
+
### Changed
|
72
|
+
|
73
|
+
- Gem deps
|
74
|
+
- Query interface
|
75
|
+
|
76
|
+
### Added
|
77
|
+
|
78
|
+
- Test suite and dummy rails app
|
79
|
+
- Add Enumerator
|
80
|
+
|
3
81
|
## [0.1.0] - 2022-11-18
|
4
82
|
|
5
83
|
- Initial release
|
data/Gemfile
CHANGED
@@ -5,16 +5,18 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in quo.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem "rails", "~> 7.2"
|
9
|
+
|
8
10
|
group :development, :test do
|
9
11
|
gem "sqlite3"
|
10
12
|
|
11
|
-
gem "rails", ">= 6", "< 8"
|
12
|
-
|
13
13
|
gem "rake", "~> 13.0"
|
14
14
|
|
15
15
|
gem "minitest", "~> 5.0"
|
16
16
|
|
17
|
-
gem "standard",
|
17
|
+
gem "standard", require: false
|
18
|
+
|
19
|
+
gem "steep", require: false
|
18
20
|
|
19
|
-
gem "
|
21
|
+
gem "rbs-inline", "~> 0.8.0", require: false
|
20
22
|
end
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2022 Stephen Ierodiaconou
|
3
|
+
Copyright (c) 2022-2024 Stephen Ierodiaconou
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
# Quo
|
1
|
+
# 'Quo' query objects for ActiveRecord
|
2
|
+
|
3
|
+
> Note: these docs are for pre-V1 and need updating. I'm working on it!
|
2
4
|
|
3
5
|
Quo query objects can help you abstract ActiveRecord DB queries into reusable and composable objects with a chainable
|
4
6
|
interface.
|
@@ -13,10 +15,12 @@ The core implementation provides the following functionality:
|
|
13
15
|
* provides a number of utility methods that operate on the underlying collection (eg `exists?`)
|
14
16
|
* provides a `+` (`compose`) method which merges two query object instances (see section below for details!)
|
15
17
|
* can specify a mapping or transform method to `transform` to perform on results
|
16
|
-
* in development outputs the callstack that led to the execution of the query
|
17
18
|
* acts as a callable which executes the underlying query with `.first`
|
18
19
|
* can return an `Enumerable` of results
|
19
20
|
|
21
|
+
|
22
|
+
`Quo::Query` subclasses are the builders, they retain configuration of the queries, and prepare the underlying query or data collections. Query objects then return `Quo::Results` which take the built queries and then take action on them, such as to fetch data or to count records.
|
23
|
+
|
20
24
|
## Creating a Quo query object
|
21
25
|
|
22
26
|
The query object must inherit from `Quo::Query` and provide an implementation for the `query` method.
|
@@ -24,7 +28,7 @@ The query object must inherit from `Quo::Query` and provide an implementation fo
|
|
24
28
|
The `query` method must return either:
|
25
29
|
|
26
30
|
- an `ActiveRecord::Relation`
|
27
|
-
- an
|
31
|
+
- an Enumerable (like a 'collection backed' query)
|
28
32
|
- or another `Quo::Query` instance.
|
29
33
|
|
30
34
|
Remember that the query object should be useful in composition with other query objects. Thus it should not directly
|
@@ -81,7 +85,7 @@ This allows you to compose together Query objects which return relations which a
|
|
81
85
|
them correctly with the appropriate joins. Note with the alias you cant neatly specify optional parameters for joins
|
82
86
|
on relations.
|
83
87
|
|
84
|
-
Note that the compose process creates a new query object instance, which is a instance of a `Quo::
|
88
|
+
Note that the compose process creates a new query object instance, which is a instance of a `Quo::ComposedQuery`.
|
85
89
|
|
86
90
|
Consider the following cases:
|
87
91
|
|
@@ -95,7 +99,7 @@ wrapped around a new 'composed' `ActiveRecords::Relation`.
|
|
95
99
|
In case (2) the query object with a `ActiveRecords::Relation` inside is executed, and the result is then concatenated
|
96
100
|
to the array-like with `+`
|
97
101
|
|
98
|
-
In case (3) the values contained
|
102
|
+
In case (3) the values contained are concatenated with `+`
|
99
103
|
|
100
104
|
*Note that*
|
101
105
|
|
@@ -106,7 +110,7 @@ query object or and `ActiveRecord::Relation`. However `Quo::Query.compose(left,
|
|
106
110
|
### Examples
|
107
111
|
|
108
112
|
```ruby
|
109
|
-
class CompanyToBeApproved < Quo::
|
113
|
+
class CompanyToBeApproved < Quo::RelationBackedQuery
|
110
114
|
def query
|
111
115
|
Registration
|
112
116
|
.left_joins(:approval)
|
@@ -114,7 +118,7 @@ class CompanyToBeApproved < Quo::Query
|
|
114
118
|
end
|
115
119
|
end
|
116
120
|
|
117
|
-
class CompanyInUsState < Quo::
|
121
|
+
class CompanyInUsState < Quo::RelationBackedQuery
|
118
122
|
def query
|
119
123
|
Registration
|
120
124
|
.joins(company: :address)
|
@@ -144,7 +148,7 @@ It is also possible to compose with an `ActiveRecord::Relation`. This can be use
|
|
144
148
|
build up the `query` relation. For example:
|
145
149
|
|
146
150
|
```ruby
|
147
|
-
class RegistrationToBeApproved < Quo::
|
151
|
+
class RegistrationToBeApproved < Quo::RelationBackedQuery
|
148
152
|
def query
|
149
153
|
done = Registration.where(step: "complete")
|
150
154
|
approved = CompanyToBeApproved.new
|
@@ -161,13 +165,13 @@ query = RegistrationToBeApproved.new + Registration.where(blocked: false)
|
|
161
165
|
Also you can use joins:
|
162
166
|
|
163
167
|
```ruby
|
164
|
-
class TagByName < Quo::
|
168
|
+
class TagByName < Quo::RelationBackedQuery
|
165
169
|
def query
|
166
170
|
Tag.where(name: options[:name])
|
167
171
|
end
|
168
172
|
end
|
169
173
|
|
170
|
-
class CategoryByName < Quo::
|
174
|
+
class CategoryByName < Quo::RelationBackedQuery
|
171
175
|
def query
|
172
176
|
Category.where(name: options[:name])
|
173
177
|
end
|
@@ -180,18 +184,18 @@ tags.compose(for_category, :category) # perform join on tag association `categor
|
|
180
184
|
# equivalent to Tag.joins(:category).where(name: "Intel").where(categories: {name: "CPUs"})
|
181
185
|
```
|
182
186
|
|
183
|
-
|
187
|
+
Collection backed queries can also be composed (see below sections for more details).
|
184
188
|
|
185
|
-
### Quo::
|
189
|
+
### Quo::ComposedQuery
|
186
190
|
|
187
|
-
The new instance of `Quo::
|
191
|
+
The new instance of `Quo::ComposedQuery` from a compose process, retains references to the original entities that were
|
188
192
|
composed. These are then used to create a more useful output from `to_s`, so that it is easier to understand what the
|
189
193
|
merged query is actually made up of:
|
190
194
|
|
191
195
|
```ruby
|
192
196
|
q = FooQuery.new + BarQuery.new
|
193
197
|
puts q
|
194
|
-
# > "Quo::
|
198
|
+
# > "Quo::ComposedQuery[FooQuery, BarQuery]"
|
195
199
|
```
|
196
200
|
|
197
201
|
## Query Objects & Pagination
|
@@ -201,39 +205,40 @@ Specify extra options to enable pagination:
|
|
201
205
|
* `page`: the current page number to fetch
|
202
206
|
* `page_size`: the number of elements to fetch in the page
|
203
207
|
|
204
|
-
### `Quo::
|
208
|
+
### `Quo::CollectionBackedQuery` & `Quo::CollectionBackedQuery` objects
|
205
209
|
|
206
|
-
`Quo::
|
207
|
-
|
208
|
-
execute immediately. Subclass
|
210
|
+
`Quo::CollectionBackedQuery` is a subclass of `Quo::Query` which can be used to create query objects which are backed
|
211
|
+
by a collection (ie an enumerable such as an Array). This is useful for encapsulating data that doesn't come from an
|
212
|
+
ActiveRecord query or queries that execute immediately. Subclass this and override `collection` to return the data you
|
213
|
+
want to encapsulate.
|
209
214
|
|
210
215
|
```ruby
|
211
|
-
class
|
216
|
+
class MyCollectionBackedQuery < Quo::CollectionBackedQuery
|
212
217
|
def collection
|
213
218
|
[1, 2, 3]
|
214
219
|
end
|
215
220
|
end
|
216
|
-
q =
|
217
|
-
q.
|
221
|
+
q = MyCollectionBackedQuery.new
|
222
|
+
q.collection? # is it a collection under the hood? Yes it is!
|
218
223
|
q.count # '3'
|
219
224
|
```
|
220
225
|
|
221
226
|
Sometimes it is useful to create similar Queries without needing to create a explicit subclass of your own. For this
|
222
|
-
use `Quo::
|
227
|
+
use `Quo::CollectionBackedQuery`:
|
223
228
|
|
224
229
|
```ruby
|
225
|
-
q = Quo::
|
226
|
-
q.
|
230
|
+
q = Quo::CollectionBackedQuery.wrap([1, 2, 3])
|
231
|
+
q.collection? # true
|
227
232
|
q.count # '3'
|
228
233
|
```
|
229
234
|
|
230
|
-
`Quo::
|
235
|
+
`Quo::CollectionBackedQuery` also uses `total_count` option value as the specified 'total count', useful when the data is
|
231
236
|
actually just a page of the data and not the total count.
|
232
237
|
|
233
|
-
Example of an
|
238
|
+
Example of an CollectionBackedQuery used to wrap a page of enumerable data:
|
234
239
|
|
235
240
|
```ruby
|
236
|
-
Quo::
|
241
|
+
Quo::CollectionBackedQuery.wrap(my_data, total_count: 100, page: current_page)
|
237
242
|
```
|
238
243
|
|
239
244
|
If a loaded query is `compose`d with other Query objects then it will be seen as an array-like, and concatenated to whatever
|
@@ -244,7 +249,7 @@ results are returned from the other queries. An loaded or eager query will force
|
|
244
249
|
Examples of composition of eager loaded queries
|
245
250
|
|
246
251
|
```ruby
|
247
|
-
class CachedTags < Quo::
|
252
|
+
class CachedTags < Quo::RelationBackedQuery
|
248
253
|
def query
|
249
254
|
@tags ||= Tag.where(active: true).to_a
|
250
255
|
end
|
@@ -256,7 +261,7 @@ composed.last
|
|
256
261
|
composed.first
|
257
262
|
# => #<Tag id: ...>
|
258
263
|
|
259
|
-
Quo::
|
264
|
+
Quo::CollectionBackedQuery.new([3, 4]).compose(Quo::CollectionBackedQuery.new([1, 2])).last
|
260
265
|
# => 2
|
261
266
|
Quo::Query.compose([1, 2], [3, 4]).last
|
262
267
|
# => 4
|
@@ -286,7 +291,7 @@ maybe desirable.
|
|
286
291
|
|
287
292
|
The spec helper method `stub_query(query_class, {results: ..., with: ...})` can do this for you.
|
288
293
|
|
289
|
-
It stubs `.new` on the Query object and returns instances of `
|
294
|
+
It stubs `.new` on the Query object and returns instances of `CollectionBackedQuery` instead with the given `results`.
|
290
295
|
The `with` option is passed to the Query object on initialisation and used when setting up the method stub on the
|
291
296
|
query class.
|
292
297
|
|
@@ -299,7 +304,7 @@ expect(TagQuery.new(name: "Something").first).to eql t1
|
|
299
304
|
|
300
305
|
*Note that*
|
301
306
|
|
302
|
-
This returns an instance of
|
307
|
+
This returns an instance of CollectionBackedQuery, so will not work for cases were the actual type of the query instance is
|
303
308
|
important or where you are doing a composition of queries backed by relations!
|
304
309
|
|
305
310
|
If `compose` will be used then `Quo::Query.compose` needs to be stubbed. Something might be possible to make this
|
@@ -338,11 +343,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/steveg
|
|
338
343
|
|
339
344
|
## Inspired by `rectify`
|
340
345
|
|
341
|
-
Note this implementation is
|
342
|
-
|
343
|
-
See https://github.com/andypike/rectify#query-objects for more information.
|
344
|
-
|
345
|
-
Thanks to Andy Pike for the inspiration.
|
346
|
+
Note this implementation is inspired by the `Rectify` gem; https://github.com/andypike/rectify. Thanks to Andy Pike for the inspiration.
|
346
347
|
|
347
348
|
## License
|
348
349
|
|
data/Steepfile
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rails", "~> 7.0"
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
gem "sqlite3"
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
gem "minitest", "~> 5.0"
|
11
|
+
gem "standard"
|
12
|
+
gem "steep"
|
13
|
+
end
|
14
|
+
|
15
|
+
gemspec path: "../"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rails", "~> 7.1"
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
gem "sqlite3"
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
gem "minitest", "~> 5.0"
|
11
|
+
gem "standard"
|
12
|
+
gem "steep"
|
13
|
+
end
|
14
|
+
|
15
|
+
gemspec path: "../"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rails", "~> 7.2"
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
gem "sqlite3"
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
gem "minitest", "~> 5.0"
|
11
|
+
gem "standard"
|
12
|
+
gem "steep"
|
13
|
+
end
|
14
|
+
|
15
|
+
gemspec path: "../"
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Quo
|
6
|
+
class CollectionBackedQuery < Query
|
7
|
+
prop :total_count, _Nilable(Integer), reader: false
|
8
|
+
|
9
|
+
# Wrap an enumerable collection or a block that returns an enumerable collection
|
10
|
+
# @rbs data: untyped, props: Symbol => untyped, block: () -> untyped
|
11
|
+
# @rbs return: Quo::CollectionBackedQuery
|
12
|
+
def self.wrap(data = nil, props: {}, &block)
|
13
|
+
klass = Class.new(self) do
|
14
|
+
props.each do |name, property|
|
15
|
+
if property.is_a?(Literal::Property)
|
16
|
+
prop name, property.type, property.kind, reader: property.reader, writer: property.writer, default: property.default
|
17
|
+
else
|
18
|
+
prop name, property
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
if block
|
23
|
+
klass.define_method(:collection, &block)
|
24
|
+
elsif data
|
25
|
+
klass.define_method(:collection) { data }
|
26
|
+
else
|
27
|
+
raise ArgumentError, "either a query or a block must be provided"
|
28
|
+
end
|
29
|
+
# klass.set_temporary_name = "quo::Wrapper" # Ruby 3.3+
|
30
|
+
klass
|
31
|
+
end
|
32
|
+
|
33
|
+
# @rbs return: Object & Enumerable[untyped]
|
34
|
+
def collection
|
35
|
+
raise NotImplementedError, "Collection backed query objects must define a 'collection' method"
|
36
|
+
end
|
37
|
+
|
38
|
+
# The default implementation of `query` just calls `collection`, however you can also
|
39
|
+
# override this method to return an ActiveRecord::Relation or any other query-like object as usual in a Query object.
|
40
|
+
# @rbs return: Object & Enumerable[untyped]
|
41
|
+
def query
|
42
|
+
collection
|
43
|
+
end
|
44
|
+
|
45
|
+
def results
|
46
|
+
Quo::CollectionResults.new(self, transformer: transformer, total_count: @total_count)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @rbs override
|
50
|
+
def relation?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @rbs override
|
55
|
+
def collection?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
# @rbs override
|
60
|
+
def to_collection
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def validated_query
|
67
|
+
query
|
68
|
+
end
|
69
|
+
|
70
|
+
# @rbs return: Object & Enumerable[untyped]
|
71
|
+
def underlying_query
|
72
|
+
validated_query
|
73
|
+
end
|
74
|
+
|
75
|
+
# The configured query is the underlying query with paging
|
76
|
+
def configured_query #: Object & Enumerable[untyped]
|
77
|
+
q = underlying_query
|
78
|
+
return q unless paged?
|
79
|
+
|
80
|
+
if q.respond_to?(:[])
|
81
|
+
q[offset, sanitised_page_size]
|
82
|
+
else
|
83
|
+
q
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Quo
|
6
|
+
class CollectionResults < Results
|
7
|
+
# @rbs override
|
8
|
+
def initialize(query, transformer: nil, total_count: nil)
|
9
|
+
raise ArgumentError, "Query must be a CollectionBackedQuery" unless query.is_a?(Quo::CollectionBackedQuery)
|
10
|
+
@total_count = total_count
|
11
|
+
@query = query
|
12
|
+
@configured_query = query.unwrap
|
13
|
+
@transformer = transformer
|
14
|
+
end
|
15
|
+
|
16
|
+
# Are there any results for this query?
|
17
|
+
def exists? #: bool
|
18
|
+
@configured_query.present?
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty? #: bool
|
22
|
+
!exists?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Gets the count of all results ignoring the current page and page size (if set).
|
26
|
+
# Optionally return the `total_count` option if it has been set.
|
27
|
+
# This is useful when the total count is known and not equal to size
|
28
|
+
# of wrapped collection.
|
29
|
+
# @rbs override
|
30
|
+
def total_count #: Integer
|
31
|
+
@total_count || @query.unwrap_unpaginated.size
|
32
|
+
end
|
33
|
+
|
34
|
+
# Gets the actual count of elements in the page of results (assuming paging is being used, otherwise the count of
|
35
|
+
# all results)
|
36
|
+
def page_count #: Integer
|
37
|
+
@configured_query.size
|
38
|
+
end
|
39
|
+
|
40
|
+
# @rbs @query: Quo::CollectionBackedQuery
|
41
|
+
# @rbs @transformer: (^(untyped, ?Integer) -> untyped)?
|
42
|
+
# @rbs @configured_query: Object & Enumerable[untyped]
|
43
|
+
end
|
44
|
+
end
|