ajax-datatables-rails 1.2.0 → 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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +120 -0
  3. data/.rubocop.yml +3 -1
  4. data/Appraisals +7 -13
  5. data/CHANGELOG.md +23 -2
  6. data/README.md +51 -70
  7. data/ajax-datatables-rails.gemspec +11 -5
  8. data/doc/migrate.md +44 -0
  9. data/doc/webpack.md +4 -1
  10. data/gemfiles/{rails_5.2.3.gemfile → rails_5.2.4.gemfile} +1 -1
  11. data/gemfiles/{rails_6.0.1.gemfile → rails_6.0.3.gemfile} +1 -1
  12. data/gemfiles/{rails_5.0.7.gemfile → rails_6.1.0.gemfile} +3 -3
  13. data/lib/ajax-datatables-rails.rb +0 -16
  14. data/lib/ajax-datatables-rails/base.rb +38 -13
  15. data/lib/ajax-datatables-rails/datatable.rb +6 -0
  16. data/lib/ajax-datatables-rails/datatable/column.rb +59 -21
  17. data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +1 -1
  18. data/lib/ajax-datatables-rails/datatable/column/search.rb +2 -2
  19. data/lib/ajax-datatables-rails/datatable/datatable.rb +17 -3
  20. data/lib/ajax-datatables-rails/datatable/simple_order.rb +7 -5
  21. data/lib/ajax-datatables-rails/error.rb +9 -0
  22. data/lib/ajax-datatables-rails/orm.rb +6 -0
  23. data/lib/ajax-datatables-rails/orm/active_record.rb +9 -10
  24. data/lib/ajax-datatables-rails/version.rb +1 -1
  25. data/spec/ajax-datatables-rails/base_spec.rb +77 -120
  26. data/spec/ajax-datatables-rails/datatable/column_spec.rb +30 -10
  27. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +61 -22
  28. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +11 -9
  29. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +352 -257
  30. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +4 -4
  31. data/spec/install_oracle.sh +4 -4
  32. data/spec/spec_helper.rb +10 -19
  33. data/spec/support/datatables/complex_datatable.rb +29 -0
  34. data/spec/support/datatables/complex_datatable_array.rb +14 -0
  35. data/spec/support/{datatable_cond_date.rb → datatables/datatable_cond_date.rb} +0 -0
  36. data/spec/support/{datatable_cond_numeric.rb → datatables/datatable_cond_numeric.rb} +0 -0
  37. data/spec/support/{datatable_cond_proc.rb → datatables/datatable_cond_proc.rb} +0 -0
  38. data/spec/support/{datatable_cond_string.rb → datatables/datatable_cond_string.rb} +0 -0
  39. data/spec/support/datatables/datatable_cond_unknown.rb +5 -0
  40. data/spec/support/{datatable_order_nulls_last.rb → datatables/datatable_order_nulls_last.rb} +0 -0
  41. data/spec/support/{test_helpers.rb → helpers/params.rb} +8 -46
  42. data/spec/support/{test_models.rb → models/user.rb} +0 -0
  43. metadata +53 -49
  44. data/.travis.yml +0 -57
  45. data/gemfiles/rails_5.1.7.gemfile +0 -11
  46. data/lib/ajax-datatables-rails/configuration.rb +0 -10
  47. data/lib/generators/datatable/config_generator.rb +0 -19
  48. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +0 -9
  49. data/spec/ajax-datatables-rails/configuration_spec.rb +0 -34
  50. data/spec/ajax-datatables-rails/extended_spec.rb +0 -19
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Error
5
+ class BaseError < StandardError; end
6
+ class InvalidSearchColumn < BaseError; end
7
+ class InvalidSearchCondition < BaseError; end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module ORM
5
+ end
6
+ end
@@ -4,10 +4,6 @@ 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
@@ -30,23 +26,26 @@ module AjaxDatatablesRails
30
26
  # ----------------- SEARCH HELPER METHODS --------------------
31
27
 
32
28
  def build_conditions
33
- if datatable.searchable?
34
- build_conditions_for_datatable
35
- else
36
- build_conditions_for_selected_columns
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 << searchable_columns.map do |simple_column|
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)
@@ -8,7 +8,7 @@ module AjaxDatatablesRails
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 1
11
- MINOR = 2
11
+ MINOR = 3
12
12
  TINY = 0
13
13
  PRE = nil
14
14
 
@@ -13,7 +13,7 @@ describe AjaxDatatablesRails::Base do
13
13
  end
14
14
  end
15
15
 
16
- context 'Public API' do
16
+ describe 'User API' do
17
17
  describe '#view_columns' do
18
18
  it 'raises an error if not defined by the user' do
19
19
  datatable = described_class.new(sample_params)
@@ -81,165 +81,122 @@ describe AjaxDatatablesRails::Base do
81
81
  end
82
82
  end
83
83
  end
84
-
85
- describe '#as_json' do
86
- let(:datatable) { ComplexDatatable.new(sample_params) }
87
-
88
- it 'should return a hash' do
89
- create_list(:user, 5)
90
- data = datatable.as_json
91
- expect(data[:recordsTotal]).to eq 5
92
- expect(data[:recordsFiltered]).to eq 5
93
- expect(data[:data]).to be_a(Array)
94
- expect(data[:data].size).to eq 5
95
- end
96
-
97
- context 'with additional_data' do
98
- it 'should return a hash' do
99
- create_list(:user, 5)
100
- expect(datatable).to receive(:additional_data){ { foo: 'bar' } }
101
- data = datatable.as_json
102
- expect(data[:recordsTotal]).to eq 5
103
- expect(data[:recordsFiltered]).to eq 5
104
- expect(data[:data]).to be_a(Array)
105
- expect(data[:data].size).to eq 5
106
- expect(data[:foo]).to eq 'bar'
107
- end
108
- end
109
- end
110
-
111
- describe '#filter_records' do
112
- let(:records) { User.all }
113
-
114
- let(:datatable) do
115
- datatable = Class.new(ComplexDatatable) do
116
- def filter_records(records)
117
- raise NotImplementedError
118
- end
119
- end
120
- datatable.new(sample_params)
121
- end
122
-
123
- it 'should allow method override' do
124
- expect { datatable.filter_records(records) }.to raise_error(NotImplementedError)
125
- end
126
- end
127
-
128
- describe '#sort_records' do
129
- let(:records) { User.all }
130
-
131
- let(:datatable) do
132
- datatable = Class.new(ComplexDatatable) do
133
- def sort_records(records)
134
- raise NotImplementedError
135
- end
136
- end
137
- datatable.new(sample_params)
138
- end
139
-
140
- it 'should allow method override' do
141
- expect { datatable.sort_records(records) }.to raise_error(NotImplementedError)
142
- end
143
- end
144
-
145
- describe '#paginate_records' do
146
- let(:records) { User.all }
147
-
148
- let(:datatable) do
149
- datatable = Class.new(ComplexDatatable) do
150
- def paginate_records(records)
151
- raise NotImplementedError
152
- end
153
- end
154
- datatable.new(sample_params)
155
- end
156
-
157
- it 'should allow method override' do
158
- expect { datatable.paginate_records(records) }.to raise_error(NotImplementedError)
159
- end
160
- end
161
84
  end
162
85
 
163
-
164
- context 'Private API' do
165
- context 'when orm is not implemented' do
86
+ describe 'ORM API' do
87
+ context 'when ORM is not implemented' do
166
88
  let(:datatable) { AjaxDatatablesRails::Base.new(sample_params) }
167
89
 
168
90
  describe '#fetch_records' do
169
91
  it 'raises an error if it does not include an ORM module' do
170
- expect { datatable.fetch_records }.to raise_error NoMethodError
92
+ expect { datatable.fetch_records }.to raise_error NotImplementedError
171
93
  end
172
94
  end
173
95
 
174
96
  describe '#filter_records' do
175
97
  it 'raises an error if it does not include an ORM module' do
176
- expect { datatable.filter_records }.to raise_error NoMethodError
98
+ expect { datatable.filter_records([]) }.to raise_error NotImplementedError
177
99
  end
178
100
  end
179
101
 
180
102
  describe '#sort_records' do
181
103
  it 'raises an error if it does not include an ORM module' do
182
- expect { datatable.sort_records }.to raise_error NoMethodError
104
+ expect { datatable.sort_records([]) }.to raise_error NotImplementedError
183
105
  end
184
106
  end
185
107
 
186
108
  describe '#paginate_records' do
187
109
  it 'raises an error if it does not include an ORM module' do
188
- expect { datatable.paginate_records }.to raise_error NoMethodError
110
+ expect { datatable.paginate_records([]) }.to raise_error NotImplementedError
189
111
  end
190
112
  end
191
113
  end
192
114
 
193
- describe 'helper methods' do
194
- describe '#offset' do
195
- it 'defaults to 0' do
196
- datatable = described_class.new({})
197
- expect(datatable.datatable.send(:offset)).to eq(0)
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)
198
132
  end
199
133
 
200
- it 'matches the value on view params[:start]' do
201
- datatable = described_class.new({ start: '11' })
202
- expect(datatable.datatable.send(:offset)).to eq(11)
134
+ describe '#filter_records' do
135
+ it {
136
+ expect { datatable.filter_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
137
+ }
203
138
  end
204
- end
205
139
 
206
- describe '#page' do
207
- it 'calculates page number from params[:start] and #per_page' do
208
- datatable = described_class.new({ start: '11' })
209
- expect(datatable.datatable.send(:page)).to eq(2)
140
+ describe '#sort_records' do
141
+ it {
142
+ expect { datatable.sort_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
143
+ }
210
144
  end
211
- end
212
145
 
213
- describe '#per_page' do
214
- it 'defaults to 10' do
215
- datatable = described_class.new(sample_params)
216
- expect(datatable.datatable.send(:per_page)).to eq(10)
146
+ describe '#paginate_records' do
147
+ it {
148
+ expect { datatable.paginate_records([]) }.to raise_error(NotImplementedError).with_message('FOO')
149
+ }
217
150
  end
151
+ end
152
+ end
153
+ end
154
+
155
+ describe 'JSON format' do
156
+ describe '#as_json' do
157
+ let(:datatable) { ComplexDatatable.new(sample_params) }
158
+
159
+ it 'should return a hash' do
160
+ create_list(:user, 5)
161
+ data = datatable.as_json
162
+ expect(data[:recordsTotal]).to eq 5
163
+ expect(data[:recordsFiltered]).to eq 5
164
+ expect(data[:data]).to be_a(Array)
165
+ expect(data[:data].size).to eq 5
166
+ end
218
167
 
219
- it 'matches the value on view params[:length]' do
220
- other_view = { length: 20 }
221
- datatable = described_class.new(other_view)
222
- expect(datatable.datatable.send(:per_page)).to eq(20)
168
+ context 'with additional_data' do
169
+ it 'should return a hash' do
170
+ create_list(:user, 5)
171
+ expect(datatable).to receive(:additional_data){ { foo: 'bar' } }
172
+ data = datatable.as_json
173
+ expect(data[:recordsTotal]).to eq 5
174
+ expect(data[:recordsFiltered]).to eq 5
175
+ expect(data[:data]).to be_a(Array)
176
+ expect(data[:data].size).to eq 5
177
+ expect(data[:foo]).to eq 'bar'
223
178
  end
224
179
  end
180
+ end
181
+ end
225
182
 
226
- describe '#column_id' do
227
- let(:datatable) { ComplexDatatable.new(sample_params) }
183
+ describe 'User helper methods' do
184
+ describe '#column_id' do
185
+ let(:datatable) { ComplexDatatable.new(sample_params) }
228
186
 
229
- it 'should return column id from view_columns hash' do
230
- expect(datatable.send(:column_id, :username)).to eq(0)
231
- expect(datatable.send(:column_id, 'username')).to eq(0)
232
- end
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)
233
190
  end
191
+ end
234
192
 
235
- describe '#column_data' do
236
- let(:datatable) { ComplexDatatable.new(sample_params) }
237
- before { datatable.params[:columns]['0'][:search][:value] = 'doe' }
193
+ describe '#column_data' do
194
+ let(:datatable) { ComplexDatatable.new(sample_params) }
195
+ before { datatable.params[:columns]['0'][:search][:value] = 'doe' }
238
196
 
239
- it 'should return column data from params' do
240
- expect(datatable.send(:column_data, :username)).to eq('doe')
241
- expect(datatable.send(:column_data, 'username')).to eq('doe')
242
- 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')
243
200
  end
244
201
  end
245
202
  end
@@ -153,53 +153,73 @@ describe AjaxDatatablesRails::Datatable::Column do
153
153
  let(:column) { datatable.datatable.columns.first }
154
154
 
155
155
  it 'returns VARCHAR if :db_adapter is :pg' do
156
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :pg }
156
+ expect(datatable).to receive(:db_adapter) { :pg }
157
157
  expect(column.send(:type_cast)).to eq('VARCHAR')
158
158
  end
159
159
 
160
160
  it 'returns VARCHAR if :db_adapter is :postgre' do
161
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :postgre }
161
+ expect(datatable).to receive(:db_adapter) { :postgre }
162
162
  expect(column.send(:type_cast)).to eq('VARCHAR')
163
163
  end
164
164
 
165
165
  it 'returns VARCHAR if :db_adapter is :postgresql' do
166
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :postgresql }
166
+ expect(datatable).to receive(:db_adapter) { :postgresql }
167
167
  expect(column.send(:type_cast)).to eq('VARCHAR')
168
168
  end
169
169
 
170
170
  it 'returns VARCHAR2(4000) if :db_adapter is :oracle' do
171
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :oracle }
171
+ expect(datatable).to receive(:db_adapter) { :oracle }
172
172
  expect(column.send(:type_cast)).to eq('VARCHAR2(4000)')
173
173
  end
174
174
 
175
175
  it 'returns VARCHAR2(4000) if :db_adapter is :oracleenhanced' do
176
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :oracleenhanced }
176
+ expect(datatable).to receive(:db_adapter) { :oracleenhanced }
177
177
  expect(column.send(:type_cast)).to eq('VARCHAR2(4000)')
178
178
  end
179
179
 
180
180
  it 'returns CHAR if :db_adapter is :mysql2' do
181
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :mysql2 }
181
+ expect(datatable).to receive(:db_adapter) { :mysql2 }
182
182
  expect(column.send(:type_cast)).to eq('CHAR')
183
183
  end
184
184
 
185
185
  it 'returns CHAR if :db_adapter is :mysql' do
186
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :mysql }
186
+ expect(datatable).to receive(:db_adapter) { :mysql }
187
187
  expect(column.send(:type_cast)).to eq('CHAR')
188
188
  end
189
189
 
190
190
  it 'returns TEXT if :db_adapter is :sqlite' do
191
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :sqlite }
191
+ expect(datatable).to receive(:db_adapter) { :sqlite }
192
192
  expect(column.send(:type_cast)).to eq('TEXT')
193
193
  end
194
194
 
195
195
  it 'returns TEXT if :db_adapter is :sqlite3' do
196
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :sqlite3 }
196
+ expect(datatable).to receive(:db_adapter) { :sqlite3 }
197
197
  expect(column.send(:type_cast)).to eq('TEXT')
198
198
  end
199
199
 
200
200
  it 'returns VARCHAR(4000) if :db_adapter is :sqlserver' do
201
- allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :sqlserver }
201
+ expect(datatable).to receive(:db_adapter) { :sqlserver }
202
202
  expect(column.send(:type_cast)).to eq('VARCHAR(4000)')
203
203
  end
204
204
  end
205
+
206
+ describe 'when empty column' do
207
+ before do
208
+ datatable.params[:columns] = {'0'=>{'data'=>'', 'name'=>'', 'searchable'=>'true', 'orderable'=>'true', 'search'=>{'value'=>'searchvalue', 'regex'=>'false'}}}
209
+ end
210
+
211
+ it 'raises error' do
212
+ expect { datatable.to_json }.to raise_error(AjaxDatatablesRails::Error::InvalidSearchColumn).with_message("Unknown column. Check that `data` field is filled on JS side with the column name")
213
+ end
214
+ end
215
+
216
+ describe 'when unknown column' do
217
+ before do
218
+ datatable.params[:columns] = {'0'=>{'data'=>'foo', 'name'=>'', 'searchable'=>'true', 'orderable'=>'true', 'search'=>{'value'=>'searchvalue', 'regex'=>'false'}}}
219
+ end
220
+
221
+ it 'raises error' do
222
+ expect { datatable.to_json }.to raise_error(AjaxDatatablesRails::Error::InvalidSearchColumn).with_message("Check that column 'foo' exists in view_columns")
223
+ end
224
+ end
205
225
  end
@@ -3,9 +3,11 @@ require 'spec_helper'
3
3
  describe AjaxDatatablesRails::Datatable::Datatable do
4
4
 
5
5
  let(:datatable) { ComplexDatatable.new(sample_params).datatable }
6
+ let(:datatable_json) { ComplexDatatable.new(sample_params_json).datatable }
6
7
  let(:order_option) { {'0'=>{'column'=>'0', 'dir'=>'asc'}, '1'=>{'column'=>'1', 'dir'=>'desc'}} }
8
+ let(:order_option_json) { [{'column'=>'0', 'dir'=>'asc'}, {'column'=>'1', 'dir'=>'desc'}] }
7
9
 
8
- describe 'order methods' do
10
+ shared_examples 'order methods' do
9
11
  it 'should be orderable' do
10
12
  expect(datatable.orderable?).to eq(true)
11
13
  end
@@ -35,6 +37,28 @@ describe AjaxDatatablesRails::Datatable::Datatable do
35
37
  end
36
38
  end
37
39
 
40
+ shared_examples 'columns methods' do
41
+ it 'should have 4 columns' do
42
+ expect(datatable.columns.count).to eq(6)
43
+ end
44
+
45
+ it 'child class' do
46
+ expect(datatable.columns.first).to be_a(AjaxDatatablesRails::Datatable::Column)
47
+ end
48
+ end
49
+
50
+ describe 'with query params' do
51
+ it_behaves_like 'order methods'
52
+ it_behaves_like 'columns methods'
53
+ end
54
+
55
+ describe 'with json params' do
56
+ let(:order_option) { order_option_json }
57
+ let(:datatable) { datatable_json }
58
+ it_behaves_like 'order methods'
59
+ it_behaves_like 'columns methods'
60
+ end
61
+
38
62
  describe 'search methods' do
39
63
  it 'should be searchable' do
40
64
  datatable.options[:search][:value] = 'atom'
@@ -51,36 +75,51 @@ describe AjaxDatatablesRails::Datatable::Datatable do
51
75
  end
52
76
  end
53
77
 
54
- describe 'columns methods' do
55
- it 'should have 4 columns' do
56
- expect(datatable.columns.count).to eq(6)
78
+ describe 'option methods' do
79
+ describe '#paginate?' do
80
+ it {
81
+ expect(datatable.paginate?).to be(true)
82
+ }
57
83
  end
58
84
 
59
- it 'child class' do
60
- expect(datatable.columns.first).to be_a(AjaxDatatablesRails::Datatable::Column)
61
- end
62
- end
85
+ describe '#per_page' do
86
+ context 'when params[:length] is missing' do
87
+ it 'defaults to 10' do
88
+ expect(datatable.per_page).to eq(10)
89
+ end
90
+ end
63
91
 
64
- describe 'option methods' do
65
- before :each do
66
- datatable.options[:start] = '50'
67
- datatable.options[:length] = '15'
68
- end
92
+ context 'when params[:length] is passed' do
93
+ let(:datatable) { ComplexDatatable.new({ length: '20' }).datatable }
69
94
 
70
- it 'paginate?' do
71
- expect(datatable.paginate?).to be(true)
95
+ it 'matches the value on view params[:length]' do
96
+ expect(datatable.per_page).to eq(20)
97
+ end
98
+ end
72
99
  end
73
100
 
74
- it 'offset' do
75
- expect(datatable.offset).to eq(50)
76
- end
101
+ describe '#offset' do
102
+ context 'when params[:start] is missing' do
103
+ it 'defaults to 0' do
104
+ expect(datatable.offset).to eq(0)
105
+ end
106
+ end
107
+
108
+ context 'when params[:start] is passed' do
109
+ let(:datatable) { ComplexDatatable.new({ start: '11' }).datatable }
77
110
 
78
- it 'page' do
79
- expect(datatable.page).to eq(4)
111
+ it 'matches the value on view params[:start]' do
112
+ expect(datatable.offset).to eq(11)
113
+ end
114
+ end
80
115
  end
81
116
 
82
- it 'per_page' do
83
- expect(datatable.per_page).to eq(15)
117
+ describe '#page' do
118
+ let(:datatable) { ComplexDatatable.new({ start: '11' }).datatable }
119
+
120
+ it 'calculates page number from params[:start] and #per_page' do
121
+ expect(datatable.page).to eq(2)
122
+ end
84
123
  end
85
124
  end
86
125
  end