sortable_by 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b73a5ddc646d2e3c337439264cb4ca123aa3e30e
4
+ data.tar.gz: 5790ac2d41f873ebaffe8e2a20a7329ee667f180
5
+ SHA512:
6
+ metadata.gz: fd6f579e3c5b90e976c23be7f5c03192d5fa816e1dbf7ee466d7df1ea02b13ab3eae36275fec8b6fa2278c791c68b16511f706ac36a0105415f2c62e12317d02
7
+ data.tar.gz: ec9f53443ec60403476953b6f8beabf3982ea7342f5134adf725f1ce3c247d07ce4af80fdb29379308fc22470022676ba6f007f2a4f4a93385bd526247b504cb
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Greg Woods
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,70 @@
1
+ # SortableBy
2
+
3
+ Add sortable table headers to your views and connect them to ActiveRecord queries in your controller.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sortable_by'
11
+ ```
12
+
13
+ And then execute:
14
+ ```bash
15
+ $ bundle install
16
+ ```
17
+
18
+ ### Example Usage
19
+
20
+ In your view, the `sortable_table_header` helper method will generate a `<thead>` element with header tags.
21
+
22
+ ```erb
23
+ <%= sortable_table_header :users_path do |t| %>
24
+ <%= t.sortable :full_name %>
25
+ <%= t.sortable :email %>
26
+ <%= t.sortable :birth_date %>
27
+ <%= t.header :favorite_color %>
28
+ <th>Plain HTML is fine too</th>
29
+ <% end %>
30
+ ```
31
+
32
+ See [table_helper.rb](./app/helpers/sortable_by/table_helper.rb) for options.
33
+
34
+ In your controller, use the `sortable_by` method to configure sorting attributes.
35
+
36
+ ```ruby
37
+ class MyController < ApplicationController
38
+ # Configure the sortable attributes
39
+ sortable_by :email, # A basic sort
40
+ full_name: [:last_name, :first_name], # This will sort on two columns
41
+ birth_date: 'birth_dates.date :dir', # This will sort on a joined table
42
+ default: :email # The sort to use when none is passed
43
+ direction: :desc # Initial sort direction (defaults to :asc)
44
+
45
+ def index
46
+ # Call #sortable_query to produce an ActiveRecord compatible sorting hash
47
+ MyModel.where(...).order(sortable_query)
48
+ end
49
+ end
50
+ ```
51
+
52
+ See [params.rb](./app/controllers/concerns/sortable_by/params.rb) for options.
53
+
54
+ ### Icon Support
55
+
56
+ [Font Awesome](http://fontawesome.io) and [Glyphicons](http://getbootstrap.com/components/#glyphicons) are both supported by default. You can also add your own icon strategy.
57
+
58
+ ```ruby
59
+ module SortableBy::IconStrategy
60
+ def self.custom(context, dir)
61
+ "test: #{dir}"
62
+ end
63
+ end
64
+ ```
65
+
66
+ ```erb
67
+ <%= sortable_table_header :users_path, icon: :custom do |t| %>
68
+ ...
69
+ <% end %>
70
+ ```
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'SortableBy'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,83 @@
1
+ module SortableBy
2
+ module Params
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ attr_accessor :default_sort_attribute
7
+ attr_accessor :default_sort_dir
8
+ attr_accessor :sortable_mapping
9
+
10
+ # Define attributes you wish to sort by
11
+ #
12
+ # This optional configuration method tells the the sortable
13
+ # helpers how they should map attributes to queries. For
14
+ # example a "name" sort might need to sort on first and last
15
+ # name attributes
16
+ #
17
+ # The `default` argument, if passed, will determine the
18
+ # attribute used when no sort param has been passed
19
+ #
20
+ # Usage:
21
+ #
22
+ # sortable_by :email, name: [:last_name, :first_name]
23
+ # default: :email,
24
+ # direction: :asc
25
+ #
26
+ #
27
+ def sortable_by(*attributes, **options)
28
+ @default_sort_attribute = options.delete(:default)
29
+ @default_sort_dir = options.delete(:direction) || :asc
30
+ @sortable_mapping = options.merge(attributes.map { |att| [att, att] }.to_h)
31
+ end
32
+ end
33
+
34
+ # Return the current sort direction
35
+ #
36
+ # Defaults to :default_sort_dir for all values other than 'asc' or 'desc'
37
+ #
38
+ def sort_direction
39
+ if ['asc', 'desc'].include?(params[:dir])
40
+ params[:dir].to_sym
41
+ else
42
+ self.class.default_sort_dir
43
+ end
44
+ end
45
+
46
+ # Return the current sort param
47
+ #
48
+ def sort_attribute
49
+ params[:sort].try(:to_sym) || self.class.default_sort_attribute
50
+ end
51
+
52
+ # Return a hash that can be used in an ActiveRecord order query
53
+ #
54
+ # Example:
55
+ # MyModel.where(...).order(sortable_query).paginate(...)
56
+ #
57
+ def sortable_query
58
+ return unless sort_attribute
59
+ normalize_sort_value(
60
+ sort_attribute,
61
+ sort_direction
62
+ )
63
+ end
64
+
65
+ # Translate the sort_by and direction into a sortable hash
66
+ #
67
+ def normalize_sort_value(sort_by, direction)
68
+ mapping = self.class.sortable_mapping[sort_by]
69
+ if mapping.is_a? Symbol
70
+ { mapping => sort_direction }
71
+ elsif mapping.is_a? String
72
+ mapping.gsub(':dir', sort_direction.to_s)
73
+ elsif mapping.is_a? Array
74
+ mapping.map { |att| [att, direction] }.to_h
75
+ elsif mapping.is_a? Proc
76
+ mapping.call(direction)
77
+ else
78
+ logger.debug("WARNING: Sort attribute '#{sort_by}' is not recognized. Did you mean to pass it to sortable_by?")
79
+ nil
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,37 @@
1
+ module SortableBy
2
+ module TableHelper
3
+
4
+ # Build a table header for a model
5
+ #
6
+ # * path_helper: The helper method you want to use to generate URLs.
7
+ # * model: The class we should use for translations (optional)
8
+ # * permit: Array of request params that are forwarded to the sort links
9
+ #
10
+ # Example:
11
+ #
12
+ # <%= sortable_header :admin_users_path, model: User do |t| %>
13
+ # <%= t.sortable :name %>
14
+ # <%= t.sortable :email %>
15
+ # <%= t.header :last_login %>
16
+ # <th></th>
17
+ # <% end %>
18
+ #
19
+ # Header labels will be pulled from en.yml. To provide a different
20
+ # label pass the label: option
21
+ #
22
+ # Example:
23
+ #
24
+ # <%= t.header :name, label: 'Full Name' %>
25
+ #
26
+ def sortable_table_header(path_helper, model: nil, permit: [], icon: SortableBy.icon_strategy, &block)
27
+ header = SortableBy::TableHeader.new(
28
+ path_helper: path_helper,
29
+ model: model,
30
+ params: params.permit(permit.concat(SortableBy.params_list)),
31
+ context: self,
32
+ icon: icon)
33
+ header.capture(block) if block
34
+ header.to_html
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ require 'sortable_by/engine'
2
+
3
+ module SortableBy
4
+ require 'sortable_by/configuration'
5
+ require 'sortable_by/table_header'
6
+ end
@@ -0,0 +1,11 @@
1
+ module SortableBy
2
+ include ActiveSupport::Configurable
3
+
4
+ config_accessor :params_list, :icon_strategy
5
+
6
+ # List of params to white list
7
+ self.params_list = [:sort, :dir, :search, :tab]
8
+
9
+ # Default icon strategy to be used
10
+ self.icon_strategy = :glyph
11
+ end
@@ -0,0 +1,7 @@
1
+ module SortableBy
2
+ class Engine < ::Rails::Engine
3
+ initializer :controller do
4
+ ActionController::Base.send(:include, SortableBy::Params)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SortableBy
4
+ class TableHeader
5
+ attr_reader :context
6
+ attr_accessor :header_html
7
+
8
+ # Forward view helper methods to the view context
9
+ delegate :concat, :content_tag, :link_to, to: :context
10
+
11
+ def initialize(path_helper:, model: nil, params: {}, context:, icon:)
12
+ @path_helper = path_helper
13
+ @model = model
14
+ @params = params
15
+ @context = context
16
+ @icon = icon.to_sym
17
+ end
18
+
19
+ def capture(block)
20
+ @html = context.capture do
21
+ block.call(self)
22
+ end
23
+ end
24
+
25
+ def sortable(attribute, label: nil)
26
+ content_tag :th, class: classes_for_attribute(attribute) do
27
+ concat(link_to_for_attribute(attribute, label))
28
+ concat(sort_arrow_for_attribute(attribute))
29
+ end
30
+ end
31
+
32
+ def header(attribute, label: nil)
33
+ label ||= translated_attribute(attribute)
34
+ content_tag :th, label
35
+ end
36
+
37
+ def to_row
38
+ content_tag(:tr, @html)
39
+ end
40
+
41
+ def to_html
42
+ content_tag :thead, to_row, class: 'tb-table-header'
43
+ end
44
+
45
+ private
46
+
47
+ def translated_attribute(attribute)
48
+ if @model
49
+ @model.human_attribute_name(attribute)
50
+ else
51
+ context.t(attribute)
52
+ end
53
+ end
54
+
55
+ def current_direction
56
+ @params[:dir] || 'asc'
57
+ end
58
+
59
+ def inverted_direction
60
+ current_direction == 'asc' ? 'desc' : 'asc'
61
+ end
62
+
63
+ def determine_direction(attribute)
64
+ if @params[:sort].nil? || @params[:sort] != attribute.to_s
65
+ 'asc'
66
+ else
67
+ inverted_direction
68
+ end
69
+ end
70
+
71
+ def classes_for_attribute(attribute)
72
+ classes = ['sortable']
73
+ if @params[:sort] == attribute.to_s
74
+ classes << 'sortable-active'
75
+ classes << "sortable-#{current_direction}"
76
+ end
77
+ classes.join(' ')
78
+ end
79
+
80
+ def link_to_for_attribute(attribute, label)
81
+ label ||= translated_attribute(attribute)
82
+ path = context.send(
83
+ @path_helper,
84
+ @params.merge(sort: attribute, dir: determine_direction(attribute))
85
+ )
86
+ link_to(label, path)
87
+ end
88
+
89
+ def sort_arrow_for_attribute(attribute)
90
+ return '' unless @params[:sort] == attribute.to_s
91
+ IconStrategy.send(@icon, context, current_direction) if IconStrategy.respond_to?(@icon)
92
+ end
93
+ end
94
+
95
+ module IconStrategy
96
+
97
+ def self.fontawesome(context, dir)
98
+ icon_class = dir == 'asc' ? 'fa-caret-up' : 'fa-caret-down'
99
+ context.content_tag :i, '', class: "fa #{icon_class}"
100
+ end
101
+
102
+ def self.glyph(context, dir)
103
+ icon_class = dir == 'asc' ? 'up' : 'down'
104
+ context.content_tag(:span, '', class: "glyphicon glyphicon-arrow-#{icon_class}")
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module SortableBy
2
+ VERSION = '1.0'.freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :sortable_by do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sortable_by
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Greg Woods
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: database_cleaner
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: This gem adds capabilities to create sortable table headersconnect those
98
+ headers to a back end ActiveRecord sort query
99
+ email:
100
+ - greg.woods@moserit.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - MIT-LICENSE
106
+ - README.md
107
+ - Rakefile
108
+ - app/controllers/concerns/sortable_by/params.rb
109
+ - app/helpers/sortable_by/table_helper.rb
110
+ - lib/sortable_by.rb
111
+ - lib/sortable_by/configuration.rb
112
+ - lib/sortable_by/engine.rb
113
+ - lib/sortable_by/table_header.rb
114
+ - lib/sortable_by/version.rb
115
+ - lib/tasks/sortable_by_tasks.rake
116
+ homepage: https://github.com/moser-inc/sortable_by
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.6.12
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Simple tool for sorting tables of data in rails
140
+ test_files: []