sort_param 0.1.0 → 1.0.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/CHANGELOG.md +6 -1
- data/README.md +94 -5
- data/lib/sort_param/definition.rb +3 -2
- data/lib/sort_param/field.rb +2 -2
- data/lib/sort_param/formatters/formatter.rb +8 -0
- data/lib/sort_param/formatters/hash.rb +9 -5
- data/lib/sort_param/formatters/mysql.rb +1 -1
- data/lib/sort_param/formatters/pg.rb +1 -2
- data/lib/sort_param/utilities.rb +15 -5
- data/lib/sort_param/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78cff5e96b34b0697aa50227db7186cbbbaac54290cdaf88ff6201ae35afda6e
|
4
|
+
data.tar.gz: 025b26afa452522750032d2be80f83dca0c0343ff00416e2a6d2299e47680980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c191c86a31f9a9c300ab3064d3b44b9be8f273fc4578de139482f7434bb07b5c69d44ef12077b262a2832757450f7ded50ced460e501cf602328a29e6e2d2ad
|
7
|
+
data.tar.gz: 788302acfbcf086d426fe2adebb1c5ee59aa85ece7e5e78e956e6ea81987b04efef002ed052d7b0615736648b17552a46c7a5d85c44e94a2f0d6f416ebf4b94e
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,31 @@
|
|
1
1
|
# SortParam
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/sort_param) [](https://github.com/jsonb-uy/sort_param/actions/workflows/ruby.yml) [](https://codecov.io/gh/jsonb-uy/sort_param) [](https://codeclimate.com/github/jsonb-uy/sort_param/maintainability)
|
4
|
+
|
5
|
+
Sort records using a query parameter based on JSON API's sort parameter format.
|
6
|
+
|
7
|
+
In a nutshell, this gem converts comma-separated sort fields from this:
|
8
|
+
<pre>
|
9
|
+
?sort=<b>+users.first_name,-users.last_name:nulls_last,users.email</b>
|
10
|
+
</pre>
|
11
|
+
|
12
|
+
|
13
|
+
to this:
|
14
|
+
```SQL
|
15
|
+
users.first_name asc, users.last_name desc nulls last, users.email asc
|
16
|
+
```
|
17
|
+
|
18
|
+
or to this:
|
19
|
+
```ruby
|
20
|
+
{"users.first_name"=>{:direction=>:asc}, "users.last_name"=>{:direction=>:desc, :nulls=>:last}, "users.email"=>{:direction=>:asc}}
|
21
|
+
```
|
4
22
|
|
5
23
|
## Features
|
6
24
|
|
25
|
+
* Sort field whitelisting.
|
7
26
|
* Supports `ORDER BY` expression generation for MySQL and PG.
|
8
|
-
*
|
27
|
+
* Parsing of comma-separated sort fields into hash for any further processing.
|
28
|
+
* Specifying `NULL` sort order.
|
9
29
|
|
10
30
|
## Installation
|
11
31
|
|
@@ -80,6 +100,21 @@ sort_param.load!("+first_name,-last_name")
|
|
80
100
|
=> {"first_name"=>{:nulls=>:first, :direction=>:asc}, "last_name"=>{:direction=>:desc}}
|
81
101
|
```
|
82
102
|
|
103
|
+
Any other additional column option set in `SortParam::Definition` or `SortParam.define` will be included in the column's hash value.
|
104
|
+
For example:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
sort_param = SortParam.define do
|
108
|
+
field :first_name, foo: :bar, nulls: :first
|
109
|
+
end
|
110
|
+
|
111
|
+
sort_param.load!("+first_name")
|
112
|
+
=> {"first_name"=>{:foo=>:bar, :nulls=>:first, :direction=>:asc}}
|
113
|
+
|
114
|
+
sort_param.load!("-first_name:nulls_last")
|
115
|
+
=> {"first_name"=>{:foo=>:bar, :nulls=>:last, :direction=>:desc}}
|
116
|
+
```
|
117
|
+
|
83
118
|
#### IV. Example with explicit nulls sort order
|
84
119
|
|
85
120
|
###### Example in PG mode:
|
@@ -91,16 +126,36 @@ sort_param.load!("+first_name:nulls_last,-last_name:nulls_first", mode: :pg)
|
|
91
126
|
```
|
92
127
|
<br/>
|
93
128
|
|
129
|
+
### Render a different field name in the output
|
130
|
+
Set the `:rename` field option to output a different field name:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
sort_param = SortParam.define do
|
134
|
+
field :first_name, rename: 'users.name'
|
135
|
+
end
|
136
|
+
|
137
|
+
sort_param.load!("+first_name", mode: :pg)
|
138
|
+
=> "users.name asc"
|
139
|
+
|
140
|
+
sort_param.load!("+first_name", mode: :mysql)
|
141
|
+
=> "users.name asc"
|
142
|
+
|
143
|
+
sort_param.load!("+first_name")
|
144
|
+
=> {"users.name"=>{:direction=>:asc}}
|
145
|
+
```
|
146
|
+
|
147
|
+
<br/>
|
148
|
+
|
94
149
|
### Rails example
|
95
150
|
|
96
151
|
```ruby
|
97
|
-
def index
|
98
|
-
|
152
|
+
def index
|
153
|
+
render json: User.all.order(sort_fields)
|
99
154
|
end
|
100
155
|
|
101
156
|
private
|
102
157
|
|
103
|
-
def
|
158
|
+
def sort_fields
|
104
159
|
SortParam.define do
|
105
160
|
field :first_name
|
106
161
|
field :last_name, nulls: :first
|
@@ -114,6 +169,40 @@ def sort_param
|
|
114
169
|
end
|
115
170
|
```
|
116
171
|
|
172
|
+
We can DRY this up a bit by creating a concern:
|
173
|
+
|
174
|
+
#### controllers/concerns/has_sort_param.rb
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
module HasSortParam
|
178
|
+
extend ActiveSupport::Concern
|
179
|
+
|
180
|
+
def sort_param(default: nil, &block)
|
181
|
+
raise ArgumentError.new('Missing block') unless block_given?
|
182
|
+
|
183
|
+
definition = SortParam.define(&block)
|
184
|
+
definition.load!(params[:sort].presence || default, mode: :pg)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
### controller
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
def index
|
193
|
+
render json: User.all.order(sort_fields)
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def sort_fields
|
199
|
+
sort_param default: '+first_name,-last_name' do
|
200
|
+
field :first_name
|
201
|
+
field :last_name, nulls: :first
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
117
206
|
### Error
|
118
207
|
|
119
208
|
| Class | Description |
|
@@ -26,6 +26,7 @@ module SortParam
|
|
26
26
|
# @param name [String, Symbol] column name
|
27
27
|
# @param defaults [Hash] column default options:
|
28
28
|
# * nulls (Symbol) nulls sort order. `:last` or `:first`
|
29
|
+
# * rename (String) field name in formatted output
|
29
30
|
#
|
30
31
|
# @return [self] Definition instance
|
31
32
|
def field(name, defaults = {})
|
@@ -52,8 +53,8 @@ module SortParam
|
|
52
53
|
#
|
53
54
|
# @param sort_string [String] Sort expression. Comma-separated sort fields.
|
54
55
|
# @param mode [Symbol, NilClass] Translation format
|
55
|
-
# * `:pg` for PostgreSQL ORDER BY SQL
|
56
|
-
# * `:mysql` for MySQL ORDER BY SQL
|
56
|
+
# * `:pg` for PostgreSQL ORDER BY SQL.
|
57
|
+
# * `:mysql` for MySQL ORDER BY SQL.
|
57
58
|
# * `:hash`/nil for the default hash representation.
|
58
59
|
#
|
59
60
|
# @example Sort by first_name ASC and then by last_name DESC
|
data/lib/sort_param/field.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module SortParam
|
4
4
|
module Formatters
|
5
5
|
class Formatter
|
6
|
+
include Utilities
|
7
|
+
|
6
8
|
def self.for(mode)
|
7
9
|
return Formatters::PG if mode == :pg
|
8
10
|
return Formatters::MySQL if mode == :mysql
|
@@ -34,6 +36,12 @@ module SortParam
|
|
34
36
|
def format_field(field)
|
35
37
|
raise NotImplementedError
|
36
38
|
end
|
39
|
+
|
40
|
+
def formatted_field_name(field)
|
41
|
+
formatted_name = definition.field_defaults(field.name)[:rename]
|
42
|
+
|
43
|
+
blank?(formatted_name) ? field.name : formatted_name
|
44
|
+
end
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -6,17 +6,21 @@ module SortParam
|
|
6
6
|
private
|
7
7
|
|
8
8
|
def format_field(field)
|
9
|
-
|
10
|
-
field_data.merge!(direction: field.direction)
|
11
|
-
field_data.merge!(nulls: field.nulls) unless field.nulls.nil?
|
12
|
-
|
13
|
-
{ field.name => field_data }
|
9
|
+
{ formatted_field_name(field) => field_data(field) }
|
14
10
|
end
|
15
11
|
|
16
12
|
def format_collection(fields)
|
17
13
|
fields.map { |field| format(field) }
|
18
14
|
.inject(&:merge!)
|
19
15
|
end
|
16
|
+
|
17
|
+
def field_data(field)
|
18
|
+
data = definition.field_defaults(field.name) || {}
|
19
|
+
data.merge!(direction: field.direction)
|
20
|
+
data.merge!(nulls: field.nulls) unless field.nulls.nil?
|
21
|
+
data.delete(:rename)
|
22
|
+
data
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -11,8 +11,8 @@ module SortParam
|
|
11
11
|
|
12
12
|
def format_field(field)
|
13
13
|
field_defaults = definition.field_defaults(field.name) || {}
|
14
|
-
column_name = field_defaults[:column_name] || field.name
|
15
14
|
|
15
|
+
column_name = formatted_field_name(field)
|
16
16
|
nulls = (field.nulls || field_defaults[:nulls]).to_s
|
17
17
|
nulls_sort_order = nulls_order(column_name, nulls)
|
18
18
|
return "#{column_name} #{field.direction}" if nulls_sort_order.nil?
|
@@ -11,10 +11,9 @@ module SortParam
|
|
11
11
|
|
12
12
|
def format_field(field)
|
13
13
|
field_defaults = definition.field_defaults(field.name) || {}
|
14
|
-
column_name = field_defaults[:column_name] || field.name
|
15
14
|
|
16
15
|
nulls = (field.nulls || field_defaults[:nulls]).to_s
|
17
|
-
"#{
|
16
|
+
"#{formatted_field_name(field)} #{field.direction}#{nulls_order(nulls)}"
|
18
17
|
end
|
19
18
|
|
20
19
|
def nulls_order(nulls)
|
data/lib/sort_param/utilities.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
module SortParam
|
2
2
|
module Utilities
|
3
|
-
def
|
4
|
-
|
5
|
-
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def blank?(str)
|
9
|
+
return true if str.nil? || str == ""
|
10
|
+
return false unless str.is_a?(String)
|
6
11
|
|
7
|
-
|
8
|
-
|
12
|
+
str.strip!
|
13
|
+
str.empty?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def blank?(str)
|
18
|
+
self.class.blank?(str)
|
9
19
|
end
|
10
20
|
end
|
11
21
|
end
|
data/lib/sort_param/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sort_param
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uy Jayson B
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Sort records using a sort query parameter à la JSON-API style
|
14
14
|
email:
|