ajax-datatables-rails 0.4.2 → 1.3.0
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 +56 -4
- data/Gemfile +0 -5
- data/Guardfile +16 -0
- data/README.md +223 -97
- data/Rakefile +1 -0
- data/ajax-datatables-rails.gemspec +24 -20
- data/bin/_guard-core +29 -0
- data/bin/appraisal +29 -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 +36 -23
- data/lib/ajax-datatables-rails/datatable/datatable.rb +16 -6
- 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 +95 -79
- data/spec/ajax-datatables-rails/datatable/column_spec.rb +83 -12
- data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +62 -24
- data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +32 -12
- data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +329 -221
- data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +5 -6
- data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +12 -11
- data/spec/ajax-datatables-rails/orm/active_record_spec.rb +3 -4
- data/spec/install_oracle.sh +9 -3
- data/spec/spec_helper.rb +10 -21
- data/spec/support/datatables/complex_datatable.rb +29 -0
- data/spec/support/datatables/complex_datatable_array.rb +14 -0
- data/spec/support/{datatable_cond_date.rb → datatables/datatable_cond_date.rb} +0 -0
- data/spec/support/{datatable_cond_numeric.rb → datatables/datatable_cond_numeric.rb} +1 -1
- data/spec/support/{datatable_cond_proc.rb → datatables/datatable_cond_proc.rb} +0 -0
- data/spec/support/datatables/datatable_cond_string.rb +41 -0
- data/spec/support/datatables/datatable_cond_unknown.rb +5 -0
- data/spec/support/{datatable_order_nulls_last.rb → datatables/datatable_order_nulls_last.rb} +0 -0
- data/spec/support/{test_helpers.rb → helpers/params.rb} +14 -41
- data/spec/support/{test_models.rb → models/user.rb} +0 -0
- metadata +75 -74
- 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/support/datatable_cond_string.rb +0 -23
@@ -3,10 +3,8 @@
|
|
3
3
|
module AjaxDatatablesRails
|
4
4
|
module Datatable
|
5
5
|
|
6
|
-
TRUE_VALUE = 'true'
|
7
|
-
|
8
6
|
class Datatable
|
9
|
-
attr_reader :
|
7
|
+
attr_reader :options
|
10
8
|
|
11
9
|
def initialize(datatable)
|
12
10
|
@datatable = datatable
|
@@ -43,7 +41,7 @@ module AjaxDatatablesRails
|
|
43
41
|
|
44
42
|
def columns
|
45
43
|
@columns ||= get_param(:columns).map do |index, column_options|
|
46
|
-
Column.new(datatable, index, column_options)
|
44
|
+
Column.new(@datatable, index, column_options)
|
47
45
|
end
|
48
46
|
end
|
49
47
|
|
@@ -70,13 +68,25 @@ module AjaxDatatablesRails
|
|
70
68
|
end
|
71
69
|
|
72
70
|
def get_param(param)
|
73
|
-
if
|
74
|
-
|
71
|
+
return {} if options[param].nil?
|
72
|
+
|
73
|
+
if options[param].is_a? Array
|
74
|
+
hash = {}
|
75
|
+
options[param].each_with_index { |value, index| hash[index] = value }
|
76
|
+
hash
|
75
77
|
else
|
76
78
|
options[param].to_unsafe_h.with_indifferent_access
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
82
|
+
def db_adapter
|
83
|
+
@datatable.db_adapter
|
84
|
+
end
|
85
|
+
|
86
|
+
def nulls_last
|
87
|
+
@datatable.nulls_last
|
88
|
+
end
|
89
|
+
|
80
90
|
end
|
81
91
|
end
|
82
92
|
end
|
@@ -4,19 +4,19 @@ module AjaxDatatablesRails
|
|
4
4
|
module Datatable
|
5
5
|
class SimpleOrder
|
6
6
|
|
7
|
-
|
7
|
+
DIRECTION_ASC = 'ASC'
|
8
|
+
DIRECTION_DESC = 'DESC'
|
9
|
+
DIRECTIONS = [DIRECTION_ASC, DIRECTION_DESC].freeze
|
8
10
|
|
9
11
|
def initialize(datatable, options = {})
|
10
|
-
@datatable
|
11
|
-
@options
|
12
|
+
@datatable = datatable
|
13
|
+
@options = options
|
14
|
+
@adapter = datatable.db_adapter
|
15
|
+
@nulls_last = datatable.nulls_last
|
12
16
|
end
|
13
17
|
|
14
18
|
def query(sort_column)
|
15
|
-
|
16
|
-
"CASE WHEN #{sort_column} IS NULL THEN 1 ELSE 0 END, #{sort_column} #{direction}"
|
17
|
-
else
|
18
|
-
"#{sort_column} #{direction}"
|
19
|
-
end
|
19
|
+
[sort_column, direction, nulls_last_sql].compact.join(' ')
|
20
20
|
end
|
21
21
|
|
22
22
|
def column
|
@@ -24,7 +24,7 @@ module AjaxDatatablesRails
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def direction
|
27
|
-
DIRECTIONS.find { |dir| dir == column_direction } ||
|
27
|
+
DIRECTIONS.find { |dir| dir == column_direction } || DIRECTION_ASC
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -38,7 +38,20 @@ module AjaxDatatablesRails
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def sort_nulls_last?
|
41
|
-
column.nulls_last? ||
|
41
|
+
column.nulls_last? || @nulls_last == true
|
42
|
+
end
|
43
|
+
|
44
|
+
def nulls_last_sql
|
45
|
+
return unless sort_nulls_last?
|
46
|
+
|
47
|
+
case @adapter
|
48
|
+
when :pg, :postgresql, :postgres, :oracle
|
49
|
+
'NULLS LAST'
|
50
|
+
when :mysql, :mysql2, :sqlite, :sqlite3
|
51
|
+
'IS NULL'
|
52
|
+
else
|
53
|
+
raise "unsupported database adapter: #{@adapter}"
|
54
|
+
end
|
42
55
|
end
|
43
56
|
|
44
57
|
end
|
@@ -4,15 +4,11 @@ module AjaxDatatablesRails
|
|
4
4
|
module ORM
|
5
5
|
module ActiveRecord
|
6
6
|
|
7
|
-
def fetch_records
|
8
|
-
get_raw_records
|
9
|
-
end
|
10
|
-
|
11
7
|
def filter_records(records)
|
12
8
|
records.where(build_conditions)
|
13
9
|
end
|
14
10
|
|
15
|
-
# rubocop:disable Style/EachWithObject
|
11
|
+
# rubocop:disable Style/EachWithObject, Style/SafeNavigation
|
16
12
|
def sort_records(records)
|
17
13
|
sort_by = datatable.orders.inject([]) do |queries, order|
|
18
14
|
column = order.column
|
@@ -21,7 +17,7 @@ module AjaxDatatablesRails
|
|
21
17
|
end
|
22
18
|
records.order(Arel.sql(sort_by.join(', ')))
|
23
19
|
end
|
24
|
-
# rubocop:enable Style/EachWithObject
|
20
|
+
# rubocop:enable Style/EachWithObject, Style/SafeNavigation
|
25
21
|
|
26
22
|
def paginate_records(records)
|
27
23
|
records.offset(datatable.offset).limit(datatable.per_page)
|
@@ -30,23 +26,26 @@ module AjaxDatatablesRails
|
|
30
26
|
# ----------------- SEARCH HELPER METHODS --------------------
|
31
27
|
|
32
28
|
def build_conditions
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
@build_conditions ||= begin
|
30
|
+
criteria = [build_conditions_for_selected_columns]
|
31
|
+
criteria << build_conditions_for_datatable if datatable.searchable?
|
32
|
+
criteria.compact.reduce(:and)
|
37
33
|
end
|
38
34
|
end
|
39
35
|
|
36
|
+
# rubocop:disable Metrics/AbcSize
|
40
37
|
def build_conditions_for_datatable
|
38
|
+
columns = searchable_columns.reject(&:searched?)
|
41
39
|
criteria = search_for.inject([]) do |crit, atom|
|
42
40
|
search = Datatable::SimpleSearch.new(value: atom, regex: datatable.search.regexp?)
|
43
|
-
crit <<
|
41
|
+
crit << columns.map do |simple_column|
|
44
42
|
simple_column.search = search
|
45
43
|
simple_column.search_query
|
46
|
-
end.reduce(:or)
|
44
|
+
end.compact.reduce(:or)
|
47
45
|
end.compact.reduce(:and)
|
48
46
|
criteria
|
49
47
|
end
|
48
|
+
# rubocop:enable Metrics/AbcSize
|
50
49
|
|
51
50
|
def build_conditions_for_selected_columns
|
52
51
|
search_columns.map(&:search_query).compact.reduce(:and)
|
@@ -1,5 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AjaxDatatablesRails
|
4
|
-
|
4
|
+
|
5
|
+
def self.gem_version
|
6
|
+
Gem::Version.new VERSION::STRING
|
7
|
+
end
|
8
|
+
|
9
|
+
module VERSION
|
10
|
+
MAJOR = 1
|
11
|
+
MINOR = 3
|
12
|
+
TINY = 0
|
13
|
+
PRE = nil
|
14
|
+
|
15
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
16
|
+
end
|
5
17
|
end
|
@@ -3,30 +3,26 @@ require 'spec_helper'
|
|
3
3
|
describe AjaxDatatablesRails::Base do
|
4
4
|
|
5
5
|
describe 'an instance' do
|
6
|
-
|
7
|
-
|
8
|
-
it 'requires a view_context' do
|
6
|
+
it 'requires a hash of params' do
|
9
7
|
expect { described_class.new }.to raise_error ArgumentError
|
10
8
|
end
|
11
9
|
|
12
10
|
it 'accepts an options hash' do
|
13
|
-
datatable = described_class.new(
|
11
|
+
datatable = described_class.new(sample_params, foo: 'bar')
|
14
12
|
expect(datatable.options).to eq(foo: 'bar')
|
15
13
|
end
|
16
14
|
end
|
17
15
|
|
18
|
-
|
19
|
-
let(:view) { double('view', params: sample_params) }
|
20
|
-
|
16
|
+
describe 'User API' do
|
21
17
|
describe '#view_columns' do
|
22
18
|
it 'raises an error if not defined by the user' do
|
23
|
-
datatable = described_class.new(
|
19
|
+
datatable = described_class.new(sample_params)
|
24
20
|
expect { datatable.view_columns }.to raise_error NotImplementedError
|
25
21
|
end
|
26
22
|
|
27
23
|
context 'child class implements view_columns' do
|
28
24
|
it 'expects a hash based defining columns' do
|
29
|
-
datatable = ComplexDatatable.new(
|
25
|
+
datatable = ComplexDatatable.new(sample_params)
|
30
26
|
expect(datatable.view_columns).to be_a(Hash)
|
31
27
|
end
|
32
28
|
end
|
@@ -34,19 +30,19 @@ describe AjaxDatatablesRails::Base do
|
|
34
30
|
|
35
31
|
describe '#get_raw_records' do
|
36
32
|
it 'raises an error if not defined by the user' do
|
37
|
-
datatable = described_class.new(
|
33
|
+
datatable = described_class.new(sample_params)
|
38
34
|
expect { datatable.get_raw_records }.to raise_error NotImplementedError
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
42
38
|
describe '#data' do
|
43
39
|
it 'raises an error if not defined by the user' do
|
44
|
-
datatable = described_class.new(
|
40
|
+
datatable = described_class.new(sample_params)
|
45
41
|
expect { datatable.data }.to raise_error NotImplementedError
|
46
42
|
end
|
47
43
|
|
48
44
|
context 'when data is defined as a hash' do
|
49
|
-
let(:datatable) { ComplexDatatable.new(
|
45
|
+
let(:datatable) { ComplexDatatable.new(sample_params) }
|
50
46
|
|
51
47
|
it 'should return an array of hashes' do
|
52
48
|
create_list(:user, 5)
|
@@ -58,7 +54,7 @@ describe AjaxDatatablesRails::Base do
|
|
58
54
|
|
59
55
|
it 'should html escape data' do
|
60
56
|
create(:user, first_name: 'Name "><img src=x onerror=alert("first_name")>', last_name: 'Name "><img src=x onerror=alert("last_name")>')
|
61
|
-
data = datatable.send(:
|
57
|
+
data = datatable.send(:sanitize_data, datatable.data)
|
62
58
|
item = data.first
|
63
59
|
expect(item[:first_name]).to eq 'Name "><img src=x onerror=alert("first_name")>'
|
64
60
|
expect(item[:last_name]).to eq 'Name "><img src=x onerror=alert("last_name")>'
|
@@ -66,7 +62,7 @@ describe AjaxDatatablesRails::Base do
|
|
66
62
|
end
|
67
63
|
|
68
64
|
context 'when data is defined as a array' do
|
69
|
-
let(:datatable) { ComplexDatatableArray.new(
|
65
|
+
let(:datatable) { ComplexDatatableArray.new(sample_params) }
|
70
66
|
|
71
67
|
it 'should return an array of arrays' do
|
72
68
|
create_list(:user, 5)
|
@@ -78,16 +74,87 @@ describe AjaxDatatablesRails::Base do
|
|
78
74
|
|
79
75
|
it 'should html escape data' do
|
80
76
|
create(:user, first_name: 'Name "><img src=x onerror=alert("first_name")>', last_name: 'Name "><img src=x onerror=alert("last_name")>')
|
81
|
-
data = datatable.send(:
|
77
|
+
data = datatable.send(:sanitize_data, datatable.data)
|
82
78
|
item = data.first
|
83
79
|
expect(item[2]).to eq 'Name "><img src=x onerror=alert("first_name")>'
|
84
80
|
expect(item[3]).to eq 'Name "><img src=x onerror=alert("last_name")>'
|
85
81
|
end
|
86
82
|
end
|
87
83
|
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'ORM API' do
|
87
|
+
context 'when ORM is not implemented' do
|
88
|
+
let(:datatable) { AjaxDatatablesRails::Base.new(sample_params) }
|
89
|
+
|
90
|
+
describe '#fetch_records' do
|
91
|
+
it 'raises an error if it does not include an ORM module' do
|
92
|
+
expect { datatable.fetch_records }.to raise_error NotImplementedError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#filter_records' do
|
97
|
+
it 'raises an error if it does not include an ORM module' do
|
98
|
+
expect { datatable.filter_records([]) }.to raise_error NotImplementedError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#sort_records' do
|
103
|
+
it 'raises an error if it does not include an ORM module' do
|
104
|
+
expect { datatable.sort_records([]) }.to raise_error NotImplementedError
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#paginate_records' do
|
109
|
+
it 'raises an error if it does not include an ORM module' do
|
110
|
+
expect { datatable.paginate_records([]) }.to raise_error NotImplementedError
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
88
114
|
|
115
|
+
context 'when ORM is implemented' do
|
116
|
+
describe 'it allows method override' do
|
117
|
+
let(:datatable) do
|
118
|
+
datatable = Class.new(ComplexDatatable) do
|
119
|
+
def filter_records(records)
|
120
|
+
raise NotImplementedError.new('FOO')
|
121
|
+
end
|
122
|
+
|
123
|
+
def sort_records(records)
|
124
|
+
raise NotImplementedError.new('FOO')
|
125
|
+
end
|
126
|
+
|
127
|
+
def paginate_records(records)
|
128
|
+
raise NotImplementedError.new('FOO')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
datatable.new(sample_params)
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#filter_records' do
|
135
|
+
it {
|
136
|
+
expect { datatable.filter_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#sort_records' do
|
141
|
+
it {
|
142
|
+
expect { datatable.sort_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#paginate_records' do
|
147
|
+
it {
|
148
|
+
expect { datatable.paginate_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'JSON format' do
|
89
156
|
describe '#as_json' do
|
90
|
-
let(:datatable) { ComplexDatatable.new(
|
157
|
+
let(:datatable) { ComplexDatatable.new(sample_params) }
|
91
158
|
|
92
159
|
it 'should return a hash' do
|
93
160
|
create_list(:user, 5)
|
@@ -113,74 +180,23 @@ describe AjaxDatatablesRails::Base do
|
|
113
180
|
end
|
114
181
|
end
|
115
182
|
|
183
|
+
describe 'User helper methods' do
|
184
|
+
describe '#column_id' do
|
185
|
+
let(:datatable) { ComplexDatatable.new(sample_params) }
|
116
186
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
let(:datatable) { ComplexDatatable.new(view) }
|
121
|
-
|
122
|
-
before(:each) do
|
123
|
-
allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:orm) { nil }
|
124
|
-
end
|
125
|
-
|
126
|
-
describe '#fetch_records' do
|
127
|
-
it 'raises an error if it does not include an ORM module' do
|
128
|
-
expect { datatable.send(:fetch_records) }.to raise_error NoMethodError
|
187
|
+
it 'should return column id from view_columns hash' do
|
188
|
+
expect(datatable.column_id(:username)).to eq(0)
|
189
|
+
expect(datatable.column_id('username')).to eq(0)
|
129
190
|
end
|
130
191
|
end
|
131
192
|
|
132
|
-
describe '#
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
describe '#sort_records' do
|
139
|
-
it 'raises an error if it does not include an ORM module' do
|
140
|
-
expect { datatable.send(:sort_records) }.to raise_error NoMethodError
|
141
|
-
end
|
142
|
-
end
|
193
|
+
describe '#column_data' do
|
194
|
+
let(:datatable) { ComplexDatatable.new(sample_params) }
|
195
|
+
before { datatable.params[:columns]['0'][:search][:value] = 'doe' }
|
143
196
|
|
144
|
-
|
145
|
-
|
146
|
-
expect
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
describe 'helper methods' do
|
151
|
-
describe '#offset' do
|
152
|
-
it 'defaults to 0' do
|
153
|
-
default_view = double('view', params: {})
|
154
|
-
datatable = described_class.new(default_view)
|
155
|
-
expect(datatable.datatable.send(:offset)).to eq(0)
|
156
|
-
end
|
157
|
-
|
158
|
-
it 'matches the value on view params[:start]' do
|
159
|
-
paginated_view = double('view', params: { start: '11' })
|
160
|
-
datatable = described_class.new(paginated_view)
|
161
|
-
expect(datatable.datatable.send(:offset)).to eq(11)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe '#page' do
|
166
|
-
it 'calculates page number from params[:start] and #per_page' do
|
167
|
-
paginated_view = double('view', params: { start: '11' })
|
168
|
-
datatable = described_class.new(paginated_view)
|
169
|
-
expect(datatable.datatable.send(:page)).to eq(2)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
describe '#per_page' do
|
174
|
-
it 'defaults to 10' do
|
175
|
-
datatable = described_class.new(view)
|
176
|
-
expect(datatable.datatable.send(:per_page)).to eq(10)
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'matches the value on view params[:length]' do
|
180
|
-
other_view = double('view', params: { length: 20 })
|
181
|
-
datatable = described_class.new(other_view)
|
182
|
-
expect(datatable.datatable.send(:per_page)).to eq(20)
|
183
|
-
end
|
197
|
+
it 'should return column data from params' do
|
198
|
+
expect(datatable.column_data(:username)).to eq('doe')
|
199
|
+
expect(datatable.column_data('username')).to eq('doe')
|
184
200
|
end
|
185
201
|
end
|
186
202
|
end
|