ajax-datatables-rails 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|