datatables-net 0.4.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 (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +73 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +618 -0
  6. data/Rakefile +14 -0
  7. data/lib/ajax-datatables-rails.rb +11 -0
  8. data/lib/ajax-datatables-rails/base.rb +205 -0
  9. data/lib/ajax-datatables-rails/config.rb +24 -0
  10. data/lib/ajax-datatables-rails/datatable/column.rb +106 -0
  11. data/lib/ajax-datatables-rails/datatable/datatable.rb +69 -0
  12. data/lib/ajax-datatables-rails/datatable/simple_order.rb +31 -0
  13. data/lib/ajax-datatables-rails/datatable/simple_search.rb +19 -0
  14. data/lib/ajax-datatables-rails/orm/active_record.rb +52 -0
  15. data/lib/ajax-datatables-rails/version.rb +3 -0
  16. data/lib/generators/datatable/config_generator.rb +17 -0
  17. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +7 -0
  18. data/lib/generators/rails/datatable_generator.rb +27 -0
  19. data/lib/generators/rails/templates/datatable.rb +41 -0
  20. data/spec/ajax-datatables-rails/base_spec.rb +140 -0
  21. data/spec/ajax-datatables-rails/configuration_spec.rb +43 -0
  22. data/spec/ajax-datatables-rails/datatable/column_spec.rb +65 -0
  23. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +97 -0
  24. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +12 -0
  25. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +16 -0
  26. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +154 -0
  27. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +51 -0
  28. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +42 -0
  29. data/spec/ajax-datatables-rails/orm/active_record_spec.rb +34 -0
  30. data/spec/schema.rb +43 -0
  31. data/spec/spec_helper.rb +10 -0
  32. data/spec/test_helpers.rb +66 -0
  33. data/spec/test_models.rb +20 -0
  34. metadata +202 -0
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::Datatable::SimpleOrder' do
4
+ let(:options) { ActiveSupport::HashWithIndifferentAccess.new({"column"=>"1", "dir"=>"desc"}) }
5
+ let(:simple_order) { AjaxDatatablesRails::Datatable::SimpleOrder.new nil, options }
6
+
7
+ describe 'option methods' do
8
+ it 'sql query' do
9
+ expect(simple_order.query('firstname')).to eq('firstname DESC')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::Datatable::SimpleSearch' do
4
+ let(:options) { ActiveSupport::HashWithIndifferentAccess.new({"value"=>"search value", "regex"=>"true"}) }
5
+ let(:simple_search) { AjaxDatatablesRails::Datatable::SimpleSearch.new options }
6
+
7
+ describe 'option methods' do
8
+ it 'regexp?' do
9
+ expect(simple_search.regexp?).to be(true)
10
+ end
11
+
12
+ it 'value' do
13
+ expect(simple_search.value).to eq("search value")
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::ORM::ActiveRecord#filter_records' do
4
+ let(:view) { double('view', params: sample_params) }
5
+ let(:datatable) { ComplexDatatable.new(view) }
6
+
7
+ before(:each) do
8
+ AjaxDatatablesRails.configure do |config|
9
+ config.db_adapter = :sqlite
10
+ config.orm = :active_record
11
+ end
12
+
13
+ User.create(username: 'johndoe', email: 'johndoe@example.com')
14
+ User.create(username: 'msmith', email: 'mary.smith@example.com')
15
+ end
16
+
17
+ after(:each) do
18
+ User.destroy_all
19
+ end
20
+
21
+ describe 'filter records' do
22
+ let(:records) { User.all }
23
+
24
+ it 'requires a records collection as argument' do
25
+ expect { datatable.send(:filter_records) }.to raise_error
26
+ end
27
+
28
+ it 'performs a simple search first' do
29
+ datatable.params[:search] = { value: 'msmith' }
30
+ expect(datatable).to receive(:build_conditions_for_datatable)
31
+ datatable.send(:filter_records, records)
32
+ end
33
+
34
+ it 'performs a composite search second' do
35
+ datatable.params[:search] = { value: '' }
36
+ expect(datatable).to receive(:build_conditions_for_selected_columns)
37
+ datatable.send(:filter_records, records)
38
+ end
39
+
40
+ describe '#build_conditions_for_datatable' do
41
+ it 'returns an Arel object' do
42
+ datatable.params[:search] = { value: 'msmith' }
43
+ result = datatable.send(:build_conditions_for_datatable)
44
+ expect(result).to be_a(Arel::Nodes::Grouping)
45
+ end
46
+
47
+ context 'no search query' do
48
+ it 'returns empty query' do
49
+ datatable.params[:search] = { value: '' }
50
+ expect(datatable.send(:build_conditions_for_datatable)).to be_blank
51
+ end
52
+ end
53
+
54
+ context 'none of columns are connected' do
55
+ before(:each) do
56
+ allow(datatable).to receive(:searchable_columns) { [] }
57
+ end
58
+
59
+ it 'returns empty query result' do
60
+ datatable.params[:search] = { value: 'msmith' }
61
+ expect(datatable.send(:build_conditions_for_datatable)).to be_blank
62
+ end
63
+ end
64
+
65
+ context 'with search query' do
66
+ before(:each) do
67
+ datatable.params[:search] = { value: "John", regex: "false" }
68
+ end
69
+
70
+ it 'returns a filtering query' do
71
+ query = datatable.send(:build_conditions_for_datatable)
72
+ results = records.where(query).map(&:username)
73
+ expect(results).to include('johndoe')
74
+ expect(results).not_to include('msmith')
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#build_conditions_for_selected_columns' do
80
+ context 'columns include search query' do
81
+ before do
82
+ datatable.params[:columns]['0'][:search][:value] = 'doe'
83
+ datatable.params[:columns]['1'][:search][:value] = 'example'
84
+ end
85
+
86
+ it 'returns an Arel object' do
87
+ result = datatable.send(:build_conditions_for_selected_columns)
88
+ expect(result).to be_a(Arel::Nodes::And)
89
+ end
90
+
91
+ it 'can call #to_sql on returned object' do
92
+ result = datatable.send(:build_conditions_for_selected_columns)
93
+ expect(result).to respond_to(:to_sql)
94
+ expect(result.to_sql).to eq(
95
+ "CAST(\"users\".\"username\" AS TEXT) LIKE '%doe%' AND CAST(\"users\".\"email\" AS TEXT) LIKE '%example%'"
96
+ )
97
+ end
98
+ end
99
+
100
+ it 'calls #build_conditions_for_selected_columns' do
101
+ expect(datatable).to receive(:build_conditions_for_selected_columns)
102
+ datatable.send(:build_conditions)
103
+ end
104
+
105
+ context 'with search values in columns' do
106
+ before(:each) do
107
+ datatable.params[:columns]['0'][:search][:value] = 'doe'
108
+ end
109
+
110
+ it 'returns a filtered set of records' do
111
+ query = datatable.send(:build_conditions_for_selected_columns)
112
+ results = records.where(query).map(&:username)
113
+ expect(results).to include('johndoe')
114
+ expect(results).not_to include('msmith')
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#typecast helper method' do
120
+ let(:view) { double('view', params: sample_params) }
121
+ let(:column) { ComplexDatatable.new(view).datatable.columns.first }
122
+
123
+ it 'returns VARCHAR if :db_adapter is :pg' do
124
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :pg }
125
+ expect(column.send(:typecast)).to eq('VARCHAR')
126
+ end
127
+
128
+ it 'returns VARCHAR if :db_adapter is :postgre' do
129
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :postgre }
130
+ expect(column.send(:typecast)).to eq('VARCHAR')
131
+ end
132
+
133
+ it 'returns CHAR if :db_adapter is :mysql2' do
134
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :mysql2 }
135
+ expect(column.send(:typecast)).to eq('CHAR')
136
+ end
137
+
138
+ it 'returns CHAR if :db_adapter is :mysql' do
139
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :mysql }
140
+ expect(column.send(:typecast)).to eq('CHAR')
141
+ end
142
+
143
+ it 'returns TEXT if :db_adapter is :sqlite' do
144
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :sqlite }
145
+ expect(column.send(:typecast)).to eq('TEXT')
146
+ end
147
+
148
+ it 'returns TEXT if :db_adapter is :sqlite3' do
149
+ allow_any_instance_of(AjaxDatatablesRails::Configuration).to receive(:db_adapter) { :sqlite3 }
150
+ expect(column.send(:typecast)).to eq('TEXT')
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::ORM::ActiveRecord#paginate_records' do
4
+ let(:view) { double('view', params: sample_params) }
5
+ let(:datatable) { SampleDatatable.new(view) }
6
+
7
+ before(:each) do
8
+ AjaxDatatablesRails.configure do |config|
9
+ config.db_adapter = :sqlite
10
+ config.orm = :active_record
11
+ end
12
+
13
+ User.create(username: 'johndoe', email: 'johndoe@example.com')
14
+ User.create(username: 'msmith', email: 'mary.smith@example.com')
15
+ end
16
+
17
+ after(:each) do
18
+ User.destroy_all
19
+ end
20
+
21
+ describe 'paginate records' do
22
+ let(:records) { User.all }
23
+
24
+ it 'requires a records collection argument' do
25
+ expect { datatable.send(:paginate_records) }.to raise_error
26
+ end
27
+
28
+ it 'paginates records properly' do
29
+ expect(datatable.send(:paginate_records, records).to_sql).to include(
30
+ "LIMIT 10 OFFSET 0"
31
+ )
32
+
33
+ datatable.params[:start] = "26"
34
+ datatable.params[:length] = "25"
35
+ expect(datatable.send(:paginate_records, records).to_sql).to include(
36
+ "LIMIT 25 OFFSET 25"
37
+ )
38
+ end
39
+
40
+ it 'depends on the value of #offset' do
41
+ expect(datatable.datatable).to receive(:offset)
42
+ datatable.send(:paginate_records, records)
43
+ end
44
+
45
+ it 'depends on the value of #per_page' do
46
+ expect(datatable.datatable).to receive(:per_page).at_least(:once) { 10 }
47
+ datatable.send(:paginate_records, records)
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::ORM::ActiveRecord#sort_records' do
4
+ let(:view) { double('view', params: sample_params) }
5
+ let(:datatable) { ComplexDatatable.new(view) }
6
+
7
+ before(:each) do
8
+ AjaxDatatablesRails.configure do |config|
9
+ config.db_adapter = :sqlite
10
+ config.orm = :active_record
11
+ end
12
+
13
+ User.create(username: 'johndoe', email: 'johndoe@example.com')
14
+ User.create(username: 'msmith', email: 'mary.smith@example.com')
15
+ end
16
+
17
+ after(:each) do
18
+ User.destroy_all
19
+ end
20
+
21
+ describe 'sort records' do
22
+ let(:records) { User.all }
23
+
24
+ it 'returns a records collection sorted by :order params' do
25
+ # set to order Users by email in descending order
26
+ datatable.params[:order]['0'] = { column: '1', dir: 'desc' }
27
+ expect(datatable.send(:sort_records, records).map(&:email)).to match(
28
+ ['mary.smith@example.com', 'johndoe@example.com']
29
+ )
30
+ end
31
+
32
+ it 'can handle multiple sorting columns' do
33
+ # set to order by Users username in ascending order, and
34
+ # by Users email in descending order
35
+ datatable.params[:order]['0'] = { column: '0', dir: 'asc' }
36
+ datatable.params[:order]['1'] = { column: '1', dir: 'desc' }
37
+ expect(datatable.send(:sort_records, records).to_sql).to include(
38
+ "ORDER BY users.username ASC, users.email DESC"
39
+ )
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'AjaxDatatablesRails::ORM::ActiveRecord#fetch_records' do
4
+ context 'Private API' do
5
+ let(:view) { double('view', params: sample_params) }
6
+ let(:datatable) { SampleDatatable.new(view) }
7
+
8
+ before(:each) do
9
+ AjaxDatatablesRails.configure do |config|
10
+ config.db_adapter = :sqlite
11
+ config.orm = :active_record
12
+ end
13
+
14
+ User.create(username: 'johndoe', email: 'johndoe@example.com')
15
+ User.create(username: 'msmith', email: 'mary.smith@example.com')
16
+ end
17
+
18
+ after(:each) do
19
+ User.destroy_all
20
+ end
21
+
22
+ describe 'fetch records' do
23
+ it 'calls #get_raw_records' do
24
+ expect(datatable).to receive(:get_raw_records) { User.all }
25
+ datatable.send(:fetch_records)
26
+ end
27
+
28
+ it 'returns a collection of records' do
29
+ expect(datatable).to receive(:get_raw_records) { User.all }
30
+ expect(datatable.send(:fetch_records)).to be_a(ActiveRecord::Relation)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :users, :force => true do |t|
5
+ t.string :username
6
+ t.string :email
7
+ t.string :first_name
8
+ t.string :last_name
9
+
10
+ t.timestamps null: false
11
+ end
12
+
13
+ create_table :addresses, :force => true do |t|
14
+ t.string :address_line1
15
+ t.string :address_line2
16
+ t.string :city
17
+ t.string :zip_code
18
+ t.string :state
19
+ t.string :country
20
+
21
+ t.timestamps null: false
22
+ end
23
+
24
+ create_table :purchased_orders, :force => true do |t|
25
+ t.string :foo
26
+ t.string :bar
27
+
28
+ t.timestamps null: false
29
+ end
30
+
31
+ create_table :statistics_requests, :force => true do |t|
32
+ t.string :baz
33
+
34
+ t.timestamps null: false
35
+ end
36
+
37
+ create_table :statistics_sessions, :force => true do |t|
38
+ t.string :foo
39
+ t.integer :bar
40
+
41
+ t.timestamps null: false
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ require 'pry'
2
+ require 'rails'
3
+ require 'active_record'
4
+ require 'ajax-datatables-rails'
5
+
6
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
7
+
8
+ load File.dirname(__FILE__) + '/schema.rb'
9
+ load File.dirname(__FILE__) + '/test_helpers.rb'
10
+ require File.dirname(__FILE__) + '/test_models.rb'
@@ -0,0 +1,66 @@
1
+ def sample_params
2
+ ActiveSupport::HashWithIndifferentAccess.new(
3
+ {
4
+ "draw"=>"1",
5
+ "columns"=> {
6
+ "0"=> {
7
+ "data"=>"username", "name"=>"", "searchable"=>"true", "orderable"=>"true",
8
+ "search"=> {
9
+ "value"=>"", "regex"=>"false"
10
+ }
11
+ },
12
+ "1"=> {
13
+ "data"=>"email", "name"=>"", "searchable"=>"true", "orderable"=>"true",
14
+ "search"=> {
15
+ "value"=>"", "regex"=>"false"
16
+ }
17
+ },
18
+ "2"=> {
19
+ "data"=>"first_name", "name"=>"", "searchable"=>"false", "orderable"=>"false",
20
+ "search"=> {
21
+ "value"=>"", "regex"=>"false"
22
+ }
23
+ },
24
+ "3"=> {
25
+ "data"=>"last_name", "name"=>"", "searchable"=>"false", "orderable"=>"true",
26
+ "search"=> {
27
+ "value"=>"", "regex"=>"false"
28
+ }
29
+ },
30
+ },
31
+ "order"=> {
32
+ "0"=> {"column"=>"0", "dir"=>"asc"}
33
+ },
34
+ "start"=>"0", "length"=>"10", "search"=>{
35
+ "value"=>"", "regex"=>"false"
36
+ },
37
+ "_"=>"1423364387185"
38
+ }
39
+ )
40
+ end
41
+
42
+ class SampleDatatable < AjaxDatatablesRails::Base
43
+ def view_columns
44
+ @view_columns ||= ['User.username', 'User.email', 'User.first_name', 'User.last_name']
45
+ end
46
+
47
+ def data
48
+ [{}, {}]
49
+ end
50
+
51
+ def get_raw_records
52
+ User.all
53
+ end
54
+ end
55
+
56
+
57
+ class ComplexDatatable < SampleDatatable
58
+ def view_columns
59
+ @view_columns ||= {
60
+ username: { source: 'User.username' },
61
+ email: { source: 'User.email' },
62
+ first_name: { source: 'User.first_name' },
63
+ last_name: { source: 'User.last_name' }
64
+ }
65
+ end
66
+ end
@@ -0,0 +1,20 @@
1
+ class User < ActiveRecord::Base
2
+ end
3
+
4
+ class Address < ActiveRecord::Base
5
+ end
6
+
7
+ class PurchasedOrder < ActiveRecord::Base
8
+ end
9
+
10
+ module Statistics
11
+ def self.table_name_prefix
12
+ "statistics_"
13
+ end
14
+ end
15
+
16
+ class Statistics::Request < ActiveRecord::Base
17
+ end
18
+
19
+ class Statistics::Session < ActiveRecord::Base
20
+ end