ajax-datatables-rails 0.4.3 → 1.3.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 +4 -4
- data/.github/workflows/ci.yml +120 -0
- data/.rubocop.yml +17 -7
- data/Appraisals +15 -20
- data/CHANGELOG.md +54 -1
- data/Gemfile +0 -5
- data/Guardfile +16 -0
- data/README.md +238 -112
- data/Rakefile +1 -0
- data/ajax-datatables-rails.gemspec +24 -20
- data/bin/_guard-core +29 -0
- data/bin/appraisal +29 -0
- data/bin/bundle +114 -0
- data/bin/guard +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/doc/migrate.md +97 -0
- data/doc/webpack.md +7 -2
- data/gemfiles/{rails_5.2.0.gemfile → rails_5.2.4.gemfile} +3 -5
- data/gemfiles/{rails_5.0.7.gemfile → rails_6.0.3.gemfile} +4 -6
- data/gemfiles/{rails_5.1.6.gemfile → rails_6.1.0.gemfile} +4 -6
- data/lib/ajax-datatables-rails.rb +12 -1
- data/lib/ajax-datatables-rails/active_record.rb +7 -0
- data/lib/ajax-datatables-rails/base.rb +47 -46
- data/lib/ajax-datatables-rails/datatable.rb +6 -0
- data/lib/ajax-datatables-rails/datatable/column.rb +65 -20
- data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +12 -21
- data/lib/ajax-datatables-rails/datatable/column/order.rb +1 -1
- data/lib/ajax-datatables-rails/datatable/column/search.rb +37 -22
- data/lib/ajax-datatables-rails/datatable/datatable.rb +16 -7
- data/lib/ajax-datatables-rails/datatable/simple_order.rb +23 -10
- data/lib/ajax-datatables-rails/datatable/simple_search.rb +2 -0
- data/lib/ajax-datatables-rails/error.rb +9 -0
- data/lib/ajax-datatables-rails/orm.rb +6 -0
- data/lib/ajax-datatables-rails/orm/active_record.rb +11 -12
- data/lib/ajax-datatables-rails/version.rb +13 -1
- data/lib/generators/rails/templates/datatable.rb +1 -1
- data/spec/ajax-datatables-rails/base_spec.rb +129 -93
- data/spec/ajax-datatables-rails/datatable/column_spec.rb +105 -37
- data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +71 -31
- data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +36 -14
- data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +4 -2
- data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +315 -272
- data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +9 -8
- data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +17 -14
- data/spec/factories/user.rb +3 -1
- data/spec/install_oracle.sh +9 -3
- data/spec/spec_helper.rb +33 -28
- data/spec/support/datatables/complex_datatable.rb +31 -0
- data/spec/support/datatables/complex_datatable_array.rb +16 -0
- data/spec/support/{datatable_cond_date.rb → datatables/datatable_cond_date.rb} +2 -0
- data/spec/support/{datatable_cond_numeric.rb → datatables/datatable_cond_numeric.rb} +3 -1
- data/spec/support/{datatable_cond_proc.rb → datatables/datatable_cond_proc.rb} +2 -0
- data/spec/support/{datatable_cond_string.rb → datatables/datatable_cond_string.rb} +9 -1
- data/spec/support/datatables/datatable_cond_unknown.rb +7 -0
- data/spec/support/{datatable_order_nulls_last.rb → datatables/datatable_order_nulls_last.rb} +2 -0
- data/spec/support/{test_helpers.rb → helpers/params.rb} +17 -42
- data/spec/support/{test_models.rb → models/user.rb} +2 -0
- data/spec/support/schema.rb +3 -1
- metadata +76 -75
- data/.travis.yml +0 -80
- data/gemfiles/rails_4.0.13.gemfile +0 -14
- data/gemfiles/rails_4.1.16.gemfile +0 -14
- data/gemfiles/rails_4.2.10.gemfile +0 -14
- data/lib/ajax-datatables-rails/config.rb +0 -31
- data/lib/ajax_datatables_rails.rb +0 -15
- data/lib/generators/datatable/config_generator.rb +0 -19
- data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +0 -12
- data/spec/ajax-datatables-rails/configuration_spec.rb +0 -43
- data/spec/ajax-datatables-rails/extended_spec.rb +0 -20
- data/spec/ajax-datatables-rails/orm/active_record_spec.rb +0 -25
data/doc/webpack.md
CHANGED
@@ -5,7 +5,9 @@ We assume here that Bootstrap and FontAwesome are already installed with Webpack
|
|
5
5
|
Inspired by https://datatables.net/download and completed :
|
6
6
|
|
7
7
|
Add npm packages :
|
8
|
-
|
8
|
+
```sh
|
9
|
+
$ yarn add imports-loader
|
10
|
+
```
|
9
11
|
```sh
|
10
12
|
$ yarn add datatables.net
|
11
13
|
$ yarn add datatables.net-bs
|
@@ -22,7 +24,10 @@ In `config/webpack/loaders/datatables.js` :
|
|
22
24
|
```js
|
23
25
|
module.exports = {
|
24
26
|
test: /datatables\.net.*/,
|
25
|
-
loader: 'imports-loader
|
27
|
+
loader: 'imports-loader',
|
28
|
+
options: {
|
29
|
+
additionalCode: 'var define = false;'
|
30
|
+
}
|
26
31
|
}
|
27
32
|
```
|
28
33
|
|
@@ -2,12 +2,10 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "5.2.
|
5
|
+
gem "rails", "5.2.4"
|
6
6
|
gem "activerecord-oracle_enhanced-adapter", "~> 5.2.0"
|
7
|
+
gem "sqlite3", "~> 1.3.0"
|
8
|
+
gem "mysql2"
|
7
9
|
gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
|
8
10
|
|
9
|
-
group :test do
|
10
|
-
gem "codeclimate-test-reporter", "~> 1.0.0"
|
11
|
-
end
|
12
|
-
|
13
11
|
gemspec path: "../"
|
@@ -2,12 +2,10 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "
|
6
|
-
gem "activerecord-oracle_enhanced-adapter", "~>
|
5
|
+
gem "rails", "6.0.3"
|
6
|
+
gem "activerecord-oracle_enhanced-adapter", "~> 6.0.0"
|
7
|
+
gem "sqlite3", "~> 1.4.0"
|
8
|
+
gem "mysql2"
|
7
9
|
gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
|
8
10
|
|
9
|
-
group :test do
|
10
|
-
gem "codeclimate-test-reporter", "~> 1.0.0"
|
11
|
-
end
|
12
|
-
|
13
11
|
gemspec path: "../"
|
@@ -2,12 +2,10 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "
|
6
|
-
gem "activerecord-oracle_enhanced-adapter", "~> 1.
|
5
|
+
gem "rails", "6.1.0"
|
6
|
+
gem "activerecord-oracle_enhanced-adapter", "~> 6.1.0"
|
7
|
+
gem "sqlite3", "~> 1.4.0"
|
8
|
+
gem "mysql2"
|
7
9
|
gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
|
8
10
|
|
9
|
-
group :test do
|
10
|
-
gem "codeclimate-test-reporter", "~> 1.0.0"
|
11
|
-
end
|
12
|
-
|
13
11
|
gemspec path: "../"
|
@@ -1,3 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'zeitwerk'
|
4
|
+
loader = Zeitwerk::Loader.for_gem
|
5
|
+
generators = "#{__dir__}/generators"
|
6
|
+
loader.ignore(generators)
|
7
|
+
loader.inflector.inflect(
|
8
|
+
'orm' => 'ORM',
|
9
|
+
'ajax-datatables-rails' => 'AjaxDatatablesRails'
|
10
|
+
)
|
11
|
+
loader.setup
|
12
|
+
|
13
|
+
module AjaxDatatablesRails
|
14
|
+
end
|
@@ -2,23 +2,21 @@
|
|
2
2
|
|
3
3
|
module AjaxDatatablesRails
|
4
4
|
class Base
|
5
|
-
extend Forwardable
|
6
5
|
|
7
|
-
|
8
|
-
|
6
|
+
class_attribute :db_adapter, default: ::ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
7
|
+
class_attribute :nulls_last, default: false
|
9
8
|
|
10
|
-
|
9
|
+
attr_reader :params, :options, :datatable
|
11
10
|
|
12
|
-
|
13
|
-
@view = view
|
14
|
-
@options = options
|
15
|
-
load_orm_extension
|
16
|
-
end
|
11
|
+
GLOBAL_SEARCH_DELIMITER = ' '
|
17
12
|
|
18
|
-
def
|
19
|
-
@
|
13
|
+
def initialize(params, options = {})
|
14
|
+
@params = params
|
15
|
+
@options = options
|
16
|
+
@datatable = Datatable::Datatable.new(self)
|
20
17
|
end
|
21
18
|
|
19
|
+
# User defined methods
|
22
20
|
def view_columns
|
23
21
|
raise(NotImplementedError, view_columns_error_text)
|
24
22
|
end
|
@@ -31,22 +29,49 @@ module AjaxDatatablesRails
|
|
31
29
|
raise(NotImplementedError, data_error_text)
|
32
30
|
end
|
33
31
|
|
32
|
+
# ORM defined methods
|
33
|
+
def fetch_records
|
34
|
+
get_raw_records
|
35
|
+
end
|
36
|
+
|
37
|
+
def filter_records(records)
|
38
|
+
raise(NotImplementedError)
|
39
|
+
end
|
40
|
+
|
41
|
+
def sort_records(records)
|
42
|
+
raise(NotImplementedError)
|
43
|
+
end
|
44
|
+
|
45
|
+
def paginate_records(records)
|
46
|
+
raise(NotImplementedError)
|
47
|
+
end
|
48
|
+
|
49
|
+
# User overides
|
34
50
|
def additional_data
|
35
51
|
{}
|
36
52
|
end
|
37
53
|
|
54
|
+
# JSON structure sent to jQuery DataTables
|
38
55
|
def as_json(*)
|
39
56
|
{
|
40
|
-
recordsTotal:
|
57
|
+
recordsTotal: records_total_count,
|
41
58
|
recordsFiltered: records_filtered_count,
|
42
|
-
data:
|
43
|
-
}.merge(
|
59
|
+
data: sanitize_data(data),
|
60
|
+
}.merge(additional_data)
|
44
61
|
end
|
45
62
|
|
46
|
-
|
47
|
-
|
63
|
+
# User helper methods
|
64
|
+
def column_id(name)
|
65
|
+
view_columns.keys.index(name.to_sym)
|
66
|
+
end
|
67
|
+
|
68
|
+
def column_data(column)
|
69
|
+
id = column_id(column)
|
70
|
+
params.dig('columns', id.to_s, 'search', 'value')
|
48
71
|
end
|
49
72
|
|
73
|
+
private
|
74
|
+
|
50
75
|
# helper methods
|
51
76
|
def connected_columns
|
52
77
|
@connected_columns ||= begin
|
@@ -68,26 +93,7 @@ module AjaxDatatablesRails
|
|
68
93
|
end
|
69
94
|
end
|
70
95
|
|
71
|
-
|
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
|
-
|
90
|
-
def sanitize(data)
|
96
|
+
def sanitize_data(data)
|
91
97
|
data.map do |record|
|
92
98
|
if record.is_a?(Array)
|
93
99
|
record.map { |td| ERB::Util.html_escape(td) }
|
@@ -97,6 +103,11 @@ module AjaxDatatablesRails
|
|
97
103
|
end
|
98
104
|
end
|
99
105
|
|
106
|
+
# called from within #data
|
107
|
+
def records
|
108
|
+
@records ||= retrieve_records
|
109
|
+
end
|
110
|
+
|
100
111
|
def retrieve_records
|
101
112
|
records = fetch_records
|
102
113
|
records = filter_records(records)
|
@@ -113,16 +124,6 @@ module AjaxDatatablesRails
|
|
113
124
|
filter_records(fetch_records).count(:all)
|
114
125
|
end
|
115
126
|
|
116
|
-
# Private helper methods
|
117
|
-
def load_orm_extension
|
118
|
-
case AjaxDatatablesRails.config.orm
|
119
|
-
when :active_record
|
120
|
-
extend ORM::ActiveRecord
|
121
|
-
when :mongoid
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
127
|
def global_search_delimiter
|
127
128
|
GLOBAL_SEARCH_DELIMITER
|
128
129
|
end
|
@@ -4,28 +4,23 @@ module AjaxDatatablesRails
|
|
4
4
|
module Datatable
|
5
5
|
class Column
|
6
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
|
-
|
16
|
-
attr_reader :datatable, :index, :options
|
17
|
-
attr_writer :search
|
18
|
-
|
19
7
|
include Search
|
20
8
|
include Order
|
21
|
-
|
9
|
+
include DateFilter
|
22
10
|
|
11
|
+
attr_reader :datatable, :index, :options
|
12
|
+
attr_writer :search
|
23
13
|
|
24
14
|
def initialize(datatable, index, options)
|
25
15
|
@datatable = datatable
|
26
16
|
@index = index
|
27
17
|
@options = options
|
28
|
-
@view_column = datatable.view_columns[
|
18
|
+
@view_column = datatable.view_columns[column_name]
|
19
|
+
validate_settings!
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_name
|
23
|
+
@column_name ||= options[:data]&.to_sym
|
29
24
|
end
|
30
25
|
|
31
26
|
def data
|
@@ -52,26 +47,76 @@ module AjaxDatatablesRails
|
|
52
47
|
!source.include?('.')
|
53
48
|
end
|
54
49
|
|
55
|
-
# Add
|
50
|
+
# Add formatter option to allow modification of the value
|
56
51
|
# before passing it to the database
|
57
|
-
def
|
58
|
-
@view_column[:
|
52
|
+
def formatter
|
53
|
+
@view_column[:formatter]
|
59
54
|
end
|
60
55
|
|
61
|
-
def
|
62
|
-
|
56
|
+
def formatted_value
|
57
|
+
formatter ? formatter.call(search.value) : search.value
|
63
58
|
end
|
64
59
|
|
65
60
|
private
|
66
61
|
|
62
|
+
TYPE_CAST_DEFAULT = 'VARCHAR'
|
63
|
+
TYPE_CAST_MYSQL = 'CHAR'
|
64
|
+
TYPE_CAST_SQLITE = 'TEXT'
|
65
|
+
TYPE_CAST_ORACLE = 'VARCHAR2(4000)'
|
66
|
+
TYPE_CAST_SQLSERVER = 'VARCHAR(4000)'
|
67
|
+
|
68
|
+
DB_ADAPTER_TYPE_CAST = {
|
69
|
+
mysql: TYPE_CAST_MYSQL,
|
70
|
+
mysql2: TYPE_CAST_MYSQL,
|
71
|
+
sqlite: TYPE_CAST_SQLITE,
|
72
|
+
sqlite3: TYPE_CAST_SQLITE,
|
73
|
+
oracle: TYPE_CAST_ORACLE,
|
74
|
+
oracleenhanced: TYPE_CAST_ORACLE,
|
75
|
+
sqlserver: TYPE_CAST_SQLSERVER,
|
76
|
+
}.freeze
|
77
|
+
|
78
|
+
private_constant :TYPE_CAST_DEFAULT
|
79
|
+
private_constant :TYPE_CAST_MYSQL
|
80
|
+
private_constant :TYPE_CAST_SQLITE
|
81
|
+
private_constant :TYPE_CAST_ORACLE
|
82
|
+
private_constant :TYPE_CAST_SQLSERVER
|
83
|
+
private_constant :DB_ADAPTER_TYPE_CAST
|
84
|
+
|
67
85
|
def type_cast
|
68
|
-
@type_cast ||=
|
86
|
+
@type_cast ||= DB_ADAPTER_TYPE_CAST.fetch(datatable.db_adapter, TYPE_CAST_DEFAULT)
|
69
87
|
end
|
70
88
|
|
71
89
|
def casted_column
|
72
90
|
@casted_column ||= ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(type_cast)])
|
73
91
|
end
|
74
92
|
|
93
|
+
def validate_settings!
|
94
|
+
raise AjaxDatatablesRails::Error::InvalidSearchColumn, "Unknown column. Check that `data` field is filled on JS side with the column name" if column_name.empty?
|
95
|
+
raise AjaxDatatablesRails::Error::InvalidSearchColumn, "Check that column '#{column_name}' exists in view_columns" unless valid_search_column?(column_name)
|
96
|
+
raise AjaxDatatablesRails::Error::InvalidSearchCondition, cond unless valid_search_condition?(cond)
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_search_column?(column_name)
|
100
|
+
!datatable.view_columns[column_name].nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
VALID_SEARCH_CONDITIONS = [
|
104
|
+
# String condition
|
105
|
+
:start_with, :end_with, :like, :string_eq, :string_in, :null_value,
|
106
|
+
# Numeric condition
|
107
|
+
:eq, :not_eq, :lt, :gt, :lteq, :gteq, :in,
|
108
|
+
# Date condition
|
109
|
+
:date_range
|
110
|
+
].freeze
|
111
|
+
|
112
|
+
private_constant :VALID_SEARCH_CONDITIONS
|
113
|
+
|
114
|
+
def valid_search_condition?(cond)
|
115
|
+
return true if cond.is_a?(Proc)
|
116
|
+
|
117
|
+
VALID_SEARCH_CONDITIONS.include?(cond)
|
118
|
+
end
|
119
|
+
|
75
120
|
end
|
76
121
|
end
|
77
122
|
end
|
@@ -5,6 +5,8 @@ module AjaxDatatablesRails
|
|
5
5
|
class Column
|
6
6
|
module DateFilter
|
7
7
|
|
8
|
+
RANGE_DELIMITER = '-'
|
9
|
+
|
8
10
|
class DateRange
|
9
11
|
attr_reader :begin, :end
|
10
12
|
|
@@ -20,55 +22,44 @@ module AjaxDatatablesRails
|
|
20
22
|
|
21
23
|
# Add delimiter option to handle range search
|
22
24
|
def delimiter
|
23
|
-
@view_column
|
24
|
-
end
|
25
|
-
|
26
|
-
def empty_range_search?
|
27
|
-
(formated_value == delimiter) || (range_start.blank? && range_end.blank?)
|
25
|
+
@delimiter ||= @view_column.fetch(:delimiter, RANGE_DELIMITER)
|
28
26
|
end
|
29
27
|
|
30
28
|
# A range value is in form '<range_start><delimiter><range_end>'
|
31
29
|
# This returns <range_start>
|
32
30
|
def range_start
|
33
|
-
@range_start ||=
|
31
|
+
@range_start ||= formatted_value.split(delimiter)[0]
|
34
32
|
end
|
35
33
|
|
36
34
|
# A range value is in form '<range_start><delimiter><range_end>'
|
37
35
|
# This returns <range_end>
|
38
36
|
def range_end
|
39
|
-
@range_end ||=
|
37
|
+
@range_end ||= formatted_value.split(delimiter)[1]
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty_range_search?
|
41
|
+
(formatted_value == delimiter) || (range_start.blank? && range_end.blank?)
|
40
42
|
end
|
41
43
|
|
42
44
|
# Do a range search
|
43
45
|
def date_range_search
|
44
46
|
return nil if empty_range_search?
|
47
|
+
|
45
48
|
table[field].between(DateRange.new(range_start_casted, range_end_casted))
|
46
49
|
end
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
def non_regex_search
|
51
|
-
if cond == :date_range
|
52
|
-
date_range_search
|
53
|
-
else
|
54
|
-
super
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
53
|
def range_start_casted
|
59
54
|
range_start.blank? ? parse_date('01/01/1970') : parse_date(range_start)
|
60
55
|
end
|
61
56
|
|
62
57
|
def range_end_casted
|
63
|
-
range_end.blank? ?
|
58
|
+
range_end.blank? ? parse_date('9999-12-31 23:59:59') : parse_date("#{range_end} 23:59:59")
|
64
59
|
end
|
65
60
|
|
66
61
|
def parse_date(date)
|
67
|
-
|
68
|
-
Time.zone.parse(date)
|
69
|
-
else
|
70
|
-
Time.parse(date)
|
71
|
-
end
|
62
|
+
Time.zone ? Time.zone.parse(date) : Time.parse(date)
|
72
63
|
end
|
73
64
|
|
74
65
|
end
|