sort_param 0.1.0 → 0.2.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: c05d893490a180c3cb1ce696d8fbddf77f9e7a0f323434c755408f54f239c427
4
+ data.tar.gz: 672ae31570afece8e8a18c504f7bbe47390d83c8e7485934edb473ace88e11db
5
5
  SHA512:
6
- metadata.gz: 283a35dd58cef523745ef97a097d0ba46548438b580947bc37d8754bdf326b99002f3ba145a270c7fa86978f8b0eca20ffc316b709f0c44b1cffc764dc01d888
7
- data.tar.gz: 51b36c10d39d676274133664678c241c301050f64de7495764e1773124f5b17ecbbf8f334c1b62f6e8a943aed57e9cde8409926917e9ca7bbc0cf27bf3eb3364
6
+ metadata.gz: 0dbaff8fc1c00aa5903ab1e75576b6af6d99ba37a3d90d41d8a2ef1ed4928d183a278d31552924aa94ba2aa0b395639d176a477cc50a0ed9379e61cc069c2ddb
7
+ data.tar.gz: fe8ca1f676f8f4ff66a5e3a9395457218725eb440b37f1ee253752a5f77a1ae761e79136521735c0b291f92f54e6e3c02ec6e7416d3baa1a79840498c3e78c85
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
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
+
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:
@@ -94,13 +129,13 @@ sort_param.load!("+first_name:nulls_last,-last_name:nulls_first", mode: :pg)
94
129
  ### Rails example
95
130
 
96
131
  ```ruby
97
- def index
98
- users = User.all.order(order_by)
132
+ def index
133
+ render json: User.all.order(sort_fields)
99
134
  end
100
135
 
101
136
  private
102
137
 
103
- def order_by
138
+ def sort_fields
104
139
  SortParam.define do
105
140
  field :first_name
106
141
  field :last_name, nulls: :first
@@ -114,6 +149,40 @@ def sort_param
114
149
  end
115
150
  ```
116
151
 
152
+ We can DRY this up a bit by creating a concern:
153
+
154
+ #### controllers/concerns/has_sort_param.rb
155
+
156
+ ```ruby
157
+ module HasSortParam
158
+ extend ActiveSupport::Concern
159
+
160
+ def sort_param(default: nil, &block)
161
+ raise ArgumentError.new('Missing block') unless block_given?
162
+
163
+ definition = SortParam.define(&block)
164
+ definition.load!(params[:sort].presence || default, mode: :pg)
165
+ end
166
+ end
167
+ ```
168
+
169
+ ### controller
170
+
171
+ ```ruby
172
+ def index
173
+ render json: User.all.order(sort_fields)
174
+ end
175
+
176
+ private
177
+
178
+ def sort_fields
179
+ sort_param default: '+first_name,-last_name' do
180
+ field :first_name
181
+ field :last_name, nulls: :first
182
+ end
183
+ end
184
+ ```
185
+
117
186
  ### Error
118
187
 
119
188
  | 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
+ # * formatted_name (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)[:formatted_name]
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(:formatted_name)
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 = "0.2.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: 0.2.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-13 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: