datatable 0.1.0alpha2
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +3 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +83 -0
- data/MIT-LICENSE +20 -0
- data/README.md +135 -0
- data/Rakefile +27 -0
- data/TODO +71 -0
- data/datatable.gemspec +20 -0
- data/example_app/.DS_Store +0 -0
- data/example_app/.gitignore +6 -0
- data/example_app/.rspec +1 -0
- data/example_app/Gemfile +35 -0
- data/example_app/Gemfile.lock +126 -0
- data/example_app/Rakefile +7 -0
- data/example_app/app/controllers/application_controller.rb +3 -0
- data/example_app/app/controllers/orders_controller.rb +13 -0
- data/example_app/app/datatables/orders_index.rb +31 -0
- data/example_app/app/helpers/application_helper.rb +2 -0
- data/example_app/app/models/customer.rb +4 -0
- data/example_app/app/models/item.rb +4 -0
- data/example_app/app/models/order.rb +5 -0
- data/example_app/app/models/order_item.rb +4 -0
- data/example_app/app/models/sales_rep.rb +3 -0
- data/example_app/app/views/layouts/application.html.erb +12 -0
- data/example_app/app/views/orders/index.html.erb +14 -0
- data/example_app/config/application.rb +41 -0
- data/example_app/config/boot.rb +6 -0
- data/example_app/config/database.yml.mysql +20 -0
- data/example_app/config/database.yml.pg +20 -0
- data/example_app/config/environment.rb +5 -0
- data/example_app/config/environments/development.rb +28 -0
- data/example_app/config/environments/production.rb +49 -0
- data/example_app/config/environments/test.rb +35 -0
- data/example_app/config/initializers/backtrace_silencers.rb +7 -0
- data/example_app/config/initializers/datatable.rb +6 -0
- data/example_app/config/initializers/inflections.rb +10 -0
- data/example_app/config/initializers/mime_types.rb +5 -0
- data/example_app/config/initializers/secret_token.rb +9 -0
- data/example_app/config/initializers/session_store.rb +8 -0
- data/example_app/config/locales/en.yml +5 -0
- data/example_app/config/routes.rb +6 -0
- data/example_app/config.ru +4 -0
- data/example_app/db/migrate/20110429185712_create_customers.rb +15 -0
- data/example_app/db/migrate/20110429185742_create_sales_reps.rb +14 -0
- data/example_app/db/migrate/20110429185807_create_items.rb +15 -0
- data/example_app/db/migrate/20110429185913_create_orders.rb +15 -0
- data/example_app/db/migrate/20110429190005_create_order_items.rb +14 -0
- data/example_app/db/schema.rb +53 -0
- data/example_app/db/seeds.rb +49 -0
- data/example_app/lib/tasks/.gitkeep +0 -0
- data/example_app/lib/tasks/setup.rake +12 -0
- data/example_app/public/404.html +26 -0
- data/example_app/public/422.html +26 -0
- data/example_app/public/500.html +26 -0
- data/example_app/public/datatable/css/demo_page.css +99 -0
- data/example_app/public/datatable/css/demo_table.css +539 -0
- data/example_app/public/datatable/css/demo_table_jui.css +521 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/example_app/public/datatable/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/example_app/public/datatable/css/smoothness/jquery-ui-1.8.14.custom.css +568 -0
- data/example_app/public/datatable/images/back_disabled.jpg +0 -0
- data/example_app/public/datatable/images/back_enabled.jpg +0 -0
- data/example_app/public/datatable/images/favicon.ico +0 -0
- data/example_app/public/datatable/images/forward_disabled.jpg +0 -0
- data/example_app/public/datatable/images/forward_enabled.jpg +0 -0
- data/example_app/public/datatable/images/sort_asc.png +0 -0
- data/example_app/public/datatable/images/sort_asc_disabled.png +0 -0
- data/example_app/public/datatable/images/sort_both.png +0 -0
- data/example_app/public/datatable/images/sort_desc.png +0 -0
- data/example_app/public/datatable/images/sort_desc_disabled.png +0 -0
- data/example_app/public/datatable/js/jquery-ui-1.8.14.custom.min.js +789 -0
- data/example_app/public/datatable/js/jquery.dataTables.js +7347 -0
- data/example_app/public/datatable/js/jquery.dataTables.min.js +151 -0
- data/example_app/public/favicon.ico +0 -0
- data/example_app/public/flash/copy_cvs_xls.swf +0 -0
- data/example_app/public/flash/copy_cvs_xls_pdf.swf +0 -0
- data/example_app/public/images/rails.png +0 -0
- data/example_app/public/javascripts/application.js +2 -0
- data/example_app/public/javascripts/jquery.js +8936 -0
- data/example_app/public/javascripts/jquery.min.js +18 -0
- data/example_app/public/javascripts/jquery_ujs.js +316 -0
- data/example_app/public/robots.txt +5 -0
- data/example_app/public/stylesheets/.gitkeep +0 -0
- data/example_app/script/rails +6 -0
- data/example_app/spec/datatables/active_record_dsl_spec.rb +59 -0
- data/example_app/spec/datatables/active_record_link_to_spec.rb +22 -0
- data/example_app/spec/datatables/active_record_pagination_spec.rb +94 -0
- data/example_app/spec/datatables/active_record_table_operations_spec.rb +180 -0
- data/example_app/spec/datatables/config_spec.rb +10 -0
- data/example_app/spec/datatables/query_params_spec.rb +73 -0
- data/example_app/spec/datatables/sql_default_spec.rb +22 -0
- data/example_app/spec/datatables/sql_pagination_spec.rb +177 -0
- data/example_app/spec/datatables/sql_search_cast_spec.rb +6 -0
- data/example_app/spec/datatables/sql_search_global_spec.rb +107 -0
- data/example_app/spec/datatables/sql_search_individual_spec.rb +113 -0
- data/example_app/spec/datatables/sql_search_where_spec.rb +87 -0
- data/example_app/spec/datatables/sql_sorting_spec.rb +80 -0
- data/example_app/spec/datatables/sql_variables_spec.rb +104 -0
- data/example_app/spec/factories/customer_factory.rb +4 -0
- data/example_app/spec/factories/item_factory.rb +2 -0
- data/example_app/spec/factories/order_factory.rb +7 -0
- data/example_app/spec/factories/order_item_factory.rb +2 -0
- data/example_app/spec/factories/sales_rep_factory.rb +4 -0
- data/example_app/spec/helpers/aocolumn_spec.rb +239 -0
- data/example_app/spec/helpers/data_table_helper_spec.rb +148 -0
- data/example_app/spec/helpers/headings_spec.rb +71 -0
- data/example_app/spec/spec_helper.rb +29 -0
- data/generators.txt +6 -0
- data/images/datatable_screenshot.png +0 -0
- data/lib/datatable/active_record_dsl.rb +49 -0
- data/lib/datatable/errors.rb +5 -0
- data/lib/datatable/helper.rb +199 -0
- data/lib/datatable/railtie.rb +17 -0
- data/lib/datatable/version.rb +4 -0
- data/lib/datatable.rb +341 -0
- data/lib/generators/datatable/install_generator.rb +58 -0
- data/lib/generators/datatable/new_generator.rb +46 -0
- data/lib/generators/templates/datatable.rb +33 -0
- data/lib/generators/templates/datatable_initializer.rb +6 -0
- data/vendor/datatable/Readme.txt +11 -0
- data/vendor/datatable/extras/TableTools/media/css/TableTools.css +264 -0
- data/vendor/datatable/extras/TableTools/media/css/TableTools_JUI.css +182 -0
- data/vendor/datatable/extras/TableTools/media/images/background.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/collection.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/collection_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/copy.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/copy_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/csv.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/csv_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/pdf.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/pdf_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/print.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/print_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/xls.png +0 -0
- data/vendor/datatable/extras/TableTools/media/images/xls_hover.png +0 -0
- data/vendor/datatable/extras/TableTools/media/js/TableTools.js +2410 -0
- data/vendor/datatable/extras/TableTools/media/js/TableTools.min.js +78 -0
- data/vendor/datatable/extras/TableTools/media/js/TableTools.min.js.gz +0 -0
- data/vendor/datatable/extras/TableTools/media/js/ZeroClipboard.js +365 -0
- data/vendor/datatable/extras/TableTools/media/swf/copy_cvs_xls.swf +0 -0
- data/vendor/datatable/extras/TableTools/media/swf/copy_cvs_xls_pdf.swf +0 -0
- data/vendor/datatable/license-bsd.txt +10 -0
- data/vendor/datatable/license-gpl2.txt +339 -0
- data/vendor/datatable/media/css/demo_page.css +99 -0
- data/vendor/datatable/media/css/demo_table.css +539 -0
- data/vendor/datatable/media/css/demo_table_jui.css +521 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/vendor/datatable/media/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/vendor/datatable/media/css/smoothness/jquery-ui-1.8.14.custom.css +568 -0
- data/vendor/datatable/media/images/back_disabled.jpg +0 -0
- data/vendor/datatable/media/images/back_enabled.jpg +0 -0
- data/vendor/datatable/media/images/favicon.ico +0 -0
- data/vendor/datatable/media/images/forward_disabled.jpg +0 -0
- data/vendor/datatable/media/images/forward_enabled.jpg +0 -0
- data/vendor/datatable/media/images/sort_asc.png +0 -0
- data/vendor/datatable/media/images/sort_asc_disabled.png +0 -0
- data/vendor/datatable/media/images/sort_both.png +0 -0
- data/vendor/datatable/media/images/sort_desc.png +0 -0
- data/vendor/datatable/media/images/sort_desc_disabled.png +0 -0
- data/vendor/datatable/media/js/jquery-ui-1.8.14.custom.min.js +789 -0
- data/vendor/datatable/media/js/jquery.dataTables.js +7347 -0
- data/vendor/datatable/media/js/jquery.dataTables.min.js +151 -0
- metadata +270 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Generating sql based on our DSL' do
|
4
|
+
|
5
|
+
# it 'should work' do
|
6
|
+
# Order.create!(:order_number => 32)
|
7
|
+
# datatable = OrdersIndex.new
|
8
|
+
# params = {}
|
9
|
+
# json = datatable.query(params).json
|
10
|
+
# json['aaRecords'][0][0].should == 32
|
11
|
+
# end
|
12
|
+
|
13
|
+
it 'should select two fields' do
|
14
|
+
class OrdersSimple < Datatable::Base
|
15
|
+
set_model Order
|
16
|
+
|
17
|
+
column :order_number
|
18
|
+
column :memo
|
19
|
+
end
|
20
|
+
|
21
|
+
orders = Order.arel_table
|
22
|
+
OrdersSimple.to_sql.should == Order.select(orders[:order_number]).select(orders[:memo]).to_sql
|
23
|
+
OrdersSimple.relation.should == Order.select(orders[:order_number]).select(orders[:memo])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should handle a simple join' do
|
27
|
+
class OrdersSimple < Datatable::Base
|
28
|
+
set_model Order
|
29
|
+
|
30
|
+
column :memo
|
31
|
+
|
32
|
+
join :customer
|
33
|
+
end
|
34
|
+
|
35
|
+
orders = Order.arel_table
|
36
|
+
OrdersSimple.to_sql.should == Order.select(orders[:memo]).joins(:customer).to_sql
|
37
|
+
OrdersSimple.relation.should == Order.select(orders[:memo]).joins(:customer)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should handle a join with an inner column' do
|
41
|
+
class OrdersSimple < Datatable::Base
|
42
|
+
set_model Order
|
43
|
+
|
44
|
+
column :memo
|
45
|
+
|
46
|
+
join :customer do
|
47
|
+
column :first_name
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
orders = Order.arel_table
|
53
|
+
customers = Arel::Table.new(:customers)
|
54
|
+
OrdersSimple.to_sql.should == Order.select(orders[:memo]).joins(:customer).select(customers[:first_name]).to_sql
|
55
|
+
OrdersSimple.relation.should == Order.select(orders[:memo]).joins(:customer).select(customers[:first_name])
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'adding links to active record datatables' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
Object.send(:remove_const, :T) rescue nil
|
7
|
+
class T < Datatable::Base
|
8
|
+
set_model Order
|
9
|
+
column :id, :link_to => link_to('{{0}}', order_path('{{0}}'))
|
10
|
+
end
|
11
|
+
@params = {
|
12
|
+
'iColumns' => 1,
|
13
|
+
"bSearchable_0" => true
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "adding link_to in block adds it to the column" do
|
18
|
+
t = T.query(@params)
|
19
|
+
T.columns['orders.id'][:link_to].should == "<a href=\"/orders/%7B%7B0%7D%7D\">{{0}}</a>"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'basic query params and pagination' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
class OrdersSimple < Datatable::Base
|
7
|
+
set_model Order
|
8
|
+
column :id
|
9
|
+
column :memo
|
10
|
+
join :customer do
|
11
|
+
column :first_name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@params = {}
|
15
|
+
|
16
|
+
@params['iColumns'] = 3
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sEcho" do
|
21
|
+
echo = rand(239823)
|
22
|
+
params = {'sEcho' => echo }
|
23
|
+
OrdersSimple.new(params).to_json['sEcho'].should == echo
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return the number of records when there are no records' do
|
27
|
+
OrdersSimple.new.to_json['iTotalRecords'].should == 0
|
28
|
+
OrdersSimple.new.to_json['iTotalDisplayRecords'].should == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return the number of records' do
|
32
|
+
3.times{ Factory(:order) }
|
33
|
+
OrdersSimple.query(@params).to_json['iTotalRecords'].should == 3
|
34
|
+
OrdersSimple.query(@params).to_json['iTotalDisplayRecords'].should == 3
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should return valid aaData' do
|
38
|
+
class OrdersComplex < Datatable::Base
|
39
|
+
set_model Order
|
40
|
+
column :order_number
|
41
|
+
column :memo
|
42
|
+
join :customer do
|
43
|
+
column :first_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@params['iColumns'] = 3
|
47
|
+
|
48
|
+
orders = [Factory(:order), Factory(:order)]
|
49
|
+
OrdersComplex.query(@params).to_json['aaData'].should == orders.map { |o| [o.order_number.to_s, o.memo, o.customer.first_name]}
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return valid aaData in different order' do
|
53
|
+
class OrdersComplex < Datatable::Base
|
54
|
+
set_model Order
|
55
|
+
column :memo
|
56
|
+
column :order_number
|
57
|
+
end
|
58
|
+
@params['iColumns'] = 2
|
59
|
+
orders = [Factory(:order), Factory(:order)]
|
60
|
+
OrdersComplex.query(@params).to_json['aaData'].should == orders.map { |o| [o.memo, o.order_number.to_s]}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should provide first page" do
|
64
|
+
class OrdersComplex2 < Datatable::Base
|
65
|
+
set_model Order
|
66
|
+
column :id
|
67
|
+
end
|
68
|
+
@params['iColumns'] = 1
|
69
|
+
|
70
|
+
@params['iDisplayStart'] = 0
|
71
|
+
@params['iDisplayLength'] = 2
|
72
|
+
orders = [Factory(:order), Factory(:order), Factory(:order), Factory(:order)]
|
73
|
+
OrdersComplex2.query(@params).to_json['iTotalRecords'].should == 2
|
74
|
+
OrdersComplex2.query(@params).to_json['iTotalDisplayRecords'].should == 4
|
75
|
+
OrdersComplex2.query(@params).to_json['aaData'].should == orders[0..1].map {|o| [o.id.to_s] }
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should provide second page" do
|
79
|
+
class OrdersComplex3 < Datatable::Base
|
80
|
+
set_model Order
|
81
|
+
column :id
|
82
|
+
end
|
83
|
+
|
84
|
+
@params['iColumns'] = 1
|
85
|
+
@params['iDisplayStart'] = 2
|
86
|
+
@params['iDisplayLength'] = 2
|
87
|
+
|
88
|
+
orders = [Factory(:order), Factory(:order), Factory(:order), Factory(:order)]
|
89
|
+
OrdersComplex3.query(@params).to_json['iTotalRecords'].should == 2
|
90
|
+
OrdersComplex3.query(@params).to_json['iTotalDisplayRecords'].should == 4
|
91
|
+
OrdersComplex3.query(@params).to_json['aaData'].should == Order.all[2..3].map {|o| [o.id.to_s] }
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Operations on the ActiveRecord table' do
|
4
|
+
# Store/ retrive:
|
5
|
+
# data type
|
6
|
+
# ordering
|
7
|
+
# human title
|
8
|
+
|
9
|
+
|
10
|
+
# Column sorting
|
11
|
+
# input: an index to be sorted on
|
12
|
+
# array of columns to sort on and asc/desc
|
13
|
+
# [['asc', 1], ['desc', 3]]
|
14
|
+
#
|
15
|
+
# need to know what index a column is
|
16
|
+
# and from that index need to know the column name
|
17
|
+
# sometimes will need the table name
|
18
|
+
|
19
|
+
# Pass in params for sorting by various columns in various orders
|
20
|
+
# Test that records are returned in the correct order
|
21
|
+
|
22
|
+
before do
|
23
|
+
Object.send(:remove_const, :T) rescue nil
|
24
|
+
|
25
|
+
class T < Datatable::Base
|
26
|
+
set_model Order
|
27
|
+
column :id
|
28
|
+
column :order_number
|
29
|
+
column :memo
|
30
|
+
end
|
31
|
+
|
32
|
+
@params = {
|
33
|
+
"iColumns" => 4,
|
34
|
+
"bSearchable_0" => true,
|
35
|
+
"bSearchable_1" => true,
|
36
|
+
"bSearchable_2" => true,
|
37
|
+
"bSearchable_3" => true,
|
38
|
+
"bSortable_0" => true,
|
39
|
+
"bSortable_1" => true,
|
40
|
+
"bSortable_2" => true,
|
41
|
+
"bSortable_3" => true,
|
42
|
+
"sSearch_0" => nil,
|
43
|
+
"sSearch_1" => nil,
|
44
|
+
"sSearch_2" => nil,
|
45
|
+
"sSearch_3" => nil,
|
46
|
+
"sSearch" => nil }
|
47
|
+
|
48
|
+
Order.delete_all
|
49
|
+
@orders = [*0..20].map do
|
50
|
+
Factory(:order, :order_number => rand(2), :memo => rand(2).even? ? 'hello' : 'goodbye')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should sort by one column' do
|
55
|
+
@params['iSortCol_0'] = 0
|
56
|
+
@params['sSortDir_0'] = 'desc' # Assume first col is ID
|
57
|
+
@params['iSortingCols'] = 1
|
58
|
+
T.query(@params).to_json['aaData'][0][0].should == Order.order("id desc").first.id.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should sort by multiple columns' do
|
62
|
+
@params['iSortCol_0'] = 2 # Memo
|
63
|
+
@params['sSortDir_0'] = 'asc'
|
64
|
+
|
65
|
+
@params['iSortCol_1'] = 0 # ID
|
66
|
+
@params['sSortDir_1'] = 'desc'
|
67
|
+
|
68
|
+
@params['iSortingCols'] = 2
|
69
|
+
|
70
|
+
|
71
|
+
T.query(@params).to_json['aaData'][0][0].should == Order.order('memo asc, id desc')[0].id.to_s
|
72
|
+
T.query(@params).to_json['aaData'][-1][0].should == Order.order('memo asc, id desc')[-1].id.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should sort ascending and descending' do
|
76
|
+
@params['iSortCol_0'] = 2 # Memo
|
77
|
+
@params['sSortDir_0'] = 'desc'
|
78
|
+
|
79
|
+
@params['iSortCol_1'] = 0 # ID
|
80
|
+
@params['sSortDir_1'] = 'asc'
|
81
|
+
|
82
|
+
@params['iSortingCols'] = 2
|
83
|
+
|
84
|
+
|
85
|
+
T.query(@params).to_json['aaData'][0][0].should == Order.order('memo desc, id asc')[0].id.to_s
|
86
|
+
T.query(@params).to_json['aaData'][-1][0].should == Order.order('memo desc, id asc')[-1].id.to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
# Multi column searching
|
90
|
+
# input: a search term
|
91
|
+
# a way to search on all of the columns that are searchable
|
92
|
+
# need to know the datatype of whats in the columns
|
93
|
+
it 'should global search' do
|
94
|
+
# Assume memo and id are searchable
|
95
|
+
## Pass in params for searching by the columns that are searchable
|
96
|
+
# Define which columns are searchable
|
97
|
+
# search using OR for each of those cols
|
98
|
+
# E.g. where int = num OR string LIKE str
|
99
|
+
# Types
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should global search string' do
|
103
|
+
# Memo is searchable
|
104
|
+
@params['sSearch'] = 'hel'
|
105
|
+
T.query(@params).to_json['aaData'][0][0].should == Order.where('memo LIKE ?', '%hel%')[0].id.to_s
|
106
|
+
T.query(@params).to_json['aaData'][0].map(&:first).should_not include(Order.where('memo NOT LIKE ?', '%hel%').map(&:id).map(&:to_s))
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should global search integer' do
|
110
|
+
# Id
|
111
|
+
order_id = Order.all[rand(Order.count)].id.to_s
|
112
|
+
@params['sSearch'] = order_id
|
113
|
+
T.query(@params).to_json['aaData'][0][0].should == order_id
|
114
|
+
T.query(@params).to_json['aaData'].length.should == 1
|
115
|
+
end
|
116
|
+
|
117
|
+
# Maybe: Test multiple results for integer
|
118
|
+
|
119
|
+
it 'should only search columns that are searchable' do
|
120
|
+
# ORder number is NOT searchable
|
121
|
+
# we need to make one value exist in 2 columsn for the test
|
122
|
+
#
|
123
|
+
Order.last.update_attribute(:order_number, Order.first.id)
|
124
|
+
@params['bSearchable_1'] = false
|
125
|
+
@params['sSearch'] = Order.first.id
|
126
|
+
T.query(@params).to_json['aaData'][0][0].should == Order.first.id.to_s
|
127
|
+
T.query(@params).to_json['aaData'].length.should == 1
|
128
|
+
end
|
129
|
+
|
130
|
+
it "shouldn't barf when no columns are searchable" do
|
131
|
+
@params['bSearchable_0'] = false
|
132
|
+
@params['bSearchable_1'] = false
|
133
|
+
@params['bSearchable_2'] = false
|
134
|
+
@params['sSearch'] = "foo"
|
135
|
+
T.query(@params)
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO
|
139
|
+
# it 'should deal w/ dates'
|
140
|
+
|
141
|
+
# Individual column searching
|
142
|
+
# input: index and a search term
|
143
|
+
# need to know what index a col is and the col name
|
144
|
+
# need to know the type
|
145
|
+
it 'should search by one string column' do
|
146
|
+
@params['bSearchable_2'] = true # col2 = memo
|
147
|
+
@params['sSearch_2'] = "hello"
|
148
|
+
T.query(@params).to_json['aaData'].length.should == Order.where('memo LIKE ?', '%hello%').count
|
149
|
+
T.query(@params).to_json['aaData'].map { |r| r[2] }.uniq.should == ['hello']
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should search by one integer column' do
|
153
|
+
@params['bSearchable_1'] = true
|
154
|
+
@params['sSearch_1'] = "1"
|
155
|
+
T.query(@params).to_json['aaData'].length.should == Order.where(:order_number => 1).count
|
156
|
+
T.query(@params).to_json['aaData'].map { |r| r[1] }.uniq.should == ["1"]
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should search by multiple columns' do
|
160
|
+
@params['bSearchable_1'] = true
|
161
|
+
@params['sSearch_1'] = "1"
|
162
|
+
@params['bSearchable_2'] = true
|
163
|
+
@params['sSearch_2'] = "hello"
|
164
|
+
T.query(@params).to_json['aaData'].length.should == Order.where('memo LIKE ?', '%hello%').where(:order_number => 1).count
|
165
|
+
T.query(@params).to_json['aaData'].map { |r| r[2] }.uniq.should == ['hello']
|
166
|
+
T.query(@params).to_json['aaData'].map { |r| r[1] }.uniq.should == ["1"]
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'only searches - when someone actually types 0'
|
170
|
+
|
171
|
+
# Column ordering (store/retrieve)
|
172
|
+
# column names in order
|
173
|
+
# sometimes will need to have the table name
|
174
|
+
# ['col.table', 'col.table']
|
175
|
+
# {'table.column' => :string, ...}
|
176
|
+
it 'should allow manually setting the order of columns'
|
177
|
+
it 'should otherwise automatically set the order' # E.g. arel
|
178
|
+
|
179
|
+
end
|
180
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'sanitize all parameters used in sql' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
Object.send(:remove_const, :T) rescue nil
|
7
|
+
class T < Datatable::Base
|
8
|
+
sql <<-SQL
|
9
|
+
SELECT
|
10
|
+
id,
|
11
|
+
order_number,
|
12
|
+
memo
|
13
|
+
FROM
|
14
|
+
orders
|
15
|
+
SQL
|
16
|
+
columns(
|
17
|
+
{'orders.id' => {:type => :integer}},
|
18
|
+
{'orders.order_number' => {:type => :integer }},
|
19
|
+
{'orders.memo' => {:type => :string }}
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
@params = {
|
24
|
+
"iColumns" => 3,
|
25
|
+
"bSearchable_0" => false,
|
26
|
+
"bSearchable_1" => false,
|
27
|
+
"bSearchable_2" => false,
|
28
|
+
"bSortable_0" => false,
|
29
|
+
"bSortable_1" => false,
|
30
|
+
"bSortable_2" => false,
|
31
|
+
"sSearch_0" => nil,
|
32
|
+
"sSearch_1" => nil,
|
33
|
+
"sSearch_2" => nil,
|
34
|
+
"sSearch" => nil
|
35
|
+
}
|
36
|
+
Order.delete_all
|
37
|
+
20.times{ Factory(:order) }
|
38
|
+
end
|
39
|
+
|
40
|
+
it "sanitize sSearch" do
|
41
|
+
@params['sSearch'] = "'invalid sql"
|
42
|
+
T.query(@params).to_json
|
43
|
+
lambda{ T.query(@params) }.should_not raise_error
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sanitize sSearch_#" do
|
47
|
+
@params['bSearchable_2'] = true
|
48
|
+
@params['sSearch_2'] = "'injected sql"
|
49
|
+
T.query(@params)
|
50
|
+
lambda{ T.query(@params) }.should_not raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it "sanitize sSortDir_#" do
|
54
|
+
@params['sSortDir_2'] = "'injected sql"
|
55
|
+
T.query(@params).to_json
|
56
|
+
lambda{ T.query(@params) }.should_not raise_error
|
57
|
+
end
|
58
|
+
|
59
|
+
it "sanitize iDisplayLength" do
|
60
|
+
@params['iDisplayLength'] = "'injected sql"
|
61
|
+
lambda{ T.query(@params) }.should_not raise_error
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sanitize iDisplayStart" do
|
65
|
+
@params['iDisplayStart'] = "'injected sql"
|
66
|
+
lambda{ T.query(@params) }.should_not raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'default response intelligently when no arguments are provided' do
|
4
|
+
|
5
|
+
# it's useful to have the datable respond with the first page and without
|
6
|
+
# any errors when the json is browsed directly for debugging purposes
|
7
|
+
|
8
|
+
it "should return default page size" do
|
9
|
+
# don't return all data decide on default page size
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not barf because iColumn is not set" do
|
13
|
+
# currently does
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to set the default sort direction" do
|
17
|
+
# most views will have an initially defined sort order
|
18
|
+
pending
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'pagination with count()' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
Object.send(:remove_const, :T) rescue nil
|
7
|
+
class T < Datatable::Base
|
8
|
+
count("SELECT COUNT(*) + 100 FROM orders")
|
9
|
+
sql("SELECT id FROM orders")
|
10
|
+
columns({"orders.id" => {:type => :integer, :link_to => link_to('{{0}}', order_path('{{0}}')) }})
|
11
|
+
end
|
12
|
+
@params = {
|
13
|
+
'iColumns' => 1,
|
14
|
+
"bSearchable_0" => true
|
15
|
+
}
|
16
|
+
4.times{ Factory(:order) }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "first page" do
|
20
|
+
before do
|
21
|
+
@params['iDisplayStart'] = 0
|
22
|
+
@params['iDisplayLength'] = 2
|
23
|
+
end
|
24
|
+
|
25
|
+
it "iTotalRecords correctly set" do
|
26
|
+
T.query(@params).to_json['iTotalRecords'].should == 2
|
27
|
+
end
|
28
|
+
|
29
|
+
it "iTotalDisplayRecords correctly set" do
|
30
|
+
T.query(@params).to_json['iTotalDisplayRecords'].should == 104
|
31
|
+
end
|
32
|
+
|
33
|
+
it "aaData contains dataset" do
|
34
|
+
T.query(@params).to_json['aaData'].should == Order.all[0..1].map {|o| [o.id.to_s] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "second page" do
|
39
|
+
before do
|
40
|
+
@params['iDisplayStart'] = 2
|
41
|
+
@params['iDisplayLength'] = 2
|
42
|
+
end
|
43
|
+
|
44
|
+
it "iTotalRecords correctly set" do
|
45
|
+
T.query(@params).to_json['iTotalRecords'].should == 2
|
46
|
+
end
|
47
|
+
|
48
|
+
it "iTotalDisplayRecords correctly set" do
|
49
|
+
T.query(@params).to_json['iTotalDisplayRecords'].should == 104
|
50
|
+
end
|
51
|
+
|
52
|
+
it "aaData contains dataset" do
|
53
|
+
T.query(@params).to_json['aaData'].should == Order.all[2..3].map {|o| [o.id.to_s] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'pagination without count()' do
|
59
|
+
|
60
|
+
before do
|
61
|
+
Object.send(:remove_const, :T) rescue nil
|
62
|
+
class T < Datatable::Base
|
63
|
+
sql("SELECT id FROM orders")
|
64
|
+
columns({"orders.id" => {:type => :integer, :link_to => link_to('{{0}}', order_path('{{0}}')) }})
|
65
|
+
end
|
66
|
+
@params = {
|
67
|
+
'iColumns' => 1,
|
68
|
+
"bSearchable_0" => true
|
69
|
+
}
|
70
|
+
4.times{ Factory(:order) }
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "first page" do
|
74
|
+
before do
|
75
|
+
@params['iDisplayStart'] = 0
|
76
|
+
@params['iDisplayLength'] = 2
|
77
|
+
end
|
78
|
+
|
79
|
+
it "iTotalRecords correctly set" do
|
80
|
+
T.query(@params).to_json['iTotalRecords'].should == 2
|
81
|
+
end
|
82
|
+
|
83
|
+
it "iTotalDisplayRecords correctly set" do
|
84
|
+
T.query(@params).to_json['iTotalDisplayRecords'].should == 4
|
85
|
+
end
|
86
|
+
|
87
|
+
it "aaData contains dataset" do
|
88
|
+
T.query(@params).to_json['aaData'].should == Order.all[0..1].map {|o| [o.id.to_s] }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
describe "second page" do
|
94
|
+
before do
|
95
|
+
@params['iDisplayStart'] = 2
|
96
|
+
@params['iDisplayLength'] = 2
|
97
|
+
end
|
98
|
+
it "iTotalRecords correctly set" do
|
99
|
+
T.query(@params).to_json['iTotalRecords'].should == 2
|
100
|
+
end
|
101
|
+
|
102
|
+
it "iTotalDisplayRecords correctly set" do
|
103
|
+
T.query(@params).to_json['iTotalDisplayRecords'].should == 4
|
104
|
+
end
|
105
|
+
|
106
|
+
it "aaData contains dataset" do
|
107
|
+
T.query(@params).to_json['aaData'].should == Order.all[2..3].map {|o| [o.id.to_s] }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
describe 'pagination with count() and where clause' do
|
115
|
+
before do
|
116
|
+
Object.send(:remove_const, :T) rescue nil
|
117
|
+
class T < Datatable::Base
|
118
|
+
count("SELECT COUNT(*) + 100 FROM orders")
|
119
|
+
sql("SELECT id FROM orders")
|
120
|
+
where("memo like 'foo'")
|
121
|
+
columns({"orders.id" => {:type => :integer, :link_to => link_to('{{0}}', order_path('{{0}}')) }})
|
122
|
+
end
|
123
|
+
@params = {
|
124
|
+
'iColumns' => 1,
|
125
|
+
"bSearchable_0" => true
|
126
|
+
}
|
127
|
+
Factory(:order, :memo => 'foo')
|
128
|
+
Factory(:order, :memo => 'foo')
|
129
|
+
Factory(:order, :memo => 'foo')
|
130
|
+
Factory(:order, :memo => 'bar')
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "first page" do
|
134
|
+
before do
|
135
|
+
@params['iDisplayStart'] = 0
|
136
|
+
@params['iDisplayLength'] = 2
|
137
|
+
end
|
138
|
+
|
139
|
+
it "iTotalRecords correctly set" do
|
140
|
+
T.query(@params).to_json['iTotalRecords'].should == 2
|
141
|
+
end
|
142
|
+
|
143
|
+
it "iTotalDisplayRecords correctly set" do
|
144
|
+
result = T.query(@params).to_json['iTotalDisplayRecords']
|
145
|
+
result.should == 103
|
146
|
+
end
|
147
|
+
|
148
|
+
it "aaData contains dataset" do
|
149
|
+
expected = Order.where("memo like 'foo'").all[0..1].map {|o| [o.id.to_s] }.sort
|
150
|
+
actual = T.query(@params).to_json['aaData'].sort
|
151
|
+
actual.should == expected
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "second page" do
|
156
|
+
before do
|
157
|
+
@params['iDisplayStart'] = 2
|
158
|
+
@params['iDisplayLength'] = 2
|
159
|
+
end
|
160
|
+
|
161
|
+
it "iTotalRecords correctly set" do
|
162
|
+
T.query(@params).to_json['iTotalRecords'].should == 1
|
163
|
+
end
|
164
|
+
|
165
|
+
it "iTotalDisplayRecords correctly set" do
|
166
|
+
T.query(@params).to_json['iTotalDisplayRecords'].should == 103
|
167
|
+
end
|
168
|
+
|
169
|
+
it "aaData contains dataset" do
|
170
|
+
expected = Order.where("memo like 'foo'").all[2..3].map {|o| [o.id.to_s] }.sort
|
171
|
+
actual = T.query(@params).to_json['aaData'].sort
|
172
|
+
actual.should == expected
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|