jsonapi-query_builder 0.2.1 → 0.4.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/.github/workflows/lint.yml +2 -2
- data/.github/workflows/spec.yml +2 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +88 -50
- data/README.md +100 -21
- data/jsonapi-query_builder.gemspec +2 -2
- data/lefthook.yml +2 -3
- data/lib/jsonapi/query_builder/base_sort.rb +1 -1
- data/lib/jsonapi/query_builder/dynamic_sort.rb +24 -0
- data/lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb +3 -3
- data/lib/jsonapi/query_builder/mixins/sort/dynamic.rb +31 -0
- data/lib/jsonapi/query_builder/mixins/sort/static.rb +26 -0
- data/lib/jsonapi/query_builder/mixins/sort.rb +37 -11
- data/lib/jsonapi/query_builder/paginator/pagy.rb +2 -3
- data/lib/jsonapi/query_builder/paginator/pagy_countless.rb +29 -0
- data/lib/jsonapi/query_builder/version.rb +1 -1
- data/lib/jsonapi/query_builder.rb +1 -0
- metadata +10 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9ea254260274b96b7d2374e9b1b6940467f86d619b2507abcffc9a592fbd1e6
|
|
4
|
+
data.tar.gz: 21d0c563ed3593a7ff198b645e92fae84a5fbe144309a71d14c0799c09e21bf6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec7b08f99bd292f0c33105e22be664f92832efee849785cc4f5da9dab1d9575993b546c10722eafd9d2e8472e4ae6d6bc23d9cff3cb48c9bdf4faa0815a9bbc8
|
|
7
|
+
data.tar.gz: 30f6711fab7a8be90960417e545dd2bbf7b3778a4997fceab15f56587fad040018f0a9d3f6f4f845c890e7a8ea2985771823160ff18d9e2672f93dbc59cfedf6
|
data/.github/workflows/lint.yml
CHANGED
|
@@ -8,7 +8,7 @@ on:
|
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
standardrb:
|
|
11
|
-
runs-on: ubuntu-
|
|
11
|
+
runs-on: ubuntu-24.04
|
|
12
12
|
|
|
13
13
|
steps:
|
|
14
14
|
- uses: actions/checkout@v2
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
run: bundle exec standardrb
|
|
20
20
|
|
|
21
21
|
rubocop-rspec:
|
|
22
|
-
runs-on: ubuntu-
|
|
22
|
+
runs-on: ubuntu-24.04
|
|
23
23
|
|
|
24
24
|
steps:
|
|
25
25
|
- uses: actions/checkout@v2
|
data/.github/workflows/spec.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.4.5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
# Change log
|
|
2
2
|
|
|
3
|
+
## 0.4.0 (2025-11-03)
|
|
4
|
+
|
|
5
|
+
### Enhancements
|
|
6
|
+
- Added support for dynamic (prefix-based) sorting [#31](https://github.com/infinum/jsonapi-query_builder/pull/31)
|
|
7
|
+
- Added Pagy countless paginator [#26](https://github.com/infinum/jsonapi-query_builder/pull/26)
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
- Dropped support for Ruby < 3.2
|
|
11
|
+
- Dropped Rails < 7.2 support
|
|
12
|
+
|
|
13
|
+
## 0.3.0 (2021-12-07)
|
|
14
|
+
|
|
15
|
+
### Enhancements
|
|
16
|
+
|
|
17
|
+
- Add support for proc and object default
|
|
18
|
+
sorts [a167f90](https://github.com/infinum/jsonapi-query_builder/commit/a167f90ca718fe62c0899520cd4c5c859f89035b)
|
|
19
|
+
|
|
3
20
|
## 0.2.1 (2021-10-04)
|
|
4
21
|
|
|
22
|
+
### Bugfixes
|
|
23
|
+
|
|
5
24
|
- [#22](https://github.com/infinum/jsonapi-query_builder/pull/22): Bump allowed pagy version.
|
|
6
25
|
|
|
7
26
|
## 0.2.0 (2021-09-29)
|
|
27
|
+
|
|
28
|
+
### Enhancements
|
|
29
|
+
|
|
8
30
|
Added support for Kaminari and Keyset pagination strategies in addition to Pagy.
|
|
9
31
|
|
|
10
32
|
- [#21](https://github.com/infinum/jsonapi-query_builder/pull/21): Extract paginators.
|
|
11
33
|
|
|
12
34
|
## 0.1.9 (2021-05-07)
|
|
13
35
|
|
|
36
|
+
### Enhancements
|
|
37
|
+
|
|
14
38
|
- [#18](https://github.com/infinum/jsonapi-query_builder/pull/18): Remove Ruby `to` version.
|
|
15
39
|
- [#9](https://github.com/infinum/jsonapi-query_builder/pull/9): added github actions
|
|
16
40
|
|
|
17
41
|
## 0.1.8 (2021-01-25)
|
|
18
42
|
|
|
43
|
+
### Enhancements
|
|
44
|
+
|
|
19
45
|
- [#8](https://github.com/infinum/jsonapi-query_builder/pull/8): add support for ruby 3.0
|
|
20
46
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,42 +1,56 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
jsonapi-query_builder (0.
|
|
5
|
-
activerecord (>=
|
|
4
|
+
jsonapi-query_builder (0.4.0)
|
|
5
|
+
activerecord (>= 7.2)
|
|
6
6
|
pagy (>= 3.5)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
actionview (
|
|
12
|
-
activesupport (=
|
|
11
|
+
actionview (8.0.2.1)
|
|
12
|
+
activesupport (= 8.0.2.1)
|
|
13
13
|
builder (~> 3.1)
|
|
14
|
-
erubi (~> 1.
|
|
15
|
-
rails-dom-testing (~> 2.
|
|
16
|
-
rails-html-sanitizer (~> 1.
|
|
17
|
-
activemodel (
|
|
18
|
-
activesupport (=
|
|
19
|
-
activerecord (
|
|
20
|
-
activemodel (=
|
|
21
|
-
activesupport (=
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
erubi (~> 1.11)
|
|
15
|
+
rails-dom-testing (~> 2.2)
|
|
16
|
+
rails-html-sanitizer (~> 1.6)
|
|
17
|
+
activemodel (8.0.2.1)
|
|
18
|
+
activesupport (= 8.0.2.1)
|
|
19
|
+
activerecord (8.0.2.1)
|
|
20
|
+
activemodel (= 8.0.2.1)
|
|
21
|
+
activesupport (= 8.0.2.1)
|
|
22
|
+
timeout (>= 0.4.0)
|
|
23
|
+
activesupport (8.0.2.1)
|
|
24
|
+
base64
|
|
25
|
+
benchmark (>= 0.3)
|
|
26
|
+
bigdecimal
|
|
27
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
28
|
+
connection_pool (>= 2.2.5)
|
|
29
|
+
drb
|
|
24
30
|
i18n (>= 1.6, < 2)
|
|
31
|
+
logger (>= 1.4.2)
|
|
25
32
|
minitest (>= 5.1)
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
securerandom (>= 0.3)
|
|
34
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
35
|
+
uri (>= 0.13.1)
|
|
28
36
|
ast (2.4.2)
|
|
37
|
+
base64 (0.3.0)
|
|
38
|
+
benchmark (0.4.1)
|
|
39
|
+
bigdecimal (3.2.3)
|
|
29
40
|
builder (3.2.4)
|
|
30
41
|
bundler-audit (0.9.0.1)
|
|
31
42
|
bundler (>= 1.2.0, < 3)
|
|
32
43
|
thor (~> 1.0)
|
|
33
44
|
coderay (1.1.3)
|
|
34
|
-
concurrent-ruby (1.
|
|
45
|
+
concurrent-ruby (1.3.5)
|
|
46
|
+
connection_pool (2.5.4)
|
|
35
47
|
crass (1.0.6)
|
|
36
48
|
diff-lcs (1.4.4)
|
|
37
|
-
|
|
49
|
+
drb (2.2.3)
|
|
50
|
+
erubi (1.13.1)
|
|
38
51
|
i18n (1.8.10)
|
|
39
52
|
concurrent-ruby (~> 1.0)
|
|
53
|
+
json (2.13.2)
|
|
40
54
|
kaminari (1.2.1)
|
|
41
55
|
activesupport (>= 4.1.0)
|
|
42
56
|
kaminari-actionview (= 1.2.1)
|
|
@@ -49,33 +63,39 @@ GEM
|
|
|
49
63
|
activerecord
|
|
50
64
|
kaminari-core (= 1.2.1)
|
|
51
65
|
kaminari-core (1.2.1)
|
|
66
|
+
language_server-protocol (3.17.0.5)
|
|
52
67
|
lefthook (0.7.6)
|
|
53
|
-
|
|
68
|
+
lint_roller (1.1.0)
|
|
69
|
+
logger (1.7.0)
|
|
70
|
+
loofah (2.24.1)
|
|
54
71
|
crass (~> 1.0.2)
|
|
55
|
-
nokogiri (>= 1.
|
|
72
|
+
nokogiri (>= 1.12.0)
|
|
56
73
|
method_source (1.0.0)
|
|
57
|
-
mini_portile2 (2.
|
|
74
|
+
mini_portile2 (2.8.9)
|
|
58
75
|
minitest (5.14.4)
|
|
59
|
-
nokogiri (1.
|
|
60
|
-
mini_portile2 (~> 2.
|
|
76
|
+
nokogiri (1.18.9)
|
|
77
|
+
mini_portile2 (~> 2.8.2)
|
|
61
78
|
racc (~> 1.4)
|
|
62
79
|
pagy (3.11.0)
|
|
63
80
|
parallel (1.21.0)
|
|
64
|
-
parser (3.
|
|
81
|
+
parser (3.3.9.0)
|
|
65
82
|
ast (~> 2.4.1)
|
|
83
|
+
racc
|
|
84
|
+
prism (1.4.0)
|
|
66
85
|
pry (0.14.1)
|
|
67
86
|
coderay (~> 1.1)
|
|
68
87
|
method_source (~> 1.0)
|
|
69
88
|
racc (1.5.2)
|
|
70
|
-
rails-dom-testing (2.0
|
|
71
|
-
activesupport (>=
|
|
89
|
+
rails-dom-testing (2.3.0)
|
|
90
|
+
activesupport (>= 5.0.0)
|
|
91
|
+
minitest
|
|
72
92
|
nokogiri (>= 1.6)
|
|
73
|
-
rails-html-sanitizer (1.
|
|
74
|
-
loofah (~> 2.
|
|
93
|
+
rails-html-sanitizer (1.6.2)
|
|
94
|
+
loofah (~> 2.21)
|
|
95
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
75
96
|
rainbow (3.0.0)
|
|
76
97
|
rake (13.0.6)
|
|
77
|
-
regexp_parser (2.
|
|
78
|
-
rexml (3.2.5)
|
|
98
|
+
regexp_parser (2.11.2)
|
|
79
99
|
rspec (3.10.0)
|
|
80
100
|
rspec-core (~> 3.10.0)
|
|
81
101
|
rspec-expectations (~> 3.10.0)
|
|
@@ -89,34 +109,52 @@ GEM
|
|
|
89
109
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
90
110
|
rspec-support (~> 3.10.0)
|
|
91
111
|
rspec-support (3.10.2)
|
|
92
|
-
rubocop (1.
|
|
112
|
+
rubocop (1.80.2)
|
|
113
|
+
json (~> 2.3)
|
|
114
|
+
language_server-protocol (~> 3.17.0.2)
|
|
115
|
+
lint_roller (~> 1.1.0)
|
|
93
116
|
parallel (~> 1.10)
|
|
94
|
-
parser (>= 3.
|
|
117
|
+
parser (>= 3.3.0.2)
|
|
95
118
|
rainbow (>= 2.2.2, < 4.0)
|
|
96
|
-
regexp_parser (>=
|
|
97
|
-
|
|
98
|
-
rubocop-ast (>= 1.9.1, < 2.0)
|
|
119
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
120
|
+
rubocop-ast (>= 1.46.0, < 2.0)
|
|
99
121
|
ruby-progressbar (~> 1.7)
|
|
100
|
-
unicode-display_width (>=
|
|
101
|
-
rubocop-ast (1.
|
|
102
|
-
parser (>= 3.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
123
|
+
rubocop-ast (1.46.0)
|
|
124
|
+
parser (>= 3.3.7.2)
|
|
125
|
+
prism (~> 1.4)
|
|
126
|
+
rubocop-performance (1.25.0)
|
|
127
|
+
lint_roller (~> 1.1)
|
|
128
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
129
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
|
106
130
|
rubocop-rspec (2.5.0)
|
|
107
131
|
rubocop (~> 1.19)
|
|
108
132
|
ruby-progressbar (1.11.0)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
securerandom (0.4.1)
|
|
134
|
+
sqlite3 (2.7.3)
|
|
135
|
+
mini_portile2 (~> 2.8.0)
|
|
136
|
+
standard (1.51.0)
|
|
137
|
+
language_server-protocol (~> 3.17.0.2)
|
|
138
|
+
lint_roller (~> 1.0)
|
|
139
|
+
rubocop (~> 1.80.2)
|
|
140
|
+
standard-custom (~> 1.0.0)
|
|
141
|
+
standard-performance (~> 1.8)
|
|
142
|
+
standard-custom (1.0.2)
|
|
143
|
+
lint_roller (~> 1.0)
|
|
144
|
+
rubocop (~> 1.50)
|
|
145
|
+
standard-performance (1.8.0)
|
|
146
|
+
lint_roller (~> 1.1)
|
|
147
|
+
rubocop-performance (~> 1.25.0)
|
|
113
148
|
standardrb (1.0.0)
|
|
114
149
|
standard
|
|
115
|
-
thor (1.
|
|
116
|
-
|
|
150
|
+
thor (1.4.0)
|
|
151
|
+
timeout (0.4.3)
|
|
152
|
+
tzinfo (2.0.6)
|
|
117
153
|
concurrent-ruby (~> 1.0)
|
|
118
|
-
unicode-display_width (2.
|
|
119
|
-
|
|
154
|
+
unicode-display_width (3.2.0)
|
|
155
|
+
unicode-emoji (~> 4.1)
|
|
156
|
+
unicode-emoji (4.1.0)
|
|
157
|
+
uri (1.0.4)
|
|
120
158
|
|
|
121
159
|
PLATFORMS
|
|
122
160
|
ruby
|
|
@@ -137,4 +175,4 @@ DEPENDENCIES
|
|
|
137
175
|
standardrb
|
|
138
176
|
|
|
139
177
|
BUNDLED WITH
|
|
140
|
-
2.2
|
|
178
|
+
2.7.2
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Jsonapi::QueryBuilder 
|
|
2
2
|
|
|
3
3
|
`Jsonapi::QueryBuilder` serves the purpose of adding the json api query related SQL conditions to the already scoped
|
|
4
|
-
collection, usually used in controller index actions.
|
|
4
|
+
collection, usually used in controller index actions.
|
|
5
5
|
|
|
6
6
|
With the query builder we can easily define logic for query filters, attributes by which we can sort, and delegate
|
|
7
7
|
pagination parameters to the underlying paginator. Included relationships are automatically included via the
|
|
@@ -27,9 +27,9 @@ Or install it yourself as:
|
|
|
27
27
|
|
|
28
28
|
```ruby
|
|
29
29
|
class UserQuery < Jsonapi::QueryBuilder::BaseQuery
|
|
30
|
-
## pagination
|
|
30
|
+
## pagination
|
|
31
31
|
paginator Jsonapi::QueryBuilder::Paginator::Pagy # default paginator
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
## sorting
|
|
34
34
|
default_sort created_at: :desc
|
|
35
35
|
sorts_by :last_name
|
|
@@ -54,21 +54,23 @@ end
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
The query class is initialized using a collection and query parameters. Since query parameters are referenced explicitly
|
|
57
|
-
we can pass them as an unsafe hash. `Jsonapi::QueryBuilder::BaseQuery` should not be responsible for scoping records
|
|
58
|
-
current user permissions, or for any other type of scoping. It's only responsibility is to support
|
|
59
|
-
querying. Use `pundit` or similar for policy scoping, custom query objects for other scoping, and then
|
|
60
|
-
collection to the `Jsonapi::QueryBuilder::BaseQuery` object.
|
|
57
|
+
we can pass them as an unsafe hash. `Jsonapi::QueryBuilder::BaseQuery` should not be responsible for scoping records
|
|
58
|
+
based on current user permissions, or for any other type of scoping. It's only responsibility is to support
|
|
59
|
+
the `json:api` querying. Use `pundit` or similar for policy scoping, custom query objects for other scoping, and then
|
|
60
|
+
pass the scoped collection to the `Jsonapi::QueryBuilder::BaseQuery` object.
|
|
61
61
|
|
|
62
62
|
### Pagination
|
|
63
|
+
|
|
63
64
|
Pagination support is configurable using the `paginator` method to define the paginator. It defaults to the `Pagy`
|
|
64
|
-
paginator, a lightweight and fast paginator. Other paginators currently supported are `Kaminari` and
|
|
65
|
-
of keyset pagination. Before using these paginators we need to explicitly require the gems in our
|
|
66
|
-
paginator file in question.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
paginator, a lightweight and fast paginator. Other paginators currently supported are `Kaminari`, `PagyCountless` and
|
|
66
|
+
an implementation of keyset pagination. Before using these paginators we need to explicitly require the gems in our
|
|
67
|
+
Gemfile and the paginator file in question. Additionally one can implement it's own paginator by inheriting
|
|
68
|
+
from `Jsonapi::QueryBuilder::Paginator::BasePaginator`. The minimum required implementation is a `#paginate` method that
|
|
69
|
+
receives page params and returns a page of the collection. It can return the pagination details as the second item of
|
|
70
|
+
the returned array, that can be used in the serializer for pagination metadata.
|
|
71
|
+
|
|
71
72
|
#### Using the Kaminari Paginator
|
|
73
|
+
|
|
72
74
|
```ruby
|
|
73
75
|
require "jsonapi/query_builder/paginator/kaminari"
|
|
74
76
|
|
|
@@ -76,64 +78,128 @@ paginator Jsonapi::QueryBuilder::Paginator::Kaminari
|
|
|
76
78
|
```
|
|
77
79
|
|
|
78
80
|
#### Using the Keyset Paginator
|
|
81
|
+
|
|
79
82
|
```ruby
|
|
80
83
|
require "jsonapi/query_builder/paginator/keyset"
|
|
81
84
|
|
|
82
85
|
paginator Jsonapi::QueryBuilder::Paginator::Keyset
|
|
83
86
|
```
|
|
84
87
|
|
|
88
|
+
#### Using the Pagy Countless Paginator
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
require "jsonapi/query_builder/paginator/pagy_countless"
|
|
92
|
+
|
|
93
|
+
paginator Jsonapi::QueryBuilder::Paginator::PagyCountless
|
|
94
|
+
```
|
|
95
|
+
|
|
85
96
|
### Sorting
|
|
97
|
+
|
|
86
98
|
#### Ensuring deterministic results
|
|
99
|
+
|
|
87
100
|
Sorting has a fallback to an unique attribute which defaults to the `id` attribute. This ensures deterministic paginated
|
|
88
101
|
collection responses. You can override the `unique_sort_attribute` in the query object.
|
|
102
|
+
|
|
89
103
|
```ruby
|
|
90
104
|
# set the unique sort attribute
|
|
91
105
|
unique_sort_attribute :email
|
|
92
106
|
# use compound unique sort attributes
|
|
93
107
|
unique_sort_attributes :created_at, :email
|
|
94
108
|
````
|
|
109
|
+
|
|
95
110
|
#### Default sort options
|
|
111
|
+
|
|
96
112
|
The `default_sort` can be set to sort by any field like `created_at` timestamp or similar. It is only used if no sort
|
|
97
113
|
parameter is set, unlike the `unique_sort_attribute` which is always appended as the last sort attribute. The parameters
|
|
98
|
-
are passed directly to the underlying active record relation, so the usual ordering options are possible.
|
|
114
|
+
are passed directly to the underlying active record relation, so the usual ordering options are possible. It is also
|
|
115
|
+
possible to define the default sort with a lambda or by passing a sort object.
|
|
116
|
+
|
|
99
117
|
```ruby
|
|
118
|
+
default_sort :created_at
|
|
119
|
+
# or
|
|
100
120
|
default_sort created_at: :desc
|
|
121
|
+
# or
|
|
122
|
+
default_sort ->(collection) { collection.order(created_at: :desc) }
|
|
123
|
+
# or
|
|
124
|
+
default_sort SortObject
|
|
101
125
|
```
|
|
126
|
+
|
|
102
127
|
#### Enabling simple sorting for attributes
|
|
128
|
+
|
|
103
129
|
`sorts_by` denotes which attributes can be used for sorting. Sorting parameters are usually parsed from the
|
|
104
130
|
`json:api` sort query parameter in the order they are given. So `sort=-first_name,email` would translate to
|
|
105
|
-
`{ first_name: :desc, email: :asc }`
|
|
131
|
+
`{ first_name: :desc, email: :asc }`
|
|
132
|
+
|
|
106
133
|
```ruby
|
|
107
134
|
sorts_by :first_name
|
|
108
135
|
sorts_by :email
|
|
109
136
|
```
|
|
137
|
+
|
|
110
138
|
#### Sorting with lambdas
|
|
111
|
-
|
|
139
|
+
|
|
140
|
+
`sorts_by` also supports passing a lambda to implement a custom order or reorder function. The parameters passed to the
|
|
112
141
|
lamdba are collection and the direction of the order, which is either `:desc` or `:asc`.
|
|
142
|
+
|
|
113
143
|
```ruby
|
|
114
144
|
sorts_by :first_name, ->(collection, direction) { collection.order(name: direction) }
|
|
115
145
|
```
|
|
116
146
|
|
|
117
147
|
#### Sorting with sort classes
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
the current scope and the
|
|
148
|
+
|
|
149
|
+
But since we're devout followers of the SOLID principles, we can define a sort class that responds to `#results` method,
|
|
150
|
+
which returns the sorted collection. Under the hood the sort class is initialized with the current scope and the
|
|
151
|
+
direction parameter.
|
|
152
|
+
|
|
153
|
+
#### Dynamic sorting (prefix-based)
|
|
154
|
+
|
|
155
|
+
Sometimes you want to allow sorting by a dynamic subset of attributes that share a common prefix (e.g., JSON/JSONB keys, translated columns, join records). You can register a dynamic sort by attribute prefix using `dynamically_sorts_by`.
|
|
156
|
+
|
|
157
|
+
- The configured prefix is matched against each parsed sort attribute.
|
|
158
|
+
- The prefix is stripped and only the dynamic part is passed to your sort handler.
|
|
159
|
+
- You can provide either a lambda/proc or a class. The callable receives `(collection, dynamic_attribute, direction)`.
|
|
160
|
+
|
|
161
|
+
Example with a lambda (PostgreSQL JSONB text value):
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
# Allows sorting by any key in the `data` column: e.g. sort=-data.name,data.created_at
|
|
165
|
+
dynamically_sorts_by :'data.', ->(collection, attribute, direction) {
|
|
166
|
+
# attribute is the part after the prefix, e.g. "name" or "created_at"
|
|
167
|
+
quoted_attribute = ActiveRecord::Base.connection.quote(attribute)
|
|
168
|
+
collection.order(Arel.sql("(data->>#{quoted_attribute}) #{direction}"))
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Example with a sort class (PostgreSQL JSONB text value):
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
class DataSort < Jsonapi::QueryBuilder::DynamicSort
|
|
176
|
+
def results
|
|
177
|
+
quoted_attribute = ActiveRecord::Base.connection.quote(dynamic_attribute)
|
|
178
|
+
collection.order(Arel.sql("(data->>#{quoted_attribute}) #{direction}"))
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
dynamically_sorts_by :'data.', DataSort
|
|
183
|
+
```
|
|
121
184
|
|
|
122
185
|
### Filtering
|
|
123
186
|
|
|
124
187
|
#### Simple exact match filters
|
|
188
|
+
|
|
125
189
|
```ruby
|
|
126
190
|
filters_by :first_name
|
|
127
191
|
# => collection.where(first_name: params.dig(:filter, :first_name)) if params.dig(:filter, :first_name).present?
|
|
128
192
|
```
|
|
129
193
|
|
|
130
194
|
#### Lambda as a filter
|
|
195
|
+
|
|
131
196
|
```ruby
|
|
132
197
|
filters_by :email, ->(collection, query) { collection.where('email ilike ?', "%#{query}%") }
|
|
133
198
|
# => collection.where('email ilike ?', "%#{params.dig(:filter, :email)}%") if params.dig(:filter, :email).present?
|
|
134
199
|
```
|
|
135
200
|
|
|
136
201
|
#### Filter classes
|
|
202
|
+
|
|
137
203
|
We can define a filter class that responds to `#results` method, which returns the filtered collection results. Under
|
|
138
204
|
the hood the filter class is initialized with the current scope and the query parameter. However, if the object responds
|
|
139
205
|
to a `call` method it sends the current scope and the query parameter to that instead. This is great if you're using
|
|
@@ -142,17 +208,23 @@ query objects for ActiveRecord scopes, you can easily use them to filter with as
|
|
|
142
208
|
```ruby
|
|
143
209
|
filters_by :type, TypeFilter
|
|
144
210
|
```
|
|
211
|
+
|
|
145
212
|
The filter class could look something like
|
|
213
|
+
|
|
146
214
|
```ruby
|
|
215
|
+
|
|
147
216
|
class TypeFilter < Jsonapi::QueryBuilder::BaseFilter
|
|
148
217
|
def results
|
|
149
218
|
collection.where(type: query.split(','))
|
|
150
219
|
end
|
|
151
220
|
end
|
|
152
221
|
```
|
|
222
|
+
|
|
153
223
|
Sometimes you need to perform in-memory filtering, for example when database attributes are encrypted. In that case,
|
|
154
224
|
those filters should be applied last, the order of definition in the query object matters.
|
|
225
|
+
|
|
155
226
|
```ruby
|
|
227
|
+
|
|
156
228
|
class MrnFilter < Jsonapi::QueryBuilder::BaseFilter
|
|
157
229
|
def results
|
|
158
230
|
collection.select { |record| /#{query}/.match?(record.mrn) }
|
|
@@ -161,26 +233,34 @@ end
|
|
|
161
233
|
```
|
|
162
234
|
|
|
163
235
|
#### Additional Options
|
|
236
|
+
|
|
164
237
|
You can override the filter query parameter name by passing the `query_parameter` option.
|
|
238
|
+
|
|
165
239
|
```ruby
|
|
166
240
|
filters_by :first_name, query_parameter: 'name'
|
|
167
241
|
# => collection.where(first_name: params.dig(:filter, :name)) if params.dig(:filter, :name).present?
|
|
168
242
|
```
|
|
243
|
+
|
|
169
244
|
`allow_nil` option changes the filter conditional to allow explicit checks for an attribute null value.
|
|
245
|
+
|
|
170
246
|
```ruby
|
|
171
247
|
filters_by :first_name, allow_nil: true
|
|
172
248
|
# => collection.where(first_name: params.dig(:filter, :first_name)) if params[:filter]&.key?(:first_name)
|
|
173
249
|
```
|
|
250
|
+
|
|
174
251
|
The conditional when the filter is applied can also be defined explicitly. Note that these options override the
|
|
175
252
|
`allow_nil` option, as the condition if defined explicitly and you should handle `nil` explicitly as well.
|
|
253
|
+
|
|
176
254
|
```ruby
|
|
177
255
|
filters_by :first_name, if: ->(query) { query.length >= 2 }
|
|
178
256
|
# => collection.where(first_name: params.dig(:filter, :first_name)) if params.dig(:filter, :first_name) >= 2
|
|
179
257
|
filters_by :first_name, unless: ->(query) { query.length < 2 }
|
|
180
258
|
# => collection.where(first_name: params.dig(:filter, :first_name)) unless params.dig(:filter, :first_name) < 2
|
|
181
259
|
```
|
|
260
|
+
|
|
182
261
|
When you're using a filter class you can pass a symbol to the `:if` and `:unless` options which invokes the method on
|
|
183
262
|
the filter class.
|
|
263
|
+
|
|
184
264
|
```ruby
|
|
185
265
|
filters_by :type, TypeFilter, if: :correct_type?
|
|
186
266
|
# => type_filter = TypeFilter.new(collection, query); type_filter.results if type_filter.correct_type?
|
|
@@ -202,7 +282,6 @@ version, push git commits and tags, and push the `.gem` file to [rubygems.org](h
|
|
|
202
282
|
|
|
203
283
|
Bug reports and pull requests are welcome on GitHub at https://github.com/infinum/jsonapi-query_builder.
|
|
204
284
|
|
|
205
|
-
|
|
206
285
|
## License
|
|
207
286
|
|
|
208
287
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -36,9 +36,9 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
37
37
|
spec.require_paths = ["lib"]
|
|
38
38
|
|
|
39
|
-
spec.required_ruby_version = ">= 2
|
|
39
|
+
spec.required_ruby_version = ">= 3.2"
|
|
40
40
|
|
|
41
|
-
spec.add_runtime_dependency "activerecord", ">=
|
|
41
|
+
spec.add_runtime_dependency "activerecord", ">= 7.2"
|
|
42
42
|
spec.add_runtime_dependency "pagy", ">= 3.5"
|
|
43
43
|
|
|
44
44
|
spec.add_development_dependency "bundler", "~> 2.0"
|
data/lefthook.yml
CHANGED
|
@@ -2,7 +2,7 @@ lint:
|
|
|
2
2
|
commands: &lint
|
|
3
3
|
lint-frozen-strings:
|
|
4
4
|
glob: "*.rb"
|
|
5
|
-
run: bundle exec rubocop --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment --format quiet --
|
|
5
|
+
run: bundle exec rubocop --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment --format quiet --autocorrect
|
|
6
6
|
|
|
7
7
|
rubocop:
|
|
8
8
|
commands: &rubocop
|
|
@@ -13,8 +13,7 @@ rubocop:
|
|
|
13
13
|
pre-commit:
|
|
14
14
|
parallel: true
|
|
15
15
|
commands:
|
|
16
|
-
<<: *lint
|
|
17
|
-
<<: *rubocop
|
|
16
|
+
<<: [*lint, *rubocop]
|
|
18
17
|
standardrb:
|
|
19
18
|
glob: "*.rb"
|
|
20
19
|
run: bundle exec standardrb {staged_files}
|
|
@@ -7,7 +7,7 @@ module Jsonapi
|
|
|
7
7
|
|
|
8
8
|
# @param [ActiveRecord::Relation] collection
|
|
9
9
|
# @param [Symbol] direction of the ordering, one of :asc or :desc
|
|
10
|
-
def initialize(collection, direction)
|
|
10
|
+
def initialize(collection, direction = :asc)
|
|
11
11
|
@collection = collection
|
|
12
12
|
@direction = direction
|
|
13
13
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "jsonapi/query_builder/base_sort"
|
|
4
|
+
|
|
5
|
+
module Jsonapi
|
|
6
|
+
module QueryBuilder
|
|
7
|
+
class DynamicSort < BaseSort
|
|
8
|
+
attr_reader :dynamic_attribute
|
|
9
|
+
|
|
10
|
+
# @param [ActiveRecord::Relation] collection
|
|
11
|
+
# @param [Symbol] dynamic_attribute, which attribute to dynamically sort by
|
|
12
|
+
# @param [Symbol] direction of the ordering, one of :asc or :desc
|
|
13
|
+
def initialize(collection, dynamic_attribute, direction = :asc)
|
|
14
|
+
super(collection, direction)
|
|
15
|
+
@dynamic_attribute = dynamic_attribute
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [ActiveRecord::Relation] Collection with order applied
|
|
19
|
+
def results
|
|
20
|
+
raise NotImplementedError, "#{self.class} should implement #results"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -5,11 +5,11 @@ module Jsonapi
|
|
|
5
5
|
module Errors
|
|
6
6
|
class UnpermittedSortParameters < ArgumentError
|
|
7
7
|
def initialize(unpermitted_parameters)
|
|
8
|
-
super
|
|
8
|
+
super([
|
|
9
9
|
unpermitted_parameters.to_sentence,
|
|
10
|
-
unpermitted_parameters.count == 1 ? "is not a" : "are not",
|
|
10
|
+
(unpermitted_parameters.count == 1) ? "is not a" : "are not",
|
|
11
11
|
"permitted sort attribute".pluralize(unpermitted_parameters.count)
|
|
12
|
-
].join(" ")
|
|
12
|
+
].join(" "))
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsonapi
|
|
4
|
+
module QueryBuilder
|
|
5
|
+
module Mixins
|
|
6
|
+
module Sort
|
|
7
|
+
class Dynamic
|
|
8
|
+
attr_reader :attribute_prefix, :sort
|
|
9
|
+
|
|
10
|
+
def initialize(attribute_prefix, sort)
|
|
11
|
+
@attribute_prefix = attribute_prefix.to_s
|
|
12
|
+
@sort = sort
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def matches?(sort_attribute)
|
|
16
|
+
sort_attribute.to_s.start_with?(attribute_prefix)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def results(collection, sort_param)
|
|
20
|
+
dynamic_attribute = sort_param.attribute.delete_prefix(attribute_prefix)
|
|
21
|
+
if sort.respond_to?(:call)
|
|
22
|
+
sort.call(collection, dynamic_attribute, sort_param.direction)
|
|
23
|
+
else
|
|
24
|
+
sort.new(collection, dynamic_attribute, sort_param.direction).results
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jsonapi
|
|
4
|
+
module QueryBuilder
|
|
5
|
+
module Mixins
|
|
6
|
+
module Sort
|
|
7
|
+
class Static
|
|
8
|
+
attr_reader :attribute, :sort
|
|
9
|
+
|
|
10
|
+
def initialize(attribute, sort)
|
|
11
|
+
@attribute = attribute
|
|
12
|
+
@sort = sort || ->(collection, direction) { collection.order(attribute => direction) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def results(collection, sort_param)
|
|
16
|
+
if sort.respond_to?(:call)
|
|
17
|
+
sort.call(collection, sort_param.direction)
|
|
18
|
+
else
|
|
19
|
+
sort.new(collection, sort_param.direction).results
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "jsonapi/query_builder/mixins/sort/param"
|
|
4
|
+
require "jsonapi/query_builder/mixins/sort/static"
|
|
5
|
+
require "jsonapi/query_builder/mixins/sort/dynamic"
|
|
4
6
|
require "jsonapi/query_builder/errors/unpermitted_sort_parameters"
|
|
5
7
|
|
|
6
8
|
module Jsonapi
|
|
@@ -16,8 +18,14 @@ module Jsonapi
|
|
|
16
18
|
@_unique_sort_attributes || [id: :asc]
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
# @return [Hash<Symbol, Jsonapi::QueryBuilder::Mixins::Sort::Static>] Supported sorts
|
|
19
22
|
def supported_sorts
|
|
20
|
-
@supported_sorts
|
|
23
|
+
@supported_sorts ||= {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Array<Jsonapi::QueryBuilder::Mixins::Sort::Dynamic>] Supported dynamic sorts
|
|
27
|
+
def supported_dynamic_sorts
|
|
28
|
+
@supported_dynamic_sorts ||= []
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
# Ensures deterministic ordering. Defaults to :id in ascending direction.
|
|
@@ -43,8 +51,14 @@ module Jsonapi
|
|
|
43
51
|
# @param [Symbol] attribute The "sortable" attribute
|
|
44
52
|
# @param [proc, Class] sort A proc or a sort class, defaults to a simple order(attribute => direction)
|
|
45
53
|
def sorts_by(attribute, sort = nil)
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
supported_sorts[attribute] = Sort::Static.new(attribute, sort)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Registers an attribute prefix that can be dynamically used for sorting. Attribute prefix is stripped from parsed sort parameter and passed to the sort proc or class.
|
|
58
|
+
# @param [Symbol] attribute_prefix The "sortable" attribute prefix, e.g. `:'data.'` for sorting by `data.name` and `data.created_at`
|
|
59
|
+
# @param [proc, Class] sort A proc or a sort class, defaults to a simple order(attribute => direction)
|
|
60
|
+
def dynamically_sorts_by(attribute_prefix, sort)
|
|
61
|
+
supported_dynamic_sorts << Sort::Dynamic.new(attribute_prefix, sort)
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
|
|
@@ -76,7 +90,9 @@ module Jsonapi
|
|
|
76
90
|
end
|
|
77
91
|
|
|
78
92
|
def ensure_permitted_sort_params!(sort_params)
|
|
79
|
-
unpermitted_parameters = sort_params.map(&:attribute).
|
|
93
|
+
unpermitted_parameters = sort_params.map(&:attribute).filter do |attribute|
|
|
94
|
+
!self.class.supported_sorts.key?(attribute.to_sym) && self.class.supported_dynamic_sorts.none? { |dynamic_sort| dynamic_sort.matches?(attribute) }
|
|
95
|
+
end
|
|
80
96
|
return if unpermitted_parameters.size.zero?
|
|
81
97
|
|
|
82
98
|
raise Errors::UnpermittedSortParameters, unpermitted_parameters
|
|
@@ -84,16 +100,26 @@ module Jsonapi
|
|
|
84
100
|
|
|
85
101
|
def add_order_attributes(collection, sort_params)
|
|
86
102
|
return collection if self.class._default_sort.nil? && sort_params.blank?
|
|
87
|
-
return collection
|
|
103
|
+
return sort_by_default(collection) if sort_params.blank?
|
|
88
104
|
|
|
89
105
|
sort_params.reduce(collection) do |sorted_collection, sort_param|
|
|
90
|
-
sort = self.class.supported_sorts.fetch(sort_param.attribute.to_sym)
|
|
91
|
-
|
|
92
|
-
if sort.respond_to?(:call)
|
|
93
|
-
sort.call(sorted_collection, sort_param.direction)
|
|
94
|
-
else
|
|
95
|
-
sort.new(sorted_collection, sort_param.direction).results
|
|
106
|
+
sort = self.class.supported_sorts.fetch(sort_param.attribute.to_sym) do
|
|
107
|
+
self.class.supported_dynamic_sorts.find { |dynamic_sort| dynamic_sort.matches?(sort_param.attribute) }
|
|
96
108
|
end
|
|
109
|
+
|
|
110
|
+
sort.results(sorted_collection, sort_param)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def sort_by_default(collection)
|
|
115
|
+
default_sort = self.class._default_sort
|
|
116
|
+
|
|
117
|
+
if default_sort.is_a?(Symbol) || default_sort.is_a?(Hash)
|
|
118
|
+
collection.order(default_sort)
|
|
119
|
+
elsif default_sort.respond_to?(:call)
|
|
120
|
+
default_sort.call(collection)
|
|
121
|
+
else
|
|
122
|
+
default_sort.new(collection).results
|
|
97
123
|
end
|
|
98
124
|
end
|
|
99
125
|
|
|
@@ -12,9 +12,8 @@ module Jsonapi
|
|
|
12
12
|
def paginate(page_params)
|
|
13
13
|
@params = {page: page_params}
|
|
14
14
|
|
|
15
|
-
pagination_details, records = pagy
|
|
16
|
-
|
|
17
|
-
outset: page_params[:offset]
|
|
15
|
+
pagination_details, records = pagy(collection, page: page_params[:number], items: page_params[:size], outset: page_params[:offset])
|
|
16
|
+
|
|
18
17
|
[records, pagination_details]
|
|
19
18
|
end
|
|
20
19
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pagy"
|
|
4
|
+
require "pagy/extras/items"
|
|
5
|
+
require "pagy/extras/countless"
|
|
6
|
+
|
|
7
|
+
module Jsonapi
|
|
8
|
+
module QueryBuilder
|
|
9
|
+
module Paginator
|
|
10
|
+
class PagyCountless < BasePaginator
|
|
11
|
+
include ::Pagy::Backend
|
|
12
|
+
|
|
13
|
+
def paginate(page_params)
|
|
14
|
+
@params = {page: page_params}
|
|
15
|
+
|
|
16
|
+
pagination_details, records = pagy_countless(collection, page: page_params[:number],
|
|
17
|
+
items: page_params[:size],
|
|
18
|
+
outset: page_params[:offset])
|
|
19
|
+
|
|
20
|
+
[records, pagination_details]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
attr_reader :params
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jsonapi-query_builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jure Cindro
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activerecord
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '7.2'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '7.2'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: pagy
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -236,17 +235,21 @@ files:
|
|
|
236
235
|
- lib/jsonapi/query_builder/base_filter.rb
|
|
237
236
|
- lib/jsonapi/query_builder/base_query.rb
|
|
238
237
|
- lib/jsonapi/query_builder/base_sort.rb
|
|
238
|
+
- lib/jsonapi/query_builder/dynamic_sort.rb
|
|
239
239
|
- lib/jsonapi/query_builder/errors/unpermitted_sort_parameters.rb
|
|
240
240
|
- lib/jsonapi/query_builder/mixins/filter.rb
|
|
241
241
|
- lib/jsonapi/query_builder/mixins/include.rb
|
|
242
242
|
- lib/jsonapi/query_builder/mixins/paginate.rb
|
|
243
243
|
- lib/jsonapi/query_builder/mixins/sort.rb
|
|
244
|
+
- lib/jsonapi/query_builder/mixins/sort/dynamic.rb
|
|
244
245
|
- lib/jsonapi/query_builder/mixins/sort/param.rb
|
|
246
|
+
- lib/jsonapi/query_builder/mixins/sort/static.rb
|
|
245
247
|
- lib/jsonapi/query_builder/paginator.rb
|
|
246
248
|
- lib/jsonapi/query_builder/paginator/base_paginator.rb
|
|
247
249
|
- lib/jsonapi/query_builder/paginator/kaminari.rb
|
|
248
250
|
- lib/jsonapi/query_builder/paginator/keyset.rb
|
|
249
251
|
- lib/jsonapi/query_builder/paginator/pagy.rb
|
|
252
|
+
- lib/jsonapi/query_builder/paginator/pagy_countless.rb
|
|
250
253
|
- lib/jsonapi/query_builder/version.rb
|
|
251
254
|
homepage: https://github.com/infinum/jsonapi-query_builder
|
|
252
255
|
licenses:
|
|
@@ -255,7 +258,6 @@ metadata:
|
|
|
255
258
|
homepage_uri: https://github.com/infinum/jsonapi-query_builder
|
|
256
259
|
source_code_uri: https://github.com/infinum/jsonapi-query_builder
|
|
257
260
|
changelog_uri: https://github.com/infinum/jsonapi-query_builder/blob/master/CHANGELOG.md
|
|
258
|
-
post_install_message:
|
|
259
261
|
rdoc_options: []
|
|
260
262
|
require_paths:
|
|
261
263
|
- lib
|
|
@@ -263,15 +265,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
263
265
|
requirements:
|
|
264
266
|
- - ">="
|
|
265
267
|
- !ruby/object:Gem::Version
|
|
266
|
-
version: '2
|
|
268
|
+
version: '3.2'
|
|
267
269
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
270
|
requirements:
|
|
269
271
|
- - ">="
|
|
270
272
|
- !ruby/object:Gem::Version
|
|
271
273
|
version: '0'
|
|
272
274
|
requirements: []
|
|
273
|
-
rubygems_version: 3.
|
|
274
|
-
signing_key:
|
|
275
|
+
rubygems_version: 3.6.9
|
|
275
276
|
specification_version: 4
|
|
276
277
|
summary: Support `json:api` querying with ease!
|
|
277
278
|
test_files: []
|