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 +4 -4
- data/CHANGELOG.md +18 -5
- data/README.md +91 -40
- data/Rakefile +5 -1
- data/lib/active_sort_order/concerns/sort_order_concern.rb +44 -23
- data/lib/active_sort_order/version.rb +1 -1
- data/test/dummy_app/config/application.rb +0 -5
- data/test/dummy_app/config/database.yml +12 -3
- data/test/dummy_app/config/environments/test.rb +1 -1
- data/test/dummy_app/db/migrate/20210128155312_set_up_test_tables.rb +7 -1
- data/test/dummy_app/db/test.sqlite3 +0 -0
- data/test/dummy_app/log/test.log +558 -9902
- data/test/test_helper.rb +16 -6
- data/test/unit/active_sort_order_test.rb +31 -3
- data/test/unit/errors_test.rb +56 -22
- metadata +27 -41
- data/test/dummy_app/app/models/test_helper.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 863fc9f8899a7aea2e126c5f482b1bb8d422513c644eaf26f4f58fbc52d82f6b
|
4
|
+
data.tar.gz: 81e3205c167c100991cbb2cf80a3528e6b4c471cfa63fa652d56572d5e981867
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
9
|
-
*
|
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://
|
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
|
-
|
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
|
-
|
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
|
-
|
28
|
+
## Dynamic Sorting
|
24
29
|
|
25
|
-
|
26
|
-
include ActiveSortOrder
|
27
|
-
end
|
30
|
+
This gem defines one scope on your models: `sort_order`
|
28
31
|
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
56
|
+
## Sorting on multiple columns
|
39
57
|
|
40
|
-
|
58
|
+
##### Method Definition:
|
41
59
|
|
42
|
-
|
60
|
+
`sort_order(sort_col_sql = nil, sort_direction_sql = nil, base_sort_order: true)`
|
43
61
|
|
44
|
-
|
62
|
+
Options:
|
45
63
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
##
|
75
|
+
## Base Sort Order
|
53
76
|
|
54
|
-
|
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
|
-
|
79
|
+
For this you can define a `base_sort_order` class method to your models.
|
57
80
|
|
58
|
-
|
81
|
+
This will be utilized on the `sort_order` method when not providing a direct `:base_sort_order` argument.
|
59
82
|
|
60
|
-
|
61
|
-
|
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
|
-
###
|
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
|
-
###
|
71
|
-
Post.all.sort_order(params[:
|
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
|
-
|
74
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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, ->(
|
8
|
-
if
|
9
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
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
|
20
|
-
|
21
|
-
elsif !
|
22
|
-
raise ArgumentError.new("Invalid second argument `
|
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
|
-
|
40
|
+
orig_direction_sql = sort_direction_sql
|
34
41
|
|
35
|
-
|
36
|
-
|
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 = "#{
|
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
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
81
|
+
next self.reorder(sanitized_str)
|
82
|
+
end
|
62
83
|
}
|
63
84
|
|
64
85
|
end
|
@@ -1,11 +1,20 @@
|
|
1
1
|
default: &default
|
2
|
+
<% if defined?(SQLite3) %>
|
2
3
|
adapter: sqlite3
|
3
|
-
|
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.
|
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
|
-
|
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
|