jsonapi-query_builder 0.3.0 → 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 +10 -0
- data/Gemfile.lock +88 -50
- data/README.md +44 -4
- data/jsonapi-query_builder.gemspec +2 -2
- data/lefthook.yml +2 -3
- 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 +24 -10
- 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,5 +1,15 @@
|
|
|
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
|
+
|
|
3
13
|
## 0.3.0 (2021-12-07)
|
|
4
14
|
|
|
5
15
|
### Enhancements
|
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
|
@@ -27,7 +27,7 @@ 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
|
|
@@ -62,9 +62,9 @@ pass the scoped collection to the `Jsonapi::QueryBuilder::BaseQuery` object.
|
|
|
62
62
|
### Pagination
|
|
63
63
|
|
|
64
64
|
Pagination support is configurable using the `paginator` method to define the paginator. It defaults to the `Pagy`
|
|
65
|
-
paginator, a lightweight and fast paginator. Other paginators currently supported are `Kaminari` and
|
|
66
|
-
of keyset pagination. Before using these paginators we need to explicitly require the gems in our
|
|
67
|
-
paginator file in question. Additionally one can implement it's own paginator by inheriting
|
|
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
68
|
from `Jsonapi::QueryBuilder::Paginator::BasePaginator`. The minimum required implementation is a `#paginate` method that
|
|
69
69
|
receives page params and returns a page of the collection. It can return the pagination details as the second item of
|
|
70
70
|
the returned array, that can be used in the serializer for pagination metadata.
|
|
@@ -85,6 +85,14 @@ require "jsonapi/query_builder/paginator/keyset"
|
|
|
85
85
|
paginator Jsonapi::QueryBuilder::Paginator::Keyset
|
|
86
86
|
```
|
|
87
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
|
+
|
|
88
96
|
### Sorting
|
|
89
97
|
|
|
90
98
|
#### Ensuring deterministic results
|
|
@@ -142,6 +150,38 @@ But since we're devout followers of the SOLID principles, we can define a sort c
|
|
|
142
150
|
which returns the sorted collection. Under the hood the sort class is initialized with the current scope and the
|
|
143
151
|
direction parameter.
|
|
144
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
|
+
```
|
|
184
|
+
|
|
145
185
|
### Filtering
|
|
146
186
|
|
|
147
187
|
#### Simple exact match filters
|
|
@@ -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}
|
|
@@ -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
|
|
@@ -87,13 +103,11 @@ module Jsonapi
|
|
|
87
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)
|
|
97
111
|
end
|
|
98
112
|
end
|
|
99
113
|
|
|
@@ -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: []
|