jsonapi.rb 1.2.1 → 1.3.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/Gemfile.lock +1 -1
- data/README.md +12 -0
- data/lib/jsonapi/filtering.rb +41 -19
- data/lib/jsonapi/patches.rb +69 -0
- data/lib/jsonapi/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84c4daefc4822fcbaf912d9bbf500fa8db752bc2
|
4
|
+
data.tar.gz: 3a338f497562d61ee01ccdb889fa309ccc2ebbdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0da896b2305034b5f448b440db0b93ec67155fdfc707c535328c045c679686b113de9511dd5d9761cf1cced8230776ab47ed85914258440a5b7d76e15e203254
|
7
|
+
data.tar.gz: 8f49760c1227c2017b74106fd3b47b64382aa46892114b54ba4db525fabe384ce6fd49ebe87bfdee69afbdec0aa0263ae39b249b0ceed78b2c2643f5fdde8088
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -195,6 +195,18 @@ $ curl -X GET \
|
|
195
195
|
&sort=-model_attr,relationship_attr
|
196
196
|
```
|
197
197
|
|
198
|
+
#### Sorting using expressions
|
199
|
+
|
200
|
+
You can use basic aggregations like `min`, `max`, `avg`, `sum` and `count`
|
201
|
+
when sorting. This is an optional feature since SQL aggregations require
|
202
|
+
grouping. To enable expressions along with filters, use the option flags:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
options = { sort_with_expressions: true }
|
206
|
+
jsonapi_filter(User.all, allowed_fields, options) do |filtered|
|
207
|
+
render jsonapi: result.group('id').to_a
|
208
|
+
end
|
209
|
+
```
|
198
210
|
|
199
211
|
### Pagination
|
200
212
|
|
data/lib/jsonapi/filtering.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
require 'ransack/predicate'
|
2
|
+
require_relative 'patches'
|
2
3
|
|
3
4
|
# Filtering and sorting support
|
4
5
|
module JSONAPI
|
5
6
|
module Filtering
|
7
|
+
# Parses and returns the attribute and the predicate of a ransack field
|
8
|
+
#
|
9
|
+
# @param requested_field [String] the field to parse
|
10
|
+
# @return [Array] with the fields and the predicate
|
11
|
+
def self.extract_attributes_and_predicate(requested_field)
|
12
|
+
field_name = requested_field.to_s.dup
|
13
|
+
predicate = Ransack::Predicate.detect_and_strip_from_string!(field_name)
|
14
|
+
predicate = Ransack::Predicate.named(predicate)
|
15
|
+
|
16
|
+
[field_name.split(/_and_|_or_/), predicate]
|
17
|
+
end
|
18
|
+
|
6
19
|
private
|
7
20
|
|
8
21
|
# Applies filtering and sorting to a set of resources if requested
|
@@ -13,10 +26,12 @@ module JSONAPI
|
|
13
26
|
# Ex.: `GET /resource?filter[region_matches_any]=Lisb%&sort=-created_at,id`
|
14
27
|
#
|
15
28
|
# @param allowed_fields [Array] a list of allowed fields to be filtered
|
29
|
+
# @param options [Hash] extra flags to enable/disable features
|
16
30
|
# @return [ActiveRecord::Base] a collection of resources
|
17
|
-
def jsonapi_filter(resources, allowed_fields)
|
31
|
+
def jsonapi_filter(resources, allowed_fields, options = {})
|
32
|
+
allowed_fields = allowed_fields.map(&:to_s)
|
18
33
|
extracted_params = jsonapi_filter_params(allowed_fields)
|
19
|
-
extracted_params[:sorts] = jsonapi_sort_params(allowed_fields)
|
34
|
+
extracted_params[:sorts] = jsonapi_sort_params(allowed_fields, options)
|
20
35
|
resources = resources.ransack(extracted_params)
|
21
36
|
block_given? ? yield(resources) : resources
|
22
37
|
end
|
@@ -34,11 +49,8 @@ module JSONAPI
|
|
34
49
|
allowed_fields = allowed_fields.map(&:to_s)
|
35
50
|
|
36
51
|
requested.each_pair do |requested_field, to_filter|
|
37
|
-
|
38
|
-
|
39
|
-
predicate = Ransack::Predicate.named(predicate)
|
40
|
-
|
41
|
-
field_names = field_name.split(/_and_|_or_/)
|
52
|
+
field_names, predicate = JSONAPI::Filtering
|
53
|
+
.extract_attributes_and_predicate(requested_field)
|
42
54
|
|
43
55
|
if to_filter.is_a?(String) && to_filter.include?(',')
|
44
56
|
to_filter = to_filter.split(',')
|
@@ -52,24 +64,34 @@ module JSONAPI
|
|
52
64
|
filtered
|
53
65
|
end
|
54
66
|
|
55
|
-
# Extracts and whitelists allowed fields to be sorted
|
67
|
+
# Extracts and whitelists allowed fields (or expressions) to be sorted
|
56
68
|
#
|
57
69
|
# @param allowed_fields [Array] a list of allowed fields to be sorted
|
70
|
+
# @param options [Hash] extra options to enable/disable features
|
58
71
|
# @return [Hash] to be passed to [ActiveRecord::Base#order]
|
59
|
-
def jsonapi_sort_params(allowed_fields)
|
72
|
+
def jsonapi_sort_params(allowed_fields, options = {})
|
73
|
+
filtered = []
|
60
74
|
requested = params[:sort].to_s.split(',')
|
61
|
-
requested.map! do |requested_field|
|
62
|
-
desc = requested_field.to_s.start_with?('-')
|
63
|
-
[
|
64
|
-
desc ? requested_field[1..-1] : requested_field,
|
65
|
-
desc ? 'desc' : 'asc'
|
66
|
-
]
|
67
|
-
end
|
68
75
|
|
69
|
-
|
70
|
-
|
71
|
-
|
76
|
+
requested.each do |requested_field|
|
77
|
+
if requested_field.to_s.start_with?('-')
|
78
|
+
dir = 'desc'
|
79
|
+
requested_field = requested_field[1..-1]
|
80
|
+
else
|
81
|
+
dir = 'asc'
|
82
|
+
end
|
83
|
+
|
84
|
+
field_names, predicate = JSONAPI::Filtering
|
85
|
+
.extract_attributes_and_predicate(requested_field)
|
86
|
+
|
87
|
+
next unless (field_names - allowed_fields).empty?
|
88
|
+
next if !options[:sort_with_expressions] && predicate
|
89
|
+
|
90
|
+
# Convert to strings instead of hashes to allow joined table columns.
|
91
|
+
filtered << [requested_field, dir].join(' ')
|
72
92
|
end
|
93
|
+
|
94
|
+
filtered
|
73
95
|
end
|
74
96
|
end
|
75
97
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'ransack'
|
2
|
+
|
3
|
+
Ransack.configure do |config|
|
4
|
+
# Raise errors if a query contains an unknown predicate or attribute.
|
5
|
+
# Default is true (do not raise error on unknown conditions).
|
6
|
+
config.ignore_unknown_conditions = true
|
7
|
+
|
8
|
+
# Enable expressions
|
9
|
+
# See: https://www.rubydoc.info/github/rails/rails/Arel/Expressions
|
10
|
+
config.add_predicate(
|
11
|
+
'count', arel_predicate: 'count',
|
12
|
+
validator: ->(_) { true }, compounds: false
|
13
|
+
)
|
14
|
+
config.add_predicate(
|
15
|
+
'count_distinct', arel_predicate: 'count',
|
16
|
+
validator: ->(_) { true }, formatter: ->(_) { true }, compounds: false
|
17
|
+
)
|
18
|
+
config.add_predicate(
|
19
|
+
'sum', arel_predicate: 'sum',
|
20
|
+
validator: ->(v) { true }, compounds: false
|
21
|
+
)
|
22
|
+
config.add_predicate(
|
23
|
+
'avg', arel_predicate: 'average',
|
24
|
+
validator: ->(v) { true }, compounds: false
|
25
|
+
)
|
26
|
+
config.add_predicate(
|
27
|
+
'min', arel_predicate: 'minimum',
|
28
|
+
validator: ->(v) { true }, compounds: false
|
29
|
+
)
|
30
|
+
config.add_predicate(
|
31
|
+
'max', arel_predicate: 'maximum',
|
32
|
+
validator: ->(v) { true }, compounds: false
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
Ransack::Visitor.class_eval do
|
38
|
+
alias_method :original_visit_Ransack_Nodes_Sort, :visit_Ransack_Nodes_Sort
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Original method assumes sorting is done only by attributes
|
43
|
+
def visit_Ransack_Nodes_Sort(node)
|
44
|
+
# Try the default sorting visitor method...
|
45
|
+
binded = original_visit_Ransack_Nodes_Sort(node)
|
46
|
+
valid = (binded.valid? if binded.respond_to?(:valid?)) || true
|
47
|
+
return binded if binded.present? && valid
|
48
|
+
|
49
|
+
# Fallback to support the expressions...
|
50
|
+
binded = Ransack::Nodes::Condition.extract(node.context, node.name, nil)
|
51
|
+
valid = (binded.valid? if binded.respond_to?(:valid?)) || true
|
52
|
+
binded.arel_predicate if binded.present? && valid
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Ransack::Nodes::Condition.class_eval do
|
57
|
+
alias_method :original_format_predicate, :format_predicate
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Original method doesn't respect the arity of expressions
|
62
|
+
# See: lib/ransack/adapters/active_record/ransack/nodes/condition.rb#L30-L42
|
63
|
+
def format_predicate(attribute)
|
64
|
+
original_format_predicate(attribute)
|
65
|
+
rescue ArgumentError
|
66
|
+
arel_pred = arel_predicate_for_attribute(attribute)
|
67
|
+
attribute.attr.public_send(arel_pred)
|
68
|
+
end
|
69
|
+
end
|
data/lib/jsonapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stas Suscov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fast_jsonapi
|
@@ -217,6 +217,7 @@ files:
|
|
217
217
|
- lib/jsonapi/fetching.rb
|
218
218
|
- lib/jsonapi/filtering.rb
|
219
219
|
- lib/jsonapi/pagination.rb
|
220
|
+
- lib/jsonapi/patches.rb
|
220
221
|
- lib/jsonapi/rails.rb
|
221
222
|
- lib/jsonapi/version.rb
|
222
223
|
homepage: https://github.com/stas/jsonapi.rb
|