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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbe25d60eee31370137b39d3edaa1b0e90270ce37e710fda5bd8d4d46bf09763
4
- data.tar.gz: 174b0d2fc448a461b6eef190f80d73c20ad21f90e821647714716f35e22b05d0
3
+ metadata.gz: 78cff5e96b34b0697aa50227db7186cbbbaac54290cdaf88ff6201ae35afda6e
4
+ data.tar.gz: 025b26afa452522750032d2be80f83dca0c0343ff00416e2a6d2299e47680980
5
5
  SHA512:
6
- metadata.gz: 283a35dd58cef523745ef97a097d0ba46548438b580947bc37d8754bdf326b99002f3ba145a270c7fa86978f8b0eca20ffc316b709f0c44b1cffc764dc01d888
7
- data.tar.gz: 51b36c10d39d676274133664678c241c301050f64de7495764e1773124f5b17ecbbf8f334c1b62f6e8a943aed57e9cde8409926917e9ca7bbc0cf27bf3eb3364
6
+ metadata.gz: 8c191c86a31f9a9c300ab3064d3b44b9be8f273fc4578de139482f7434bb07b5c69d44ef12077b262a2832757450f7ded50ced460e501cf602328a29e6e2d2ad
7
+ data.tar.gz: 788302acfbcf086d426fe2adebb1c5ee59aa85ece7e5e78e956e6ea81987b04efef002ed052d7b0615736648b17552a46c7a5d85c44e94a2f0d6f416ebf4b94e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
1
  ## [0.1.0]
2
-
3
2
  - Initial release
3
+
4
+ ## [0.2.0]
5
+ - Support setting a different field name in the formatted output
6
+
7
+ ## [1.0.0]
8
+ - Rename :formatted_name option to :rename
data/README.md CHANGED
@@ -1,11 +1,31 @@
1
1
  # SortParam
2
2
 
3
- Sort records using a query parameter based on JSON API's sorting format.
3
+ [![Gem Version](https://badge.fury.io/rb/sort_param.svg)](https://badge.fury.io/rb/sort_param) [![CI](https://github.com/jsonb-uy/sort_param/actions/workflows/ruby.yml/badge.svg?branch=main)](https://github.com/jsonb-uy/sort_param/actions/workflows/ruby.yml) [![codecov](https://codecov.io/gh/jsonb-uy/sort_param/branch/main/graph/badge.svg?token=09RE3PZW4G)](https://codecov.io/gh/jsonb-uy/sort_param) [![Maintainability](https://api.codeclimate.com/v1/badges/d1655f67377c21e9618a/maintainability)](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
- * Parse the sort string/expression into hash for any further processing.
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
- users = User.all.order(order_by)
152
+ def index
153
+ render json: User.all.order(sort_fields)
99
154
  end
100
155
 
101
156
  private
102
157
 
103
- def order_by
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
@@ -1,10 +1,10 @@
1
1
  module SortParam
2
2
  class Field
3
+ include Utilities
4
+
3
5
  SORT_SYMBOL_DIRECTION = { "+" => :asc, "-" => :desc }.freeze
4
6
 
5
7
  class << self
6
- include Utilities
7
-
8
8
  def from_string(sort_string)
9
9
  return nil if blank?(sort_string)
10
10
 
@@ -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
- field_data = definition.field_defaults(field.name) || {}
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
- "#{column_name} #{field.direction}#{nulls_order(nulls)}"
16
+ "#{formatted_field_name(field)} #{field.direction}#{nulls_order(nulls)}"
18
17
  end
19
18
 
20
19
  def nulls_order(nulls)
@@ -1,11 +1,21 @@
1
1
  module SortParam
2
2
  module Utilities
3
- def blank?(str)
4
- return true if str.nil? || str == ""
5
- return false unless str.is_a?(String)
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
- str.strip!
8
- str.empty?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SortParam
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
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: 0.1.0
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-05 00:00:00.000000000 Z
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: