ajax-datatables-rails 0.4.0 → 0.4.1
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 +5 -5
- data/.rubocop.yml +23 -1132
- data/.travis.yml +36 -24
- data/Appraisals +14 -8
- data/CHANGELOG.md +32 -7
- data/Gemfile +2 -0
- data/README.md +400 -335
- data/Rakefile +2 -1
- data/ajax-datatables-rails.gemspec +8 -6
- data/doc/webpack.md +50 -0
- data/gemfiles/{rails_4.1.15.gemfile → rails_4.1.16.gemfile} +1 -1
- data/gemfiles/{rails_4.2.8.gemfile → rails_4.2.10.gemfile} +2 -1
- data/gemfiles/{rails_5.0.3.gemfile → rails_5.0.7.gemfile} +1 -1
- data/gemfiles/{rails_5.1.1.gemfile → rails_5.1.6.gemfile} +1 -1
- data/gemfiles/rails_5.2.0.gemfile +13 -0
- data/lib/ajax-datatables-rails.rb +3 -11
- data/lib/ajax-datatables-rails/base.rb +44 -24
- data/lib/ajax-datatables-rails/config.rb +3 -0
- data/lib/ajax-datatables-rails/datatable/column.rb +33 -125
- data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +77 -0
- data/lib/ajax-datatables-rails/datatable/column/order.rb +29 -0
- data/lib/ajax-datatables-rails/datatable/column/search.rb +88 -0
- data/lib/ajax-datatables-rails/datatable/datatable.rb +10 -7
- data/lib/ajax-datatables-rails/datatable/simple_order.rb +17 -2
- data/lib/ajax-datatables-rails/datatable/simple_search.rb +3 -0
- data/lib/ajax-datatables-rails/orm/active_record.rb +14 -5
- data/lib/ajax-datatables-rails/version.rb +3 -1
- data/lib/ajax_datatables_rails.rb +15 -0
- data/lib/generators/datatable/config_generator.rb +2 -0
- data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +5 -0
- data/lib/generators/rails/datatable_generator.rb +6 -5
- data/lib/generators/rails/templates/datatable.rb +1 -15
- data/spec/ajax-datatables-rails/base_spec.rb +23 -26
- data/spec/ajax-datatables-rails/datatable/column_spec.rb +68 -23
- data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +1 -1
- data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +29 -2
- data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +1 -1
- data/spec/ajax-datatables-rails/extended_spec.rb +3 -3
- data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +94 -35
- data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +6 -6
- data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +43 -0
- data/spec/ajax-datatables-rails/orm/active_record_spec.rb +1 -1
- data/spec/factories/user.rb +1 -1
- data/spec/install_oracle.sh +2 -2
- data/spec/spec_helper.rb +8 -3
- data/spec/support/datatable_cond_date.rb +5 -0
- data/spec/support/datatable_cond_numeric.rb +41 -0
- data/spec/support/datatable_cond_proc.rb +11 -0
- data/spec/support/datatable_cond_string.rb +23 -0
- data/spec/support/datatable_order_nulls_last.rb +5 -0
- data/spec/support/test_helpers.rb +13 -88
- metadata +28 -13
- data/lib/ajax-datatables-rails/datatable/column_date_filter.rb +0 -41
data/Rakefile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# coding: utf-8
|
2
|
-
|
3
|
+
|
4
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
3
5
|
require 'ajax-datatables-rails/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |s|
|
@@ -9,15 +11,15 @@ Gem::Specification.new do |s|
|
|
9
11
|
s.authors = ['Joel Quenneville', 'Antonio Antillon']
|
10
12
|
s.email = ['joel.quenneville@collegeplus.org', 'antillas21@gmail.com']
|
11
13
|
s.homepage = 'https://github.com/jbox-web/ajax-datatables-rails'
|
12
|
-
s.summary =
|
13
|
-
s.description =
|
14
|
+
s.summary = 'A gem that simplifies using datatables and hundreds of records via ajax'
|
15
|
+
s.description = "A wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app"
|
14
16
|
s.license = 'MIT'
|
15
17
|
|
16
18
|
s.add_dependency 'railties', '>= 4.0'
|
17
19
|
|
18
20
|
s.add_development_dependency 'rails', '>= 4.0'
|
19
21
|
s.add_development_dependency 'rake'
|
20
|
-
s.add_development_dependency 'pg'
|
22
|
+
s.add_development_dependency 'pg', '< 1.0'
|
21
23
|
s.add_development_dependency 'mysql2'
|
22
24
|
s.add_development_dependency 'sqlite3'
|
23
25
|
s.add_development_dependency 'activerecord-oracle_enhanced-adapter'
|
@@ -26,12 +28,12 @@ Gem::Specification.new do |s|
|
|
26
28
|
s.add_development_dependency 'pry'
|
27
29
|
s.add_development_dependency 'simplecov'
|
28
30
|
s.add_development_dependency 'database_cleaner'
|
29
|
-
s.add_development_dependency '
|
31
|
+
s.add_development_dependency 'factory_bot'
|
30
32
|
s.add_development_dependency 'faker'
|
31
33
|
s.add_development_dependency 'appraisal'
|
32
34
|
|
33
35
|
s.files = `git ls-files`.split("\n")
|
34
36
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
35
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
37
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
36
38
|
s.require_paths = ['lib']
|
37
39
|
end
|
data/doc/webpack.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
### Webpack
|
2
|
+
|
3
|
+
We assume here that Bootstrap and FontAwesome are already installed with Webpack.
|
4
|
+
|
5
|
+
Inspired by https://datatables.net/download and completed :
|
6
|
+
|
7
|
+
Add npm packages :
|
8
|
+
|
9
|
+
```sh
|
10
|
+
$ yarn add datatables.net
|
11
|
+
$ yarn add datatables.net-bs
|
12
|
+
$ yarn add datatables.net-buttons
|
13
|
+
$ yarn add datatables.net-buttons-bs
|
14
|
+
$ yarn add datatables.net-responsive
|
15
|
+
$ yarn add datatables.net-responsive-bs
|
16
|
+
$ yarn add datatables.net-select
|
17
|
+
$ yarn add datatables.net-select-bs
|
18
|
+
```
|
19
|
+
|
20
|
+
In `config/webpack/loaders/datatables.js` :
|
21
|
+
|
22
|
+
```js
|
23
|
+
module.exports = {
|
24
|
+
test: /datatables\.net.*/,
|
25
|
+
loader: 'imports-loader?define=>false'
|
26
|
+
}
|
27
|
+
```
|
28
|
+
|
29
|
+
In `config/webpack/environment.js` :
|
30
|
+
|
31
|
+
```js
|
32
|
+
const { environment } = require('@rails/webpacker')
|
33
|
+
const datatables = require('./loaders/datatables')
|
34
|
+
environment.loaders.append('datatables', datatables)
|
35
|
+
module.exports = environment
|
36
|
+
```
|
37
|
+
|
38
|
+
in `app/javascript/pack/application.js` :
|
39
|
+
|
40
|
+
```js
|
41
|
+
// Load Datatables
|
42
|
+
require('datatables.net-bs')(window, $)
|
43
|
+
require('datatables.net-buttons-bs')(window, $)
|
44
|
+
require('datatables.net-buttons/js/buttons.colVis.js')(window, $)
|
45
|
+
require('datatables.net-buttons/js/buttons.html5.js')(window, $)
|
46
|
+
require('datatables.net-buttons/js/buttons.print.js')(window, $)
|
47
|
+
require('datatables.net-responsive-bs')(window, $)
|
48
|
+
require('datatables.net-select')(window, $)
|
49
|
+
// require('yadcf')(window, $) // Uncomment if you use yadcf (need a recent version of yadcf)
|
50
|
+
```
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rails", "5.2.0"
|
6
|
+
gem "activerecord-oracle_enhanced-adapter", "~> 5.2.0"
|
7
|
+
gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem "codeclimate-test-reporter", "~> 1.0.0"
|
11
|
+
end
|
12
|
+
|
13
|
+
gemspec path: "../"
|
@@ -1,11 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require 'ajax-datatables-rails/base'
|
5
|
-
require 'ajax-datatables-rails/datatable/datatable'
|
6
|
-
require 'ajax-datatables-rails/datatable/simple_search'
|
7
|
-
require 'ajax-datatables-rails/datatable/simple_order'
|
8
|
-
require 'ajax-datatables-rails/datatable/column_date_filter' unless AjaxDatatablesRails.old_rails?
|
9
|
-
require 'ajax-datatables-rails/datatable/column'
|
10
|
-
require 'ajax-datatables-rails/orm/active_record'
|
11
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ajax_datatables_rails'
|
@@ -1,39 +1,37 @@
|
|
1
|
-
|
2
|
-
class NotImplemented < StandardError; end
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
module AjaxDatatablesRails
|
4
4
|
class Base
|
5
5
|
extend Forwardable
|
6
6
|
|
7
7
|
attr_reader :view, :options
|
8
8
|
def_delegator :@view, :params
|
9
9
|
|
10
|
+
GLOBAL_SEARCH_DELIMITER = ' '
|
11
|
+
|
10
12
|
def initialize(view, options = {})
|
11
13
|
@view = view
|
12
14
|
@options = options
|
13
15
|
load_orm_extension
|
14
16
|
end
|
15
17
|
|
16
|
-
def config
|
17
|
-
@config ||= AjaxDatatablesRails.config
|
18
|
-
end
|
19
|
-
|
20
18
|
def datatable
|
21
19
|
@datatable ||= Datatable::Datatable.new(self)
|
22
20
|
end
|
23
21
|
|
24
22
|
def view_columns
|
25
|
-
|
23
|
+
raise(NotImplementedError, view_columns_error_text)
|
26
24
|
end
|
27
25
|
|
28
26
|
def get_raw_records
|
29
|
-
|
27
|
+
raise(NotImplementedError, raw_records_error_text)
|
30
28
|
end
|
31
29
|
|
32
30
|
def data
|
33
|
-
|
31
|
+
raise(NotImplementedError, data_error_text)
|
34
32
|
end
|
35
33
|
|
36
|
-
def
|
34
|
+
def additional_data
|
37
35
|
{}
|
38
36
|
end
|
39
37
|
|
@@ -42,7 +40,7 @@ module AjaxDatatablesRails
|
|
42
40
|
recordsTotal: records_total_count,
|
43
41
|
recordsFiltered: records_filtered_count,
|
44
42
|
data: sanitize(data)
|
45
|
-
}.merge(
|
43
|
+
}.merge(get_additional_data)
|
46
44
|
end
|
47
45
|
|
48
46
|
def records
|
@@ -72,12 +70,29 @@ module AjaxDatatablesRails
|
|
72
70
|
|
73
71
|
private
|
74
72
|
|
73
|
+
# This method is necessary for smooth transition from
|
74
|
+
# `additinonal_datas` method to `additional_data`
|
75
|
+
# without breaking change.
|
76
|
+
def get_additional_data
|
77
|
+
if respond_to?(:additional_datas)
|
78
|
+
puts <<-WARNING
|
79
|
+
`additional_datas` has been deprecated and
|
80
|
+
will be removed in next major version update!
|
81
|
+
Please use `additional_data` instead.
|
82
|
+
WARNING
|
83
|
+
|
84
|
+
additional_datas
|
85
|
+
else
|
86
|
+
additional_data
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
75
90
|
def sanitize(data)
|
76
91
|
data.map do |record|
|
77
92
|
if record.is_a?(Array)
|
78
93
|
record.map { |td| ERB::Util.html_escape(td) }
|
79
94
|
else
|
80
|
-
record.update(record){ |_, v| ERB::Util.html_escape(v) }
|
95
|
+
record.update(record) { |_, v| ERB::Util.html_escape(v) }
|
81
96
|
end
|
82
97
|
end
|
83
98
|
end
|
@@ -91,48 +106,53 @@ module AjaxDatatablesRails
|
|
91
106
|
end
|
92
107
|
|
93
108
|
def records_total_count
|
94
|
-
|
109
|
+
fetch_records.count(:all)
|
95
110
|
end
|
96
111
|
|
97
112
|
def records_filtered_count
|
98
|
-
filter_records(
|
113
|
+
filter_records(fetch_records).count(:all)
|
99
114
|
end
|
100
115
|
|
101
116
|
# Private helper methods
|
102
117
|
def load_orm_extension
|
103
|
-
case config.orm
|
104
|
-
when :
|
105
|
-
|
106
|
-
|
118
|
+
case AjaxDatatablesRails.config.orm
|
119
|
+
when :active_record
|
120
|
+
extend ORM::ActiveRecord
|
121
|
+
when :mongoid
|
107
122
|
nil
|
108
123
|
end
|
109
124
|
end
|
110
125
|
|
126
|
+
def global_search_delimiter
|
127
|
+
GLOBAL_SEARCH_DELIMITER
|
128
|
+
end
|
129
|
+
|
111
130
|
def raw_records_error_text
|
112
|
-
|
131
|
+
<<-ERROR
|
113
132
|
|
114
133
|
You should implement this method in your class and specify
|
115
134
|
how records are going to be retrieved from the database.
|
116
|
-
|
135
|
+
ERROR
|
117
136
|
end
|
118
137
|
|
119
138
|
def data_error_text
|
120
|
-
|
139
|
+
<<-ERROR
|
121
140
|
|
122
141
|
You should implement this method in your class and return an array
|
123
142
|
of arrays, or an array of hashes, as defined in the jQuery.dataTables
|
124
143
|
plugin documentation.
|
125
|
-
|
144
|
+
ERROR
|
126
145
|
end
|
127
146
|
|
128
147
|
def view_columns_error_text
|
129
|
-
|
148
|
+
<<-ERROR
|
130
149
|
|
131
150
|
You should implement this method in your class and return an array
|
132
151
|
of database columns based on the columns displayed in the HTML view.
|
133
152
|
These columns should be represented in the ModelName.column_name,
|
134
153
|
or aliased_join_table.column_name notation.
|
135
|
-
|
154
|
+
ERROR
|
136
155
|
end
|
156
|
+
|
137
157
|
end
|
138
158
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/configurable'
|
2
4
|
|
3
5
|
module AjaxDatatablesRails
|
@@ -24,5 +26,6 @@ module AjaxDatatablesRails
|
|
24
26
|
|
25
27
|
config_accessor(:orm) { :active_record }
|
26
28
|
config_accessor(:db_adapter) { :postgresql }
|
29
|
+
config_accessor(:nulls_last) { false }
|
27
30
|
end
|
28
31
|
end
|
@@ -1,167 +1,75 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AjaxDatatablesRails
|
4
4
|
module Datatable
|
5
5
|
class Column
|
6
|
+
|
7
|
+
DB_ADAPTER_TYPE_CAST = {
|
8
|
+
mysql: 'CHAR',
|
9
|
+
mysql2: 'CHAR',
|
10
|
+
sqlite: 'TEXT',
|
11
|
+
sqlite3: 'TEXT',
|
12
|
+
oracle: 'VARCHAR2(4000)',
|
13
|
+
oracleenhanced: 'VARCHAR2(4000)'
|
14
|
+
}.freeze
|
15
|
+
|
6
16
|
attr_reader :datatable, :index, :options
|
17
|
+
attr_writer :search
|
18
|
+
|
19
|
+
include Search
|
20
|
+
include Order
|
21
|
+
prepend DateFilter unless AjaxDatatablesRails.old_rails?
|
7
22
|
|
8
|
-
unless AjaxDatatablesRails.old_rails?
|
9
|
-
prepend ColumnDateFilter
|
10
|
-
end
|
11
23
|
|
12
24
|
def initialize(datatable, index, options)
|
13
|
-
@datatable
|
14
|
-
@
|
25
|
+
@datatable = datatable
|
26
|
+
@index = index
|
27
|
+
@options = options
|
28
|
+
@view_column = datatable.view_columns[options[:data].to_sym]
|
15
29
|
end
|
16
30
|
|
17
31
|
def data
|
18
32
|
options[:data].presence || options[:name]
|
19
33
|
end
|
20
34
|
|
21
|
-
def searchable?
|
22
|
-
@view_column.fetch(:searchable, true)
|
23
|
-
end
|
24
|
-
|
25
|
-
def orderable?
|
26
|
-
@view_column.fetch(:orderable, true)
|
27
|
-
end
|
28
|
-
|
29
|
-
def search
|
30
|
-
@search ||= SimpleSearch.new(options[:search])
|
31
|
-
end
|
32
|
-
|
33
|
-
def searched?
|
34
|
-
search.value.present?
|
35
|
-
end
|
36
|
-
|
37
|
-
def search=(value)
|
38
|
-
@search = value
|
39
|
-
end
|
40
|
-
|
41
|
-
def cond
|
42
|
-
@view_column[:cond] || :like
|
43
|
-
end
|
44
|
-
|
45
|
-
def filter(value)
|
46
|
-
@view_column[:cond].call(value)
|
47
|
-
end
|
48
|
-
|
49
35
|
def source
|
50
36
|
@view_column[:source]
|
51
37
|
end
|
52
38
|
|
53
|
-
# Add sort_field option to allow overriding of sort field
|
54
|
-
def sort_field
|
55
|
-
@view_column[:sort_field] || field
|
56
|
-
end
|
57
|
-
|
58
|
-
# Add formater option to allow modification of the value
|
59
|
-
# before passing it to the database
|
60
|
-
def formater
|
61
|
-
@view_column[:formater]
|
62
|
-
end
|
63
|
-
|
64
|
-
# Add use_regex option to allow bypassing of regex search
|
65
|
-
def use_regex?
|
66
|
-
@view_column.fetch(:use_regex, true)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Add delimiter option to handle range search
|
70
|
-
def delimiter
|
71
|
-
@view_column[:delimiter] || '-'
|
72
|
-
end
|
73
|
-
|
74
39
|
def table
|
75
|
-
model
|
76
|
-
model.arel_table rescue model
|
40
|
+
model.respond_to?(:arel_table) ? model.arel_table : model
|
77
41
|
end
|
78
42
|
|
79
|
-
def
|
80
|
-
source.split('.').
|
81
|
-
end
|
82
|
-
|
83
|
-
def search_query
|
84
|
-
search.regexp? ? regex_search : non_regex_search
|
43
|
+
def model
|
44
|
+
@model ||= source.split('.').first.constantize
|
85
45
|
end
|
86
46
|
|
87
|
-
def
|
88
|
-
|
47
|
+
def field
|
48
|
+
@field ||= source.split('.').last.to_sym
|
89
49
|
end
|
90
50
|
|
91
|
-
private
|
92
|
-
|
93
51
|
def custom_field?
|
94
52
|
!source.include?('.')
|
95
53
|
end
|
96
54
|
|
97
|
-
|
98
|
-
|
55
|
+
# Add formater option to allow modification of the value
|
56
|
+
# before passing it to the database
|
57
|
+
def formater
|
58
|
+
@view_column[:formater]
|
99
59
|
end
|
100
60
|
|
101
61
|
def formated_value
|
102
62
|
formater ? formater.call(search.value) : search.value
|
103
63
|
end
|
104
64
|
|
105
|
-
|
106
|
-
# Unfortunately regex_search doesn't work when filtering on primary keys with integer.
|
107
|
-
# It generates this kind of query : AND ("regions"."id" ~ '2|3') which throws an error :
|
108
|
-
# operator doesn't exist : integer ~ unknown
|
109
|
-
# The solution is to bypass regex_search and use non_regex_search with :in operator
|
110
|
-
def regex_search
|
111
|
-
if use_regex?
|
112
|
-
::Arel::Nodes::Regexp.new((custom_field? ? field : table[field]), ::Arel::Nodes.build_quoted(formated_value))
|
113
|
-
else
|
114
|
-
non_regex_search
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def non_regex_search
|
119
|
-
case cond
|
120
|
-
when Proc
|
121
|
-
filter(formated_value)
|
122
|
-
when :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in
|
123
|
-
numeric_search
|
124
|
-
when :null_value
|
125
|
-
null_value_search
|
126
|
-
when :start_with
|
127
|
-
casted_column.matches("#{formated_value}%")
|
128
|
-
when :end_with
|
129
|
-
casted_column.matches("%#{formated_value}")
|
130
|
-
when :like
|
131
|
-
casted_column.matches("%#{formated_value}%")
|
132
|
-
else
|
133
|
-
nil
|
134
|
-
end
|
135
|
-
end
|
65
|
+
private
|
136
66
|
|
137
|
-
def
|
138
|
-
|
139
|
-
when :oracle, :oracleenhanced then 'VARCHAR2(4000)'
|
140
|
-
when :mysql, :mysql2 then 'CHAR'
|
141
|
-
when :sqlite, :sqlite3 then 'TEXT'
|
142
|
-
else
|
143
|
-
'VARCHAR'
|
144
|
-
end
|
67
|
+
def type_cast
|
68
|
+
@type_cast ||= (DB_ADAPTER_TYPE_CAST[AjaxDatatablesRails.config.db_adapter] || 'VARCHAR')
|
145
69
|
end
|
146
70
|
|
147
71
|
def casted_column
|
148
|
-
::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(
|
149
|
-
end
|
150
|
-
|
151
|
-
def null_value_search
|
152
|
-
if formated_value == '!NULL'
|
153
|
-
table[field].not_eq(nil)
|
154
|
-
else
|
155
|
-
table[field].eq(nil)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def numeric_search
|
160
|
-
if custom_field?
|
161
|
-
::Arel::Nodes::SqlLiteral.new(field).eq(formated_value)
|
162
|
-
else
|
163
|
-
table[field].send(cond, formated_value)
|
164
|
-
end
|
72
|
+
@casted_column ||= ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(type_cast)])
|
165
73
|
end
|
166
74
|
|
167
75
|
end
|