json_api_filter 0.2 → 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/gem-push.yml +44 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -2
- data/Gemfile.lock +205 -131
- data/README.md +101 -6
- data/json_api_filter.gemspec +6 -5
- data/lib/json_api_filter/custom_filter.rb +24 -0
- data/lib/json_api_filter/dispatch.rb +35 -6
- data/lib/json_api_filter/version.rb +1 -1
- data/lib/json_api_filter.rb +63 -2
- metadata +23 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26dba474ac325ef0b01c1f2a6046d94ec4883e497eb892fc5646744eb0e67273
|
|
4
|
+
data.tar.gz: 627dffb840e95036c4617175345702278c6221f7fc513033b94d8eb0b343e0cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b6afe3cc8426481a78cf30287ccd153c5f55c981b593a533e3f1941d376b5e72453dd4537a478b73b5600154559d57529390d66cadc87380be78036897b4ef22
|
|
7
|
+
data.tar.gz: 607c4107b32a551de880edccd2f10c86711fa45250120756bd390eff953ed44677e9c28a69ecc19ec5b0ed832a76f4d024684198897ef9847d6c710ecb6d7751
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Ruby Gem
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ master ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build + Publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
packages: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: 3.3.4
|
|
22
|
+
bundler: 2.5.11
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Set up the test database
|
|
26
|
+
run: |
|
|
27
|
+
bundle install
|
|
28
|
+
bundle exec rails db:setup
|
|
29
|
+
working-directory: spec/dummy
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: bundle exec rspec
|
|
33
|
+
|
|
34
|
+
- name: Publish to RubyGems
|
|
35
|
+
if: github.event_name == 'push'
|
|
36
|
+
run: |
|
|
37
|
+
mkdir -p $HOME/.gem
|
|
38
|
+
touch $HOME/.gem/credentials
|
|
39
|
+
chmod 0600 $HOME/.gem/credentials
|
|
40
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
|
41
|
+
gem build *.gemspec
|
|
42
|
+
gem push *.gem
|
|
43
|
+
env:
|
|
44
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.4
|
data/.travis.yml
CHANGED
|
@@ -3,8 +3,9 @@ sudo: false
|
|
|
3
3
|
language: ruby
|
|
4
4
|
cache: bundler
|
|
5
5
|
rvm:
|
|
6
|
-
-
|
|
7
|
-
before_install: gem install bundler -v
|
|
6
|
+
- 3.3.4
|
|
7
|
+
before_install: gem install bundler -v 2.5.11
|
|
8
8
|
script:
|
|
9
|
+
- (cd spec/dummy && bundle install)
|
|
9
10
|
- (cd spec/dummy && RAILS_ENV=test bundle exec rails db:setup)
|
|
10
11
|
- bundle exec rspec
|
data/Gemfile.lock
CHANGED
|
@@ -1,175 +1,249 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
json_api_filter (0.
|
|
4
|
+
json_api_filter (0.4.0)
|
|
5
5
|
activesupport (>= 3.0.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
actioncable (
|
|
11
|
-
actionpack (=
|
|
12
|
-
activesupport (=
|
|
10
|
+
actioncable (7.1.6)
|
|
11
|
+
actionpack (= 7.1.6)
|
|
12
|
+
activesupport (= 7.1.6)
|
|
13
13
|
nio4r (~> 2.0)
|
|
14
14
|
websocket-driver (>= 0.6.1)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
zeitwerk (~> 2.6)
|
|
16
|
+
actionmailbox (7.1.6)
|
|
17
|
+
actionpack (= 7.1.6)
|
|
18
|
+
activejob (= 7.1.6)
|
|
19
|
+
activerecord (= 7.1.6)
|
|
20
|
+
activestorage (= 7.1.6)
|
|
21
|
+
activesupport (= 7.1.6)
|
|
21
22
|
mail (>= 2.7.1)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
net-imap
|
|
24
|
+
net-pop
|
|
25
|
+
net-smtp
|
|
26
|
+
actionmailer (7.1.6)
|
|
27
|
+
actionpack (= 7.1.6)
|
|
28
|
+
actionview (= 7.1.6)
|
|
29
|
+
activejob (= 7.1.6)
|
|
30
|
+
activesupport (= 7.1.6)
|
|
27
31
|
mail (~> 2.5, >= 2.5.4)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
net-imap
|
|
33
|
+
net-pop
|
|
34
|
+
net-smtp
|
|
35
|
+
rails-dom-testing (~> 2.2)
|
|
36
|
+
actionpack (7.1.6)
|
|
37
|
+
actionview (= 7.1.6)
|
|
38
|
+
activesupport (= 7.1.6)
|
|
39
|
+
cgi
|
|
40
|
+
nokogiri (>= 1.8.5)
|
|
41
|
+
racc
|
|
42
|
+
rack (>= 2.2.4)
|
|
43
|
+
rack-session (>= 1.0.1)
|
|
33
44
|
rack-test (>= 0.6.3)
|
|
34
|
-
rails-dom-testing (~> 2.
|
|
35
|
-
rails-html-sanitizer (~> 1.
|
|
36
|
-
actiontext (
|
|
37
|
-
actionpack (=
|
|
38
|
-
activerecord (=
|
|
39
|
-
activestorage (=
|
|
40
|
-
activesupport (=
|
|
45
|
+
rails-dom-testing (~> 2.2)
|
|
46
|
+
rails-html-sanitizer (~> 1.6)
|
|
47
|
+
actiontext (7.1.6)
|
|
48
|
+
actionpack (= 7.1.6)
|
|
49
|
+
activerecord (= 7.1.6)
|
|
50
|
+
activestorage (= 7.1.6)
|
|
51
|
+
activesupport (= 7.1.6)
|
|
52
|
+
globalid (>= 0.6.0)
|
|
41
53
|
nokogiri (>= 1.8.5)
|
|
42
|
-
actionview (
|
|
43
|
-
activesupport (=
|
|
54
|
+
actionview (7.1.6)
|
|
55
|
+
activesupport (= 7.1.6)
|
|
44
56
|
builder (~> 3.1)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
rails-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
cgi
|
|
58
|
+
erubi (~> 1.11)
|
|
59
|
+
rails-dom-testing (~> 2.2)
|
|
60
|
+
rails-html-sanitizer (~> 1.6)
|
|
61
|
+
activejob (7.1.6)
|
|
62
|
+
activesupport (= 7.1.6)
|
|
50
63
|
globalid (>= 0.3.6)
|
|
51
|
-
activemodel (
|
|
52
|
-
activesupport (=
|
|
53
|
-
activerecord (
|
|
54
|
-
activemodel (=
|
|
55
|
-
activesupport (=
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
activesupport (
|
|
64
|
+
activemodel (7.1.6)
|
|
65
|
+
activesupport (= 7.1.6)
|
|
66
|
+
activerecord (7.1.6)
|
|
67
|
+
activemodel (= 7.1.6)
|
|
68
|
+
activesupport (= 7.1.6)
|
|
69
|
+
timeout (>= 0.4.0)
|
|
70
|
+
activestorage (7.1.6)
|
|
71
|
+
actionpack (= 7.1.6)
|
|
72
|
+
activejob (= 7.1.6)
|
|
73
|
+
activerecord (= 7.1.6)
|
|
74
|
+
activesupport (= 7.1.6)
|
|
75
|
+
marcel (~> 1.0)
|
|
76
|
+
activesupport (7.1.6)
|
|
77
|
+
base64
|
|
78
|
+
benchmark (>= 0.3)
|
|
79
|
+
bigdecimal
|
|
64
80
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
81
|
+
connection_pool (>= 2.2.5)
|
|
82
|
+
drb
|
|
65
83
|
i18n (>= 1.6, < 2)
|
|
84
|
+
logger (>= 1.4.2)
|
|
66
85
|
minitest (>= 5.1)
|
|
86
|
+
mutex_m
|
|
87
|
+
securerandom (>= 0.3)
|
|
67
88
|
tzinfo (~> 2.0)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
base64 (0.3.0)
|
|
90
|
+
benchmark (0.5.0)
|
|
91
|
+
bigdecimal (4.1.2)
|
|
92
|
+
builder (3.3.0)
|
|
93
|
+
byebug (13.0.0)
|
|
94
|
+
reline (>= 0.6.0)
|
|
95
|
+
cgi (0.5.2)
|
|
96
|
+
concurrent-ruby (1.3.8)
|
|
97
|
+
connection_pool (3.0.2)
|
|
98
|
+
crass (1.0.7)
|
|
99
|
+
date (3.5.1)
|
|
100
|
+
diff-lcs (1.6.2)
|
|
101
|
+
drb (2.2.3)
|
|
102
|
+
erb (6.0.7)
|
|
103
|
+
erubi (1.13.1)
|
|
104
|
+
globalid (1.4.0)
|
|
105
|
+
activesupport (>= 6.1)
|
|
106
|
+
i18n (1.15.2)
|
|
78
107
|
concurrent-ruby (~> 1.0)
|
|
79
|
-
|
|
108
|
+
io-console (0.9.2)
|
|
109
|
+
irb (1.18.0)
|
|
110
|
+
pp (>= 0.6.0)
|
|
111
|
+
prism (>= 1.3.0)
|
|
112
|
+
rdoc (>= 4.0.0)
|
|
113
|
+
reline (>= 0.4.2)
|
|
114
|
+
logger (1.7.0)
|
|
115
|
+
loofah (2.25.2)
|
|
80
116
|
crass (~> 1.0.2)
|
|
81
|
-
nokogiri (>= 1.
|
|
82
|
-
mail (2.
|
|
117
|
+
nokogiri (>= 1.12.0)
|
|
118
|
+
mail (2.9.1)
|
|
119
|
+
logger
|
|
83
120
|
mini_mime (>= 0.1.1)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
net-imap
|
|
122
|
+
net-pop
|
|
123
|
+
net-smtp
|
|
124
|
+
marcel (1.2.1)
|
|
125
|
+
mini_mime (1.1.5)
|
|
126
|
+
mini_portile2 (2.8.9)
|
|
127
|
+
minitest (6.0.6)
|
|
128
|
+
drb (~> 2.0)
|
|
129
|
+
prism (~> 1.5)
|
|
130
|
+
mutex_m (0.3.0)
|
|
131
|
+
net-imap (0.6.6)
|
|
132
|
+
date
|
|
133
|
+
net-protocol
|
|
134
|
+
net-pop (0.1.2)
|
|
135
|
+
net-protocol
|
|
136
|
+
net-protocol (0.3.0)
|
|
137
|
+
timeout
|
|
138
|
+
net-smtp (0.5.1)
|
|
139
|
+
net-protocol
|
|
140
|
+
nio4r (2.7.5)
|
|
141
|
+
nokogiri (1.19.4)
|
|
142
|
+
mini_portile2 (~> 2.8.2)
|
|
92
143
|
racc (~> 1.4)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
144
|
+
pp (0.6.4)
|
|
145
|
+
prettyprint
|
|
146
|
+
prettyprint (0.2.0)
|
|
147
|
+
prism (1.9.0)
|
|
148
|
+
racc (1.8.1)
|
|
149
|
+
rack (3.2.7)
|
|
150
|
+
rack-session (2.1.2)
|
|
151
|
+
base64 (>= 0.1.0)
|
|
152
|
+
rack (>= 3.0.0)
|
|
153
|
+
rack-test (2.2.0)
|
|
154
|
+
rack (>= 1.3)
|
|
155
|
+
rackup (2.3.1)
|
|
156
|
+
rack (>= 3)
|
|
157
|
+
rails (7.1.6)
|
|
158
|
+
actioncable (= 7.1.6)
|
|
159
|
+
actionmailbox (= 7.1.6)
|
|
160
|
+
actionmailer (= 7.1.6)
|
|
161
|
+
actionpack (= 7.1.6)
|
|
162
|
+
actiontext (= 7.1.6)
|
|
163
|
+
actionview (= 7.1.6)
|
|
164
|
+
activejob (= 7.1.6)
|
|
165
|
+
activemodel (= 7.1.6)
|
|
166
|
+
activerecord (= 7.1.6)
|
|
167
|
+
activestorage (= 7.1.6)
|
|
168
|
+
activesupport (= 7.1.6)
|
|
109
169
|
bundler (>= 1.15.0)
|
|
110
|
-
railties (=
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
170
|
+
railties (= 7.1.6)
|
|
171
|
+
rails-dom-testing (2.3.0)
|
|
172
|
+
activesupport (>= 5.0.0)
|
|
173
|
+
minitest
|
|
114
174
|
nokogiri (>= 1.6)
|
|
115
|
-
rails-html-sanitizer (1.
|
|
116
|
-
loofah (~> 2.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
175
|
+
rails-html-sanitizer (1.7.1)
|
|
176
|
+
loofah (~> 2.25, >= 2.25.2)
|
|
177
|
+
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)
|
|
178
|
+
railties (7.1.6)
|
|
179
|
+
actionpack (= 7.1.6)
|
|
180
|
+
activesupport (= 7.1.6)
|
|
181
|
+
cgi
|
|
182
|
+
irb
|
|
183
|
+
rackup (>= 1.0.0)
|
|
184
|
+
rake (>= 12.2)
|
|
185
|
+
thor (~> 1.0, >= 1.2.2)
|
|
186
|
+
tsort (>= 0.2)
|
|
187
|
+
zeitwerk (~> 2.6)
|
|
188
|
+
rake (13.4.2)
|
|
189
|
+
rbs (4.2.0)
|
|
190
|
+
logger
|
|
191
|
+
prism (>= 1.6.0)
|
|
192
|
+
tsort
|
|
193
|
+
rdoc (8.0.0)
|
|
194
|
+
erb
|
|
195
|
+
prism (>= 1.6.0)
|
|
196
|
+
rbs (>= 4.0.0)
|
|
197
|
+
tsort
|
|
198
|
+
reline (0.7.0)
|
|
199
|
+
io-console (~> 0.5)
|
|
200
|
+
rspec (3.13.2)
|
|
201
|
+
rspec-core (~> 3.13.0)
|
|
202
|
+
rspec-expectations (~> 3.13.0)
|
|
203
|
+
rspec-mocks (~> 3.13.0)
|
|
204
|
+
rspec-core (3.13.6)
|
|
205
|
+
rspec-support (~> 3.13.0)
|
|
206
|
+
rspec-expectations (3.13.5)
|
|
131
207
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
132
|
-
rspec-support (~> 3.
|
|
133
|
-
rspec-mocks (3.
|
|
208
|
+
rspec-support (~> 3.13.0)
|
|
209
|
+
rspec-mocks (3.13.8)
|
|
134
210
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
135
|
-
rspec-support (~> 3.
|
|
136
|
-
rspec-rails (
|
|
137
|
-
actionpack (>=
|
|
138
|
-
activesupport (>=
|
|
139
|
-
railties (>=
|
|
140
|
-
rspec-core (~> 3.
|
|
141
|
-
rspec-expectations (~> 3.
|
|
142
|
-
rspec-mocks (~> 3.
|
|
143
|
-
rspec-support (~> 3.
|
|
144
|
-
rspec-support (3.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
sqlite3 (1.4.2)
|
|
153
|
-
thor (1.1.0)
|
|
154
|
-
tzinfo (2.0.4)
|
|
211
|
+
rspec-support (~> 3.13.0)
|
|
212
|
+
rspec-rails (7.1.1)
|
|
213
|
+
actionpack (>= 7.0)
|
|
214
|
+
activesupport (>= 7.0)
|
|
215
|
+
railties (>= 7.0)
|
|
216
|
+
rspec-core (~> 3.13)
|
|
217
|
+
rspec-expectations (~> 3.13)
|
|
218
|
+
rspec-mocks (~> 3.13)
|
|
219
|
+
rspec-support (~> 3.13)
|
|
220
|
+
rspec-support (3.13.7)
|
|
221
|
+
securerandom (0.4.1)
|
|
222
|
+
sqlite3 (2.9.6)
|
|
223
|
+
mini_portile2 (~> 2.8.0)
|
|
224
|
+
thor (1.5.0)
|
|
225
|
+
timeout (0.6.1)
|
|
226
|
+
tsort (0.2.0)
|
|
227
|
+
tzinfo (2.0.6)
|
|
155
228
|
concurrent-ruby (~> 1.0)
|
|
156
|
-
websocket-driver (0.
|
|
229
|
+
websocket-driver (0.8.2)
|
|
230
|
+
base64
|
|
157
231
|
websocket-extensions (>= 0.1.0)
|
|
158
232
|
websocket-extensions (0.1.5)
|
|
159
|
-
zeitwerk (2.
|
|
233
|
+
zeitwerk (2.8.3)
|
|
160
234
|
|
|
161
235
|
PLATFORMS
|
|
162
236
|
ruby
|
|
163
237
|
|
|
164
238
|
DEPENDENCIES
|
|
165
|
-
bundler (~>
|
|
239
|
+
bundler (~> 2.5)
|
|
166
240
|
byebug
|
|
167
241
|
json_api_filter!
|
|
168
|
-
rails
|
|
242
|
+
rails (>= 7.1, < 7.2)
|
|
169
243
|
rake (~> 13.0)
|
|
170
244
|
rspec (>= 3.0)
|
|
171
245
|
rspec-rails
|
|
172
246
|
sqlite3
|
|
173
247
|
|
|
174
248
|
BUNDLED WITH
|
|
175
|
-
|
|
249
|
+
2.5.11
|
data/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Filter for rails controller based on JsonAPI spec: `/books?filter[library_id]=1,2&filter[author_id][eq]=12&filter[created_at][gt]=2021-02-02`
|
|
4
4
|
|
|
5
|
-
[](https://codeclimate.com/github/evalmee/json_api_filter/maintainability)
|
|
6
6
|
[](https://badge.fury.io/rb/json_api_filter)
|
|
7
|
-
[](https://travis-ci.com/evalmee/json_api_filter)
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ $ gem install json_api_filter
|
|
|
28
28
|
|
|
29
29
|
### Quick start
|
|
30
30
|
|
|
31
|
-
To filter this request `/books?filter[library_id]=1,2&filter[author_id]=12&search=Lord of the ring`
|
|
31
|
+
To filter this request `/books?filter[library_id]=1,2&filter[author_id]=12&search=Lord of the ring&include=users,users.posts`
|
|
32
32
|
|
|
33
33
|
```ruby
|
|
34
34
|
class Book < ApplicationController
|
|
@@ -36,19 +36,114 @@ class Book < ApplicationController
|
|
|
36
36
|
include JsonApiFilter
|
|
37
37
|
permitted_filters %i[library_id author_id]
|
|
38
38
|
permitted_searches :user_search
|
|
39
|
-
|
|
39
|
+
permitted_inclusions %i[users users.posts]
|
|
40
|
+
|
|
40
41
|
def index
|
|
41
42
|
@books = json_api_filter(Book, params)
|
|
43
|
+
inclusions = json_api_inclusions(params) # returns [:users, :'users.posts']
|
|
44
|
+
|
|
45
|
+
# then use `inclusions` in the serialiser
|
|
46
|
+
BookSerializer.new(@books, include: inclusions).serializable_hash.to_json
|
|
42
47
|
end
|
|
43
|
-
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
```
|
|
47
51
|
|
|
48
|
-
- `permitted_filters` let you define allowed attributes to filter on (mandatory)
|
|
52
|
+
- `permitted_filters` let you define allowed attributes to filter on (mandatory unless a custom filter is declared)
|
|
49
53
|
- `permitted_searches` let you define the allowed search method defined in you model what will be called if you pass `search` params in your request (can be a pg_search scope)
|
|
54
|
+
- `permitted_inclusions` let you define the allowed inclusions
|
|
50
55
|
- `json_api_filter(scope, params)` return an active record relation (`Book::` in this example)
|
|
56
|
+
|
|
57
|
+
## Custom filters
|
|
58
|
+
|
|
59
|
+
Use `filter` when the public filter name or values do not map directly to a database column. Custom filters are
|
|
60
|
+
permitted by their declaration and do not also belong in `permitted_filters`.
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
TYPE_MAP = {
|
|
64
|
+
"online" => "exam",
|
|
65
|
+
"paper" => "paper",
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
filter :type do
|
|
69
|
+
eq do |scope, values|
|
|
70
|
+
mapped_values = values.map { |value| TYPE_MAP[value] }
|
|
71
|
+
|
|
72
|
+
mapped_values.any?(&:nil?) ? scope.none : scope.where(quiz_type: mapped_values)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Direct syntax uses the `eq` handler, so both of these requests call the block above:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
/exams?filter[type]=online
|
|
81
|
+
/exams?filter[type][eq]=online
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Values are always passed as an array. Comma-separated values are split before the handler is called, so
|
|
85
|
+
`filter[type]=online,paper` passes `["online", "paper"]`.
|
|
86
|
+
|
|
87
|
+
The supported custom operators are the operators already understood by the gem: `eq`, `ne`, `gt`, `ge`, `lt`, and
|
|
88
|
+
`le`. Only declared operator blocks run. A custom filter never falls back to treating its public name as a database
|
|
89
|
+
column. Every handler must return an Active Record scope.
|
|
90
|
+
|
|
91
|
+
# Use inclusions in serializers
|
|
92
|
+
|
|
93
|
+
# Handling errors
|
|
94
|
+
|
|
95
|
+
If an endpoint does not support the include parameter, it MUST respond with 400 Bad Request to any requests that include it.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
class Book < ApplicationController
|
|
99
|
+
|
|
100
|
+
include JsonApiFilter
|
|
101
|
+
permitted_filters %i[library_id author_id]
|
|
102
|
+
permitted_searches :user_search
|
|
103
|
+
|
|
104
|
+
rescue_from JsonApiFilter::MissingPermittedInclusionError, with: :render_400
|
|
105
|
+
|
|
106
|
+
def index
|
|
107
|
+
@books = json_api_filter(Book, params)
|
|
108
|
+
inclusions = json_api_inclusions(params) # raises JsonApiFilter::MissingPermittedInclusionError
|
|
109
|
+
end
|
|
51
110
|
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def render_400(exception)
|
|
114
|
+
render json: exception, status: 400
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If a server is unable to identify a relationship path or does not support inclusion of resources from a path, it MUST respond with 400 Bad Request.
|
|
120
|
+
This request should return a 400 status:
|
|
121
|
+
|
|
122
|
+
`/books?filter[library_id]=1,2&filter[author_id]=12&search=Lord of the ring&include=users,users.posts, users.addresses`
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
class Book < ApplicationController
|
|
126
|
+
|
|
127
|
+
include JsonApiFilter
|
|
128
|
+
permitted_filters %i[library_id author_id]
|
|
129
|
+
permitted_searches :user_search
|
|
130
|
+
permitted_inclusions %i[users users.posts]
|
|
131
|
+
|
|
132
|
+
rescue_from JsonApiFilter::UnknownInclusionsError, with: :render_400
|
|
133
|
+
|
|
134
|
+
def index
|
|
135
|
+
@books = json_api_filter(Book, params)
|
|
136
|
+
inclusions = json_api_inclusions(params) # raises JsonApiFilter::UnknownInclusionsError
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def render_400(exception)
|
|
142
|
+
render json: exception, status: 400
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
```
|
|
146
|
+
|
|
52
147
|
## Migration from 0.1
|
|
53
148
|
0.2.x version is not compatible with 0.1
|
|
54
149
|
In your controller, you will have to replace all occurrences of `attr_filter` as bellow :
|
data/json_api_filter.gemspec
CHANGED
|
@@ -11,8 +11,9 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
|
|
12
12
|
spec.summary = "Provide a filter for jsonAPI Controller in Rails"
|
|
13
13
|
spec.description = ""
|
|
14
|
-
spec.homepage = "https://github.com/
|
|
14
|
+
spec.homepage = "https://github.com/evalmee/json_api_filter"
|
|
15
15
|
spec.license = "MIT"
|
|
16
|
+
spec.required_ruby_version = ">= 3.3.4"
|
|
16
17
|
|
|
17
18
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
18
19
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
@@ -20,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
|
20
21
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
21
22
|
|
|
22
23
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
-
spec.metadata["source_code_uri"] = "https://github.com/
|
|
24
|
+
spec.metadata["source_code_uri"] = "https://github.com/evalmee/json_api_filter"
|
|
24
25
|
else
|
|
25
26
|
raise "RubyGems 2.0 or newer is required to protect against " \
|
|
26
27
|
"public gem pushes."
|
|
@@ -35,12 +36,12 @@ Gem::Specification.new do |spec|
|
|
|
35
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
36
37
|
spec.require_paths = ["lib"]
|
|
37
38
|
|
|
38
|
-
spec.add_development_dependency "bundler", "~>
|
|
39
|
+
spec.add_development_dependency "bundler", "~> 2.5"
|
|
39
40
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
40
41
|
spec.add_development_dependency "rspec", ">= 3.0"
|
|
41
42
|
spec.add_development_dependency "rspec-rails"
|
|
42
43
|
spec.add_development_dependency "sqlite3"
|
|
43
|
-
spec.add_development_dependency 'rails'
|
|
44
|
+
spec.add_development_dependency 'rails', '>= 7.1', '< 7.2'
|
|
44
45
|
spec.add_development_dependency 'byebug'
|
|
45
46
|
spec.add_dependency "activesupport", ">= 3.0.0"
|
|
46
|
-
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module JsonApiFilter
|
|
2
|
+
class CustomFilter
|
|
3
|
+
OPERATORS = %i[eq ne gt ge lt le].freeze
|
|
4
|
+
|
|
5
|
+
attr_reader :handlers
|
|
6
|
+
|
|
7
|
+
def initialize(&definition)
|
|
8
|
+
@handlers = {}
|
|
9
|
+
instance_eval(&definition)
|
|
10
|
+
@handlers.freeze
|
|
11
|
+
freeze
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
OPERATORS.each do |operator|
|
|
15
|
+
define_method(operator) do |&handler|
|
|
16
|
+
handlers[operator.to_s] = handler
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def handler_for(operator)
|
|
21
|
+
handlers[operator.to_s]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
module JsonApiFilter
|
|
2
2
|
class Dispatch
|
|
3
3
|
|
|
4
|
-
attr_reader :params, :scope, :allowed_filters, :allowed_searches
|
|
4
|
+
attr_reader :params, :scope, :allowed_filters, :allowed_searches, :custom_filters
|
|
5
5
|
|
|
6
6
|
# @param [ActiveRecord::Base] scope
|
|
7
7
|
# @param [Hash, ActionController::Parameters] params
|
|
8
8
|
# @param [Array<Symbol>] allowed_filters
|
|
9
|
-
def initialize(scope, params, allowed_filters:, allowed_searches:)
|
|
9
|
+
def initialize(scope, params, allowed_filters:, allowed_searches:, custom_filters: {})
|
|
10
10
|
@params = params
|
|
11
11
|
@scope = scope
|
|
12
12
|
@allowed_filters = allowed_filters
|
|
13
13
|
@allowed_searches = allowed_searches
|
|
14
|
+
@custom_filters = custom_filters
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
# @return [ActiveRecord_Relation]
|
|
@@ -32,6 +33,8 @@ module JsonApiFilter
|
|
|
32
33
|
def filters_predicate
|
|
33
34
|
#TODO: split this method
|
|
34
35
|
parser_params.fetch('filter', {}).map do |key, value|
|
|
36
|
+
custom_filter = custom_filters[key.to_s]
|
|
37
|
+
next custom_filter_predicate(custom_filter, value) if custom_filter
|
|
35
38
|
next unless filters.include?(key)
|
|
36
39
|
|
|
37
40
|
if nested_filters.include?(key.to_sym)
|
|
@@ -39,19 +42,45 @@ module JsonApiFilter
|
|
|
39
42
|
scope,
|
|
40
43
|
{key => value},
|
|
41
44
|
association: true
|
|
42
|
-
)
|
|
45
|
+
).predicate
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
if value.class != ActiveSupport::HashWithIndifferentAccess
|
|
46
|
-
next ::JsonApiFilter::FieldFilters::Matcher.new(scope, {key => value})
|
|
49
|
+
next ::JsonApiFilter::FieldFilters::Matcher.new(scope, {key => value}).predicate
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
::JsonApiFilter::FieldFilters::Compare.new(
|
|
50
53
|
scope,
|
|
51
54
|
{key => value},
|
|
52
55
|
allowed_searches: allowed_searches
|
|
53
|
-
)
|
|
54
|
-
end.compact.
|
|
56
|
+
).predicate
|
|
57
|
+
end.compact.reduce(&:merge)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def custom_filter_predicate(custom_filter, value)
|
|
61
|
+
predicates = if value.respond_to?(:each_pair)
|
|
62
|
+
value.map do |operator, operator_value|
|
|
63
|
+
apply_custom_filter(custom_filter, operator, operator_value)
|
|
64
|
+
end
|
|
65
|
+
else
|
|
66
|
+
[apply_custom_filter(custom_filter, :eq, value)]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
predicates.compact.reduce(&:merge)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def apply_custom_filter(custom_filter, operator, value)
|
|
73
|
+
handler = custom_filter.handler_for(operator)
|
|
74
|
+
return unless handler
|
|
75
|
+
|
|
76
|
+
handler.call(scope, custom_filter_values(value))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def custom_filter_values(value)
|
|
80
|
+
return value if value.is_a?(Array)
|
|
81
|
+
return ::JsonApiFilter::ValueParser.parse(value) if value.is_a?(String)
|
|
82
|
+
|
|
83
|
+
[value]
|
|
55
84
|
end
|
|
56
85
|
|
|
57
86
|
# @return [ActiveRecord::Base, NilClass]
|
data/lib/json_api_filter.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "json_api_filter/version"
|
|
2
2
|
require "json_api_filter/auto_join"
|
|
3
|
+
require "json_api_filter/custom_filter"
|
|
3
4
|
require "json_api_filter/dispatch"
|
|
4
5
|
require "json_api_filter/filter_attributes"
|
|
5
6
|
require "json_api_filter/value_parser"
|
|
@@ -19,12 +20,30 @@ module JsonApiFilter
|
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
class MissingPermittedInclusionError < ::StandardError
|
|
24
|
+
def message
|
|
25
|
+
"PERMITTED_INCLUSIONS are required"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class UnknownInclusionsError < ::StandardError
|
|
30
|
+
attr_reader :item
|
|
31
|
+
def initialize(item)
|
|
32
|
+
super(message)
|
|
33
|
+
@item = item
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def message
|
|
37
|
+
"Unable to identify #{item} as a relationship path or inclusion"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
22
41
|
extend ::ActiveSupport::Concern
|
|
23
42
|
included do
|
|
24
43
|
|
|
25
44
|
# @param [ActiveRecord::Base] scope
|
|
26
45
|
def json_api_filter(scope, query_params = params)
|
|
27
|
-
unless self.class.json_api_permitted_filters.present?
|
|
46
|
+
unless self.class.json_api_permitted_filters.present? || self.class.json_api_custom_filters.present?
|
|
28
47
|
raise ::JsonApiFilter::MissingPermittedFilterError
|
|
29
48
|
end
|
|
30
49
|
|
|
@@ -32,10 +51,22 @@ module JsonApiFilter
|
|
|
32
51
|
scope,
|
|
33
52
|
query_params,
|
|
34
53
|
allowed_filters: self.class.json_api_permitted_filters,
|
|
35
|
-
allowed_searches: self.class.json_api_permitted_searches
|
|
54
|
+
allowed_searches: self.class.json_api_permitted_searches,
|
|
55
|
+
custom_filters: self.class.json_api_custom_filters
|
|
36
56
|
).process
|
|
37
57
|
end
|
|
38
58
|
|
|
59
|
+
def json_api_inclusions(params)
|
|
60
|
+
inclusions_params = params.fetch(:include, "").split(",").map(&:to_sym).uniq
|
|
61
|
+
unknown_inclusion = inclusions_params.find { |resource| json_api_permitted_inclusions.exclude?(resource) }
|
|
62
|
+
|
|
63
|
+
# If a server is unable to identify a relationship path or does not support inclusion of resources from a path,
|
|
64
|
+
# it MUST respond with 400 Bad Request.
|
|
65
|
+
raise UnknownInclusionsError, unknown_inclusion if unknown_inclusion.present?
|
|
66
|
+
|
|
67
|
+
inclusions_params
|
|
68
|
+
end
|
|
69
|
+
|
|
39
70
|
def self.permitted_filters(val)
|
|
40
71
|
define_singleton_method(:json_api_permitted_filters) do
|
|
41
72
|
val
|
|
@@ -46,6 +77,20 @@ module JsonApiFilter
|
|
|
46
77
|
[]
|
|
47
78
|
end
|
|
48
79
|
|
|
80
|
+
def self.filter(name, &definition)
|
|
81
|
+
filters = json_api_custom_filters.merge(
|
|
82
|
+
name.to_s => ::JsonApiFilter::CustomFilter.new(&definition)
|
|
83
|
+
).freeze
|
|
84
|
+
|
|
85
|
+
define_singleton_method(:json_api_custom_filters) do
|
|
86
|
+
filters
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.json_api_custom_filters
|
|
91
|
+
{}.freeze
|
|
92
|
+
end
|
|
93
|
+
|
|
49
94
|
def self.permitted_searches(global, **columns)
|
|
50
95
|
define_singleton_method(:json_api_permitted_searches) do
|
|
51
96
|
{ global: global, columns: columns }
|
|
@@ -55,5 +100,21 @@ module JsonApiFilter
|
|
|
55
100
|
def self.json_api_permitted_searches
|
|
56
101
|
{}
|
|
57
102
|
end
|
|
103
|
+
|
|
104
|
+
def self.permitted_inclusions(inclusions)
|
|
105
|
+
define_singleton_method(:json_api_permitted_inclusions) do
|
|
106
|
+
inclusions
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# If an endpoint does not support the include parameter,
|
|
111
|
+
# it MUST respond with 400 Bad Request to any requests that include it.
|
|
112
|
+
def self.json_api_permitted_inclusions
|
|
113
|
+
raise MissingPermittedInclusionError
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def json_api_permitted_inclusions
|
|
117
|
+
self.class.json_api_permitted_inclusions
|
|
118
|
+
end
|
|
58
119
|
end
|
|
59
120
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json_api_filter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Blaked
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-09-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '2.5'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '2.5'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,14 +86,20 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
89
|
+
version: '7.1'
|
|
90
|
+
- - "<"
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '7.2'
|
|
90
93
|
type: :development
|
|
91
94
|
prerelease: false
|
|
92
95
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
96
|
requirements:
|
|
94
97
|
- - ">="
|
|
95
98
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
99
|
+
version: '7.1'
|
|
100
|
+
- - "<"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '7.2'
|
|
97
103
|
- !ruby/object:Gem::Dependency
|
|
98
104
|
name: byebug
|
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -129,8 +135,10 @@ executables: []
|
|
|
129
135
|
extensions: []
|
|
130
136
|
extra_rdoc_files: []
|
|
131
137
|
files:
|
|
138
|
+
- ".github/workflows/gem-push.yml"
|
|
132
139
|
- ".gitignore"
|
|
133
140
|
- ".rspec"
|
|
141
|
+
- ".ruby-version"
|
|
134
142
|
- ".travis.yml"
|
|
135
143
|
- CODE_OF_CONDUCT.md
|
|
136
144
|
- Gemfile
|
|
@@ -143,6 +151,7 @@ files:
|
|
|
143
151
|
- json_api_filter.gemspec
|
|
144
152
|
- lib/json_api_filter.rb
|
|
145
153
|
- lib/json_api_filter/auto_join.rb
|
|
154
|
+
- lib/json_api_filter/custom_filter.rb
|
|
146
155
|
- lib/json_api_filter/dispatch.rb
|
|
147
156
|
- lib/json_api_filter/field_filters/base.rb
|
|
148
157
|
- lib/json_api_filter/field_filters/compare.rb
|
|
@@ -153,14 +162,14 @@ files:
|
|
|
153
162
|
- lib/json_api_filter/filter_attributes.rb
|
|
154
163
|
- lib/json_api_filter/value_parser.rb
|
|
155
164
|
- lib/json_api_filter/version.rb
|
|
156
|
-
homepage: https://github.com/
|
|
165
|
+
homepage: https://github.com/evalmee/json_api_filter
|
|
157
166
|
licenses:
|
|
158
167
|
- MIT
|
|
159
168
|
metadata:
|
|
160
169
|
allowed_push_host: https://rubygems.org
|
|
161
|
-
homepage_uri: https://github.com/
|
|
162
|
-
source_code_uri: https://github.com/
|
|
163
|
-
post_install_message:
|
|
170
|
+
homepage_uri: https://github.com/evalmee/json_api_filter
|
|
171
|
+
source_code_uri: https://github.com/evalmee/json_api_filter
|
|
172
|
+
post_install_message:
|
|
164
173
|
rdoc_options: []
|
|
165
174
|
require_paths:
|
|
166
175
|
- lib
|
|
@@ -168,15 +177,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
168
177
|
requirements:
|
|
169
178
|
- - ">="
|
|
170
179
|
- !ruby/object:Gem::Version
|
|
171
|
-
version:
|
|
180
|
+
version: 3.3.4
|
|
172
181
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
182
|
requirements:
|
|
174
183
|
- - ">="
|
|
175
184
|
- !ruby/object:Gem::Version
|
|
176
185
|
version: '0'
|
|
177
186
|
requirements: []
|
|
178
|
-
rubygems_version: 3.
|
|
179
|
-
signing_key:
|
|
187
|
+
rubygems_version: 3.5.11
|
|
188
|
+
signing_key:
|
|
180
189
|
specification_version: 4
|
|
181
190
|
summary: Provide a filter for jsonAPI Controller in Rails
|
|
182
191
|
test_files: []
|