active_sort_order 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/LICENSE +21 -0
  4. data/README.md +140 -0
  5. data/Rakefile +21 -0
  6. data/lib/active_sort_order.rb +11 -0
  7. data/lib/active_sort_order/concerns/sort_order_concern.rb +67 -0
  8. data/lib/active_sort_order/version.rb +3 -0
  9. data/test/dummy_app/Rakefile +7 -0
  10. data/test/dummy_app/app/assets/config/manifest.js +3 -0
  11. data/test/dummy_app/app/assets/javascripts/application.js +0 -0
  12. data/test/dummy_app/app/assets/stylesheets/application.css +3 -0
  13. data/test/dummy_app/app/controllers/application_controller.rb +3 -0
  14. data/test/dummy_app/app/models/application_record.rb +3 -0
  15. data/test/dummy_app/app/models/post.rb +3 -0
  16. data/test/dummy_app/app/models/post_with_base_order_a.rb +7 -0
  17. data/test/dummy_app/app/models/post_with_base_order_a_and_b.rb +7 -0
  18. data/test/dummy_app/app/models/post_with_base_order_b.rb +7 -0
  19. data/test/dummy_app/app/models/post_with_base_order_b_and_a.rb +7 -0
  20. data/test/dummy_app/app/models/post_with_volatile_base_order.rb +7 -0
  21. data/test/dummy_app/app/models/test_helper.rb +0 -0
  22. data/test/dummy_app/app/views/layouts/application.html.erb +14 -0
  23. data/test/dummy_app/config.ru +4 -0
  24. data/test/dummy_app/config/application.rb +66 -0
  25. data/test/dummy_app/config/boot.rb +10 -0
  26. data/test/dummy_app/config/database.yml +11 -0
  27. data/test/dummy_app/config/environment.rb +5 -0
  28. data/test/dummy_app/config/environments/development.rb +30 -0
  29. data/test/dummy_app/config/environments/production.rb +60 -0
  30. data/test/dummy_app/config/environments/test.rb +41 -0
  31. data/test/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
  32. data/test/dummy_app/config/initializers/inflections.rb +10 -0
  33. data/test/dummy_app/config/initializers/mime_types.rb +5 -0
  34. data/test/dummy_app/config/initializers/secret_token.rb +11 -0
  35. data/test/dummy_app/config/initializers/session_store.rb +8 -0
  36. data/test/dummy_app/config/initializers/wrap_parameters.rb +14 -0
  37. data/test/dummy_app/config/locales/en.yml +5 -0
  38. data/test/dummy_app/config/routes.rb +6 -0
  39. data/test/dummy_app/config/secrets.yml +22 -0
  40. data/test/dummy_app/db/migrate/20210128155312_set_up_test_tables.rb +9 -0
  41. data/test/dummy_app/db/schema.rb +19 -0
  42. data/test/dummy_app/db/test.sqlite3 +0 -0
  43. data/test/dummy_app/log/test.log +10098 -0
  44. data/test/test_helper.rb +53 -0
  45. data/test/unit/active_sort_order_test.rb +116 -0
  46. data/test/unit/errors_test.rb +171 -0
  47. metadata +156 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 29d4251d6a9b034fe9354311a077024e119a7e5de8f15c566ce5e41671e8640a
4
+ data.tar.gz: 79e4e40796445bf88ee888289255989c8adbcfaecce2b44bb382363f38f29617
5
+ SHA512:
6
+ metadata.gz: c6cd7b26a6bbc32d2c0c9e78631dffc95c31fb9d907cb36ca37e70b9fdead4b56c4041dc57f2271ef1ee59a8cc5c381b8cea87f14d7f62504ce572663928d193
7
+ data.tar.gz: c13ca12d41b8afe6247120dac2cc1c02a636e55c55a5852ddffbda4ddb2198599c66d399f1f134246ecb52014d327cd0cfaa0f53161a51c397612736a6dd15ad
@@ -0,0 +1,10 @@
1
+ CHANGELOG
2
+ ---------
3
+
4
+ - Unreleased
5
+ * [View Diff](https://github.com/westonganger/active_sort_order/compare/v0.9.0...master)
6
+ * 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)
10
+ * Initial Release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Weston Ganger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,140 @@
1
+ # Active Sort Order
2
+
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>
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
+
7
+ Dead simple, fully customizable sorting pattern for ActiveRecord.
8
+
9
+ # Installation
10
+
11
+ ```ruby
12
+ gem 'active_sort_order'
13
+ ```
14
+
15
+ Then add `include ActiveSortOrder` to your ApplicationRecord or models.
16
+
17
+ ```ruby
18
+ ### Preferred
19
+ class ApplicationRecord < ActiveRecord::Base
20
+ include ActiveSortOrder
21
+ end
22
+
23
+ ### OR for individual models
24
+
25
+ class Post < ActiveRecord::Base
26
+ include ActiveSortOrder
27
+ end
28
+
29
+ ### OR for all models without an ApplicationRecord model
30
+
31
+ # config/initializers/active_sort_order.rb
32
+ ActiveSupport.on_load(:active_record) do
33
+ ### Load for all ActiveRecord models
34
+ include ActiveSortOrder
35
+ end
36
+ ```
37
+
38
+ # Usage
39
+
40
+ ## Base Order
41
+
42
+ You will likely want to define a `base_sort_order` class method to each model.
43
+
44
+ This will be utilized when not providing a `:base_sort_order` argument.
45
+
46
+ ```ruby
47
+ def self.base_sort_order
48
+ "lower(#{table_name}.name) ASC, lower(#{table_name}.code) ASC" # for example
49
+ end
50
+ ```
51
+
52
+ ## Sorting
53
+
54
+ This gem defines one scope on your models: `sort_order`
55
+
56
+ This method uses `reorder` so any previously defined `order` will be removed.
57
+
58
+ In the below examples:
59
+
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"
62
+
63
+ ```ruby
64
+ ### Applies the classes base_sort_order (if defined)
65
+ Post.all.sort_order
66
+
67
+ ### Use custom base_sort_order
68
+ Post.all.sort_order(base_sort_order: "lower(number) DESC")
69
+
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])
72
+
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")
75
+
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)
79
+ ```
80
+
81
+ ## Additional Customizations
82
+
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.
84
+
85
+ - [lib/active_sort_order/concerns/sort_order_concern.rb](./lib/active_sort_order/concerns/sort_order_concern.rb)
86
+
87
+ ## Helper / View Examples
88
+
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
90
+
91
+ ```ruby
92
+ ### app/helpers/application_helper.rb
93
+
94
+ module ApplicationHelper
95
+
96
+ def sort_link(column, title = nil, opts = {})
97
+ column = column.to_s
98
+
99
+ if title && title.is_a?(Hash)
100
+ opts = title
101
+ title = opts[:title]
102
+ end
103
+
104
+ title ||= column.titleize
105
+
106
+ if opts[:disabled]
107
+ return title
108
+ else
109
+ if params[:direction].present? && params[:sort].present?
110
+ direction = (column == params[:sort] && params[:direction] == "asc") ? "desc" : "asc"
111
+ else
112
+ direction = "asc"
113
+ end
114
+
115
+ return link_to(title, params.to_unsafe_h.merge(sort: column, direction: direction))
116
+ end
117
+ end
118
+
119
+ end
120
+ ```
121
+
122
+ Then use the link helper within your views like:
123
+
124
+ ```erb
125
+ <th>
126
+ <%= sort_link :name %>
127
+ </th>
128
+
129
+ <th>
130
+ <%= sort_link "companies.name", "Company Name" %>
131
+ </th>
132
+
133
+ <th>
134
+ <%= sort_link "companies.name", "Company Name", disabled: !@sort_enabled %>
135
+ </th>
136
+ ```
137
+
138
+ # Credits
139
+
140
+ Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/lib/active_sort_order/version.rb')
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: [:test]
13
+
14
+ task :console do
15
+ require 'active_sort_order'
16
+
17
+ require 'test/dummy_app/app/models/post'
18
+
19
+ require 'irb'
20
+ binding.irb
21
+ end
@@ -0,0 +1,11 @@
1
+ require "active_record"
2
+
3
+ require "active_sort_order/concerns/sort_order_concern"
4
+
5
+ module ActiveSortOrder
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveSortOrder::SortOrderConcern
10
+ end
11
+ end
@@ -0,0 +1,67 @@
1
+ module ActiveSortOrder
2
+ module SortOrderConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
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")
10
+ end
11
+
12
+ if sort_str.present?
13
+ sort_str = sort_str.to_s
14
+
15
+ if sort_direction.is_a?(Symbol)
16
+ sort_direction = sort_direction.to_s.gsub('_', ' ')
17
+ end
18
+
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")
23
+ else
24
+ valid_directions = [
25
+ "ASC",
26
+ "DESC",
27
+ "ASC NULLS FIRST",
28
+ "ASC NULLS LAST",
29
+ "DESC NULLS FIRST",
30
+ "DESC NULLS LAST",
31
+ ].freeze
32
+
33
+ sanitized_sort_direction = sort_direction.split(' ').join(' ')
34
+
35
+ if !valid_directions.include?(sanitized_sort_direction.upcase)
36
+ raise ArgumentError.new("Invalid second argument `sort_direction` #{sort_direction}")
37
+ end
38
+ end
39
+
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
45
+ end
46
+
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")
49
+ end
50
+
51
+ if base_sort_order.present?
52
+ if sql_str.present?
53
+ sql_str << ", #{base_sort_order}"
54
+ else
55
+ sql_str = base_sort_order
56
+ end
57
+ end
58
+
59
+ sanitized_str = sql_str.blank? ? nil : Arel.sql(sanitize_sql_for_order(sql_str))
60
+
61
+ next self.reorder(sanitized_str)
62
+ }
63
+
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveSortOrder
2
+ VERSION = "0.9.0".freeze
3
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
3
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
+
5
+ require File.expand_path('../config/application', __FILE__)
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css .scss
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require_self
3
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ApplicationRecord
2
+ include ActiveSortOrder
3
+ end
@@ -0,0 +1,7 @@
1
+ class PostWithBaseOrderA < Post
2
+
3
+ def self.base_sort_order
4
+ "posts.a ASC"
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class PostWithBaseOrderAAndB < Post
2
+
3
+ def self.base_sort_order
4
+ "posts.a ASC, posts.b ASC"
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class PostWithBaseOrderB < Post
2
+
3
+ def self.base_sort_order
4
+ "posts.b ASC"
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class PostWithBaseOrderBAndA < Post
2
+
3
+ def self.base_sort_order
4
+ "posts.b ASC, posts.a ASC"
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class PostWithVolatileBaseOrder < Post
2
+
3
+ def self.base_sort_order
4
+ # Will be overwritten in tests
5
+ end
6
+
7
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy App</title>
5
+ <%= stylesheet_link_tag "application" %>
6
+ <%= javascript_include_tag "application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require 'warning'
4
+ Warning.ignore(
5
+ %r{mail/parsers/address_lists_parser}, ### Hide mail gem warnings
6
+ )
7
+
8
+ require 'rails/all'
9
+
10
+ Bundler.require
11
+
12
+ module Dummy
13
+ class Application < Rails::Application
14
+ # Settings in config/environments/* take precedence over those specified here.
15
+ # Application configuration should go into files in config/initializers
16
+ # -- all .rb files in that directory are automatically loaded.
17
+
18
+ # Custom directories with classes and modules you want to be autoloadable.
19
+ # config.autoload_paths += %W(#{config.root}/extras)
20
+
21
+ # Only load the plugins named here, in the order given (default is alphabetical).
22
+ # :all can be used as a placeholder for all plugins not explicitly named.
23
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
+
25
+ # Activate observers that should always be running.
26
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
+
28
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
+ # config.time_zone = 'Central Time (US & Canada)'
31
+
32
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34
+ # config.i18n.default_locale = :de
35
+
36
+ # Configure the default encoding used in templates for Ruby 1.9.
37
+ config.encoding = "utf-8"
38
+
39
+ # Configure sensitive parameters which will be filtered from the log file.
40
+ config.filter_parameters += [:password]
41
+
42
+ # Enable the asset pipeline
43
+ config.assets.enabled = true
44
+
45
+ config.assets.quiet = true
46
+
47
+ # Version of your assets, change this if you want to expire all your assets
48
+ config.assets.version = '1.0'
49
+
50
+ config.generators.test_framework = false
51
+ config.generators.helper = false
52
+ config.generators.stylesheets = false
53
+ config.generators.javascripts = false
54
+
55
+ config.after_initialize do
56
+ ActiveRecord::Migration.migrate(Rails.root.join("db/migrate/*").to_s)
57
+ end
58
+
59
+ if ActiveRecord.respond_to?(:gem_version)
60
+ gem_version = ActiveRecord.gem_version
61
+ if gem_version.to_s.start_with?("5.2.")
62
+ config.active_record.sqlite3.represent_boolean_as_integer = true
63
+ end
64
+ end
65
+ end
66
+ end