active_sort_order 0.9.0 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29d4251d6a9b034fe9354311a077024e119a7e5de8f15c566ce5e41671e8640a
4
- data.tar.gz: 79e4e40796445bf88ee888289255989c8adbcfaecce2b44bb382363f38f29617
3
+ metadata.gz: 863fc9f8899a7aea2e126c5f482b1bb8d422513c644eaf26f4f58fbc52d82f6b
4
+ data.tar.gz: 81e3205c167c100991cbb2cf80a3528e6b4c471cfa63fa652d56572d5e981867
5
5
  SHA512:
6
- metadata.gz: c6cd7b26a6bbc32d2c0c9e78631dffc95c31fb9d907cb36ca37e70b9fdead4b56c4041dc57f2271ef1ee59a8cc5c381b8cea87f14d7f62504ce572663928d193
7
- data.tar.gz: c13ca12d41b8afe6247120dac2cc1c02a636e55c55a5852ddffbda4ddb2198599c66d399f1f134246ecb52014d327cd0cfaa0f53161a51c397612736a6dd15ad
6
+ metadata.gz: f222b38e5900633eb6b2925a33e58d15425848183501b8ff5ab22010ad8fcd68ab2429dbf4de6302dae3155f49026330aa6c41c95fc52b777e13e257045cb7e8
7
+ data.tar.gz: 85f20d93e24af7b65ffb9371605e18d8729c0b4e2e34d1ad7283bc51beed78016e924a77a55a081d19e90af8c8e81b38d3a010b243de67547337947a1aa88c53
data/CHANGELOG.md CHANGED
@@ -1,10 +1,23 @@
1
1
  CHANGELOG
2
2
  ---------
3
3
 
4
- - Unreleased
5
- * [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.0...master)
4
+ - **Unreleased - [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.4...master)**
6
5
  * Nothing yet
7
-
8
- - v0.9.0 - Jan 29, 2021
9
- * [View Diff](https://github.com/westonganger/active_sort_order/compare/371fc82...v0.9.0)
6
+
7
+ - **v0.9.4 - [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.3...v0.9.4)**
8
+ * Improve error handling for sort_col_sql argument
9
+
10
+ - **v0.9.3 - [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.2...v0.9.3)**
11
+ * Allow ability to sort on multiple fields
12
+
13
+ - **v0.9.2 - Apr 28, 2021 - [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.1...v0.9.2)**
14
+ * Fix deprecation warning in Rails 6.1 for `reorder(nil)`
15
+
16
+ - **v0.9.1 - Jan 31, 2021 - [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.0...v0.9.1)**
17
+ * General Improvements
18
+ * Add Github Actions CI supporting multiple version of Ruby, Rails and multiple databases types
19
+ * Fix bugs with Rails 5.0 and 5.1
20
+ * Limit supports to Rails 5.0+. Tests were showing some failures with Rails 4.2 so I dropped it.
21
+
22
+ - **v0.9.0 - Jan 29, 2021 - [View Diff](https://github.com/westonganger/active_sort_order/compare/371fc82...v0.9.0)**
10
23
  * Initial Release
data/README.md CHANGED
@@ -1,92 +1,143 @@
1
1
  # Active Sort Order
2
2
 
3
3
  <a href="https://badge.fury.io/rb/active_sort_order" target="_blank"><img height="21" style='border:0px;height:21px;' border='0' src="https://badge.fury.io/rb/active_sort_order.svg" alt="Gem Version"></a>
4
- <a href='https://travis-ci.com/westonganger/active_sort_order' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://api.travis-ci.org/westonganger/active_sort_order.svg?branch=master' border='0' alt='Build Status' /></a>
4
+ <a href='https://github.com/westonganger/active_sort_order/actions' target='_blank'><img src="https://github.com/westonganger/active_sort_order/workflows/Tests/badge.svg" style="max-width:100%;" height='21' style='border:0px;height:21px;' border='0' alt="CI Status"></a>
5
5
  <a href='https://rubygems.org/gems/active_sort_order' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://ruby-gem-downloads-badge.herokuapp.com/active_sort_order?label=rubygems&type=total&total_label=downloads&color=brightgreen' border='0' alt='RubyGems Downloads' /></a>
6
6
 
7
- Dead simple, fully customizable sorting pattern for ActiveRecord.
7
+ The "easy-peasy" dynamic sorting pattern for ActiveRecord that your Rails apps deserve. Useful for Rails controllers with large data, pagination, etc.
8
8
 
9
- # Installation
9
+ Features:
10
+
11
+ - Full SQL compatibility
12
+ - Dead Simple. Just [one concern with one scope](#additional-customizations).
13
+
14
+ ## Installation
10
15
 
11
16
  ```ruby
12
17
  gem 'active_sort_order'
13
18
  ```
14
19
 
15
- Then add `include ActiveSortOrder` to your ApplicationRecord or models.
20
+ Then add `include ActiveSortOrder` to your ApplicationRecord or individual models.
16
21
 
17
22
  ```ruby
18
- ### Preferred
19
23
  class ApplicationRecord < ActiveRecord::Base
20
24
  include ActiveSortOrder
21
25
  end
26
+ ```
22
27
 
23
- ### OR for individual models
28
+ ## Dynamic Sorting
24
29
 
25
- class Post < ActiveRecord::Base
26
- include ActiveSortOrder
27
- end
30
+ This gem defines one scope on your models: `sort_order`
28
31
 
29
- ### OR for all models without an ApplicationRecord model
32
+ This method uses ActiveRecord's `reorder` under the hood, so any previously defined `order` will be removed upon calling `sort_order`
30
33
 
31
- # config/initializers/active_sort_order.rb
32
- ActiveSupport.on_load(:active_record) do
33
- ### Load for all ActiveRecord models
34
- include ActiveSortOrder
34
+ In the below examples we are within a controller and are using the params as our variables:
35
+
36
+ ```ruby
37
+ # app/controllers/posts_controller.rb
38
+
39
+ case params[:sort]
40
+ when "number_str"
41
+ sort_col_sql = "CAST(posts.number_str AS int)"
42
+ when "user"
43
+ ### To sort on multiple fields pass in an Array
44
+ sort_col_sql = ["users.first_name", "users.last_name"]
45
+ else
46
+ sort_col_sql = params[:sort]
35
47
  end
48
+
49
+ ### Output combined sort order (if present) and secondary / base sort order
50
+ Post.all.sort_order(sort_col_sql, params[:direction], base_sort_order: "lower(number) ASC, lower(code) ASC")
51
+
52
+ ### Output combined sort order (if present) AND applies the classes base_sort_order (if defined)
53
+ Post.all.sort_order(sort_col_sql, params[:direction])
36
54
  ```
37
55
 
38
- # Usage
56
+ ## Sorting on multiple columns
39
57
 
40
- ## Base Order
58
+ ##### Method Definition:
41
59
 
42
- You will likely want to define a `base_sort_order` class method to each model.
60
+ `sort_order(sort_col_sql = nil, sort_direction_sql = nil, base_sort_order: true)`
43
61
 
44
- This will be utilized when not providing a `:base_sort_order` argument.
62
+ Options:
45
63
 
46
- ```ruby
47
- def self.base_sort_order
48
- "lower(#{table_name}.name) ASC, lower(#{table_name}.code) ASC" # for example
49
- end
50
- ```
64
+ - `sort_col_sql` is a SQL String of the column name
65
+ * Feel free to use any SQL manipulation on the column name
66
+ * There is no built-in SQL string validation so be sure to handle your sanitization in your project before passing to this method. See [Safely Handling Input](#safely-handling-input)
67
+ * If blank value provided it will skip the dynamic sort and just apply the `base_sort_order`
68
+ - `sort_direction_sql` is a String of the SQL ORDER BY direction
69
+ * The SQL String is automatically validated within the few allowable SQL ORDER BY directions.
70
+ * If nil or "blank string" provided it will fallback to "ASC"
71
+ - `base_sort_order` is a String of the SQL base ordering
72
+ * If not provided or true it will use the classes `base_sort_order` method (if defined)
73
+ * If nil or false is provided it will skip the classes `base_sort_order`
51
74
 
52
- ## Sorting
75
+ ## Base Sort Order
53
76
 
54
- This gem defines one scope on your models: `sort_order`
77
+ To maintain consistency when sorting its always a good idea to have a secondary or base sort order for when duplicates of the main sort column are found or no sort is provided.
55
78
 
56
- This method uses `reorder` so any previously defined `order` will be removed.
79
+ For this you can define a `base_sort_order` class method to your models.
57
80
 
58
- In the below examples:
81
+ This will be utilized on the `sort_order` method when not providing a direct `:base_sort_order` argument.
59
82
 
60
- - `params[:sort]` is a String of the full SQL column name, feel free to use any SQL as required
61
- - `params[:direction]` is a String, if nil/blank string value provided it will fallback to "ASC"
83
+ ```ruby
84
+ class Post < ActiveRecord::Base
85
+ include ActiveSortOrder
86
+
87
+ def self.base_sort_order
88
+ "lower(#{table_name}.name) ASC, lower(#{table_name}.code) ASC" # for example
89
+ end
90
+
91
+ end
92
+ ```
93
+
94
+ The default behaviours of this are shown below.
62
95
 
63
96
  ```ruby
64
97
  ### Applies the classes base_sort_order (if defined)
65
98
  Post.all.sort_order
66
99
 
67
- ### Use custom base_sort_order
100
+ ### Override the classes base_sort_order
68
101
  Post.all.sort_order(base_sort_order: "lower(number) DESC")
102
+ # OR
103
+ Post.all.sort_order(sort_col_sql, params[:direction], base_sort_order: "lower(number) DESC")
69
104
 
70
- ### Output combined sort order (if present) AND applies the classes base_sort_order (if defined)
71
- Post.all.sort_order(params[:sort], params[:direction])
105
+ ### Skip the classes base_sort_order by providing false, nil will still use classes base_sort_order
106
+ Post.all.sort_order(sort_col_sql, params[:direction], base_sort_order: false)
107
+ ```
108
+
109
+ ## Safely Handling Input
110
+
111
+ When accepting params or any custom input for column names it is wise to safely map the field name/alias to the correct SQL string rather than directly sending in the params.
72
112
 
73
- ### output combined sort order (if present) and applies a custom base_sort_order
74
- post.all.sort_order(params[:sort], params[:direction], base_sort_order: "lower(number) desc, lower(code) asc")
113
+ Here is an example on how to handle this within your controller:
114
+
115
+ ```ruby
116
+ if params[:sort].present?
117
+ case params[:sort]
118
+ when "author_name"
119
+ sort_col_sql = "authors.name"
120
+ when "a_or_b"
121
+ sort_col_sql = "COALESCE(posts.field_a, posts.field_b)"
122
+ when "price"
123
+ sort_col_sql = "CAST(REPLACE(posts.price, '$', ',', '') AS int)"
124
+ else
125
+ raise "Invalid Sort Column Given: #{params[:sort]}"
126
+ end
127
+ end
75
128
 
76
- ### Skip the classes base_sort_order by providing false or nil
77
- Post.all.sort_order(params[:sort], params[:direction], base_sort_order: false)
78
- Post.all.sort_order(params[:sort], params[:direction], base_sort_order: nil)
129
+ Post.all.sort_order(sort_col_sql, params[:direction])
79
130
  ```
80
131
 
81
132
  ## Additional Customizations
82
133
 
83
- This gem is just ONE concern with ONE scope. I strongly encourage you to read the code for this library to understand how it works within your project so that you are capable of customizing the functionality later. You can always copy the code directly into your project for deeper project-specific customizations.
134
+ This gem is just one concern with one scope. I encourage you to read the code for this library to understand how it works within your project so that you are capable of customizing the functionality later. You can always copy the code directly into your project for deeper project-specific customizations.
84
135
 
85
136
  - [lib/active_sort_order/concerns/sort_order_concern.rb](./lib/active_sort_order/concerns/sort_order_concern.rb)
86
137
 
87
138
  ## Helper / View Examples
88
139
 
89
- We do not provide built in helpers or view templates because this is a major restriction to applications. Instead we provide simple copy-and-pasteable starter templates
140
+ We do not provide built in helpers or view templates because this is a major restriction to applications. Instead we provide a simple copy-and-pasteable starter template for the sort link:
90
141
 
91
142
  ```ruby
92
143
  ### app/helpers/application_helper.rb
@@ -135,6 +186,6 @@ Then use the link helper within your views like:
135
186
  </th>
136
187
  ```
137
188
 
138
- # Credits
189
+ ## Credits
139
190
 
140
191
  Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
data/Rakefile CHANGED
@@ -14,7 +14,11 @@ task default: [:test]
14
14
  task :console do
15
15
  require 'active_sort_order'
16
16
 
17
- require 'test/dummy_app/app/models/post'
17
+ require_relative 'test/dummy_app/app/models/application_record.rb'
18
+ require_relative 'test/dummy_app/app/models/post.rb'
19
+ Dir.glob("test/dummy_app/app/models/*.rb").each do |f|
20
+ require_relative(f)
21
+ end
18
22
 
19
23
  require 'irb'
20
24
  binding.irb
@@ -4,22 +4,29 @@ module ActiveSortOrder
4
4
 
5
5
  included do
6
6
 
7
- scope :sort_order, ->(sort_str = nil, sort_direction = nil, base_sort_order: "NONE"){
8
- if [String, Symbol, NilClass].none?{|x| sort_str.is_a?(x) }
9
- raise ArgumentError.new("Invalid first argument `sort_str`, expecting a a String or Symbol")
7
+ scope :sort_order, ->(sort_col_sql = nil, sort_direction_sql = nil, base_sort_order: true){
8
+ if !sort_col_sql.is_a?(Array)
9
+ sort_col_sql = [sort_col_sql].compact
10
10
  end
11
11
 
12
- if sort_str.present?
13
- sort_str = sort_str.to_s
12
+ sort_col_sql.each_with_index do |x, i|
13
+ if [String, Symbol].exclude?(x.class)
14
+ raise ArgumentError.new("Invalid first argument `sort_col_sql`, expecting a String or Symbol or Array")
15
+ else
16
+ sort_col_sql[i] = x.to_s
17
+ end
18
+ end
14
19
 
15
- if sort_direction.is_a?(Symbol)
16
- sort_direction = sort_direction.to_s.gsub('_', ' ')
20
+ if sort_col_sql.present?
21
+ ### SORT DIRECTION HANDLING
22
+ if sort_direction_sql.is_a?(Symbol)
23
+ sort_direction_sql = sort_direction_sql.to_s.gsub('_', ' ')
17
24
  end
18
25
 
19
- if sort_direction.nil? || (sort_direction.is_a?(String) && sort_direction == "")
20
- sort_direction = "ASC"
21
- elsif !sort_direction.is_a?(String)
22
- raise ArgumentError.new("Invalid second argument `sort_direction` #{sort_direction}, expecting a a String or Symbol")
26
+ if sort_direction_sql.nil? || (sort_direction_sql.is_a?(String) && sort_direction_sql == "")
27
+ sort_direction_sql = "ASC"
28
+ elsif !sort_direction_sql.is_a?(String)
29
+ raise ArgumentError.new("Invalid second argument `sort_direction_sql`, expecting a String or Symbol")
23
30
  else
24
31
  valid_directions = [
25
32
  "ASC",
@@ -30,22 +37,32 @@ module ActiveSortOrder
30
37
  "DESC NULLS LAST",
31
38
  ].freeze
32
39
 
33
- sanitized_sort_direction = sort_direction.split(' ').join(' ')
40
+ orig_direction_sql = sort_direction_sql
34
41
 
35
- if !valid_directions.include?(sanitized_sort_direction.upcase)
36
- raise ArgumentError.new("Invalid second argument `sort_direction` #{sort_direction}")
42
+ ### REMOVE DUPLICATE BLANKS - Apparently this also removes "\n" and "\t"
43
+ sort_direction_sql = orig_direction_sql.split(' ').join(' ')
44
+
45
+ if !valid_directions.include?(sort_direction_sql.upcase)
46
+ raise ArgumentError.new("Invalid second argument `sort_direction_sql`: #{orig_direction_sql}")
37
47
  end
38
48
  end
39
49
 
40
- sql_str = "#{sort_str} #{sanitized_sort_direction}"
41
- end
42
-
43
- if base_sort_order == "NONE" && self.klass.respond_to?(:base_sort_order)
44
- base_sort_order = self.klass.base_sort_order
50
+ sql_str = sort_col_sql.map{|x| "#{x} #{sort_direction_sql}" }.join(", ")
45
51
  end
52
+
53
+ ### BASE SORT ORDER HANDLING
54
+ if base_sort_order == true
55
+ if self.respond_to?(:base_sort_order)
56
+ base_sort_order = self.base_sort_order
46
57
 
47
- if base_sort_order && !base_sort_order.is_a?(String)
48
- raise ArgumentError.new("Invalid :base_sort_order #{base_sort_order}, expecting a String")
58
+ if [String, NilClass, FalseClass].exclude?(base_sort_order.class)
59
+ raise ArgumentError.new("Invalid value returned from class method `base_sort_order`")
60
+ end
61
+ else
62
+ base_sort_order = nil
63
+ end
64
+ elsif base_sort_order && !base_sort_order.is_a?(String)
65
+ raise ArgumentError.new("Invalid argument provided for :base_sort_order")
49
66
  end
50
67
 
51
68
  if base_sort_order.present?
@@ -56,9 +73,13 @@ module ActiveSortOrder
56
73
  end
57
74
  end
58
75
 
59
- sanitized_str = sql_str.blank? ? nil : Arel.sql(sanitize_sql_for_order(sql_str))
76
+ if sql_str.blank?
77
+ next self.where(nil)
78
+ else
79
+ sanitized_str = Arel.sql(sanitize_sql_for_order(sql_str))
60
80
 
61
- next self.reorder(sanitized_str)
81
+ next self.reorder(sanitized_str)
82
+ end
62
83
  }
63
84
 
64
85
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveSortOrder
2
- VERSION = "0.9.0".freeze
2
+ VERSION = "0.9.4".freeze
3
3
  end
@@ -1,10 +1,5 @@
1
1
  require File.expand_path('../boot', __FILE__)
2
2
 
3
- require 'warning'
4
- Warning.ignore(
5
- %r{mail/parsers/address_lists_parser}, ### Hide mail gem warnings
6
- )
7
-
8
3
  require 'rails/all'
9
4
 
10
5
  Bundler.require
@@ -1,11 +1,20 @@
1
1
  default: &default
2
+ <% if defined?(SQLite3) %>
2
3
  adapter: sqlite3
3
- encoding: unicode
4
+ database: db/test.sqlite3
5
+
6
+ <% elsif defined?(Mysql2) %>
7
+ adapter: mysql2
8
+ database: active_sort_order_test
9
+
10
+ <% elsif defined?(PG) %>
11
+ adapter: postgresql
12
+ database: active_sort_order_test
13
+
14
+ <% end %>
4
15
 
5
16
  development:
6
17
  <<: *default
7
- database: db/dev.sqlite3
8
18
 
9
19
  test:
10
20
  <<: *default
11
- database: db/test.sqlite3
@@ -9,7 +9,7 @@ Dummy::Application.configure do
9
9
 
10
10
  # Configure static asset server for tests with Cache-Control for performance
11
11
  config.serve_static_files = true
12
- config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
12
+ config.public_file_server.enabled = true
13
13
 
14
14
  # Log error messages when you accidentally call methods on nil
15
15
  config.whiny_nils = true
@@ -1,4 +1,10 @@
1
- class SetUpTestTables < ActiveRecord::Migration::Current
1
+ if defined?(ActiveRecord::Migration::Current)
2
+ migration_klass = ActiveRecord::Migration::Current
3
+ else
4
+ migration_klass = ActiveRecord::Migration
5
+ end
6
+
7
+ class SetUpTestTables < migration_klass
2
8
 
3
9
  def change
4
10
  create_table :posts do |t|
Binary file