apotomo-datatable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +113 -0
- data/Rakefile +46 -0
- data/lib/apotomo-datatable.rb +11 -0
- data/lib/apotomo-datatable/railtie.rb +24 -0
- data/lib/apotomo-datatable/version.rb +3 -0
- data/lib/apotomo/datatable/display_html.html.haml +25 -0
- data/lib/apotomo/datatable/display_js.html.haml +2 -0
- data/lib/apotomo/datatable/head_foot.html.haml +22 -0
- data/lib/apotomo/datatable_widget.rb +303 -0
- data/lib/tasks/apotomo-datatable_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/features/load_datatable_in_various_ways_spec.rb +32 -0
- data/spec/features/load_datatable_in_various_ways_spec.rb.log +52 -0
- data/spec/features/server_side_processing_spec.rb +62 -0
- data/spec/features/server_side_processing_spec.rb.log +16 -0
- data/spec/widget/apotomo_datatable_widget_spec.rb +107 -0
- data/spec/widget/data_event_spec.rb +24 -0
- data/spec/widget/display_event_spec.rb +45 -0
- data/spec/widget/included_excluded_columns_spec.rb +38 -0
- data/spec/widget/merge_options_spec.rb +35 -0
- metadata +359 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Apotomo-Datatable
|
2
|
+
Rails Gem for generating jQuery Datatables with extremely minimal configuration
|
3
|
+
|
4
|
+
Built on the Apotomo widget gem
|
5
|
+
|
6
|
+
Pacaged with jQuery Datatables version 1.9.4
|
7
|
+
|
8
|
+
Datatables may be rendered within a view (like a partial) or as javascipt for AJAX implementation
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
### Gemfile
|
13
|
+
gem 'apotomo-datatable'
|
14
|
+
gem 'haml'
|
15
|
+
|
16
|
+
### Simple use case for a given model/controller
|
17
|
+
|
18
|
+
#### items_controller.rb
|
19
|
+
has_widgets do |root|
|
20
|
+
root << datatable=widget('apotomo/datatable',:datatable)
|
21
|
+
end
|
22
|
+
|
23
|
+
#### Embedding with an HTML view
|
24
|
+
##### view/items/index.html.haml
|
25
|
+
=render_widget :datatable,:display
|
26
|
+
|
27
|
+
#### AJAX rendering
|
28
|
+
##### view/items/index.html.haml
|
29
|
+
%div#parentDiv
|
30
|
+
=link_to "Create table", items_path+'.js?template[parent]=parentDiv', :remote=>true, :title=>"Create table"
|
31
|
+
##### view/items/index.js.haml
|
32
|
+
=render_widget :datatable,:display
|
33
|
+
|
34
|
+
### Passing options from various points
|
35
|
+
|
36
|
+
#### items_controller.rb
|
37
|
+
root << datatable=widget('apotomo/datatable',:datatable,
|
38
|
+
:widget=>{}, #widget options (the model is derived from the controller name by default, but may be passed here as :model=>Model)
|
39
|
+
:template=>{:footer=>true}, #template options
|
40
|
+
:plugin=>{:sScrollY=>150} #plugin options, see: http://datatables.net/usage/options
|
41
|
+
)
|
42
|
+
|
43
|
+
def index
|
44
|
+
respond_to do |format|
|
45
|
+
format.html
|
46
|
+
format.js {render :script=>true}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# By defaultathe widget will query the model on its own
|
51
|
+
# If apotomo_datatable_datasource is defined, Apotomo::DatatableWidget.datasource will use this to populate
|
52
|
+
# The hash provided, which is expected to be standard search results from an ActiveRecord query, is encapsulated in a hash expected by jquery datatables
|
53
|
+
|
54
|
+
def apotomo_datatable_datasource(filter)
|
55
|
+
Item.find(:all,filter)
|
56
|
+
end
|
57
|
+
|
58
|
+
#### view/items/index.html.haml
|
59
|
+
%h1="Apotomo Datatable Example"
|
60
|
+
|
61
|
+
%h2="Rendered as HTML"
|
62
|
+
=render_widget :items_datatable,:display,:plugin=>{:sScrollY=>100,:bScrollCollapse=>true},:template=>{:id=>'html_datatable'}
|
63
|
+
|
64
|
+
%h2="Rendered via AJAX"
|
65
|
+
|
66
|
+
%div#parentDiv1
|
67
|
+
=link_to "Create table from options set in the URL", items_path+'.js?template[parent]=parentDiv1&plugin[sScrollY]=100', :remote=>true
|
68
|
+
|
69
|
+
:javascript
|
70
|
+
var plugin_options={sScrollY: 50, iDisplayStart: 20};
|
71
|
+
|
72
|
+
|
73
|
+
%div#parentDiv2
|
74
|
+
=link_to "Create table from options defined in javascript variable", items_path+'.js?template[plugin_options]=plugin_options&template[parent]=parentDiv2&template[id]=datatable_2', :remote=>true
|
75
|
+
|
76
|
+
#### index.js.haml
|
77
|
+
=render_widget :items_datatable,:display,:widget=>{},:template=>{:footer=>true},:plugin=>{:sScrollY=>150}
|
78
|
+
|
79
|
+
|
80
|
+
## Options
|
81
|
+
|
82
|
+
The @options hash includes sub-hashes for the widget, templates and client-side plugin
|
83
|
+
@options={:widget=>{...}, :template=>{...}, :plugin=>{...}}
|
84
|
+
|
85
|
+
Default options are generated and merged with controller-provided options in Apotomo::DatatableWidget.set_options
|
86
|
+
|
87
|
+
Default options may be overridden from the controller, view, by URL parameters and by a client-side javascript hash, as seen in the example above, with the client-side javascript hash taking the highest precedence
|
88
|
+
client_side_options -> url_param_options -> view_options -> controller_options -> default_options
|
89
|
+
|
90
|
+
URL parameters may only define template and plugin options. Defining widget options from the URL would present a security hole
|
91
|
+
|
92
|
+
The client-side hash may only define plugin options. Since this hash is not passed to the server template and widget options would never be seen
|
93
|
+
|
94
|
+
$.extend(@options[:widget].to_json,client_side_options) constitutes the arguments passed to the datatable initialization function.
|
95
|
+
See http://datatables.net/usage/options for options. As such, any option specified in the jquery datatables API may be set in this sub hash
|
96
|
+
Note: Server-side processing is not yet supported
|
97
|
+
|
98
|
+
By default, :id, :created_at and :updated_at columns are excluded from display.
|
99
|
+
Pass the [:template][:excluded_columns] option to exclude more
|
100
|
+
Pass the [:template][:included_columns] option to include excluded columns
|
101
|
+
|
102
|
+
## Current Status
|
103
|
+
|
104
|
+
The above examples will load a functional datatable, with sorting and global filtering working
|
105
|
+
Basic tests for loading and sorting are implemented. Sorting tests fail when plugin[bServerSide]=true since that is not implemented yet
|
106
|
+
Template for column-based filters started, but not yet complete or functional
|
107
|
+
|
108
|
+
## TODO
|
109
|
+
|
110
|
+
Implement server-side sorting and filtering
|
111
|
+
Expand feature tests
|
112
|
+
Implementing the other base features
|
113
|
+
Move on to implementing plugins
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ApotomoDatatable'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
29
|
+
require 'rspec/core'
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
32
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
33
|
+
task :default => :spec
|
34
|
+
|
35
|
+
=begin
|
36
|
+
|
37
|
+
Rake::TestTask.new(:test) do |t|
|
38
|
+
t.libs << 'test'
|
39
|
+
t.test_files = FileList['test/**/*_test.rb']
|
40
|
+
t.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
Bundler::GemHelper.install_tasks
|
46
|
+
=end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'apotomo/datatable_widget'
|
2
|
+
require 'apotomo-datatable/railtie'
|
3
|
+
module ApotomoDatatable
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
# loads views from 'cell/views' and NOT from 'app/cells'
|
6
|
+
# config.paths.add 'app/cell_views', :with => 'cell/views'
|
7
|
+
|
8
|
+
# appends 'lib/my_cells_view_path' to this Railtie view path contribution
|
9
|
+
# config.paths['app/cell_views'] << 'lib/apotomo/datatable'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rails/railtie"
|
2
|
+
|
3
|
+
module ApotomoDatatable
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
# rake_tasks do
|
6
|
+
# load "apotomo/apotomo.rake"
|
7
|
+
# end
|
8
|
+
|
9
|
+
# As we are a Railtie only, the routes won't be loaded automatically. Beside that, we want our
|
10
|
+
# route to be the very first (otherwise #resources might supersede it).
|
11
|
+
# initializer 'apotomo.prepend_routes', :after => :add_routing_paths do |app|
|
12
|
+
# app.routes_reloader.paths.unshift(File.dirname(__FILE__) + "/../../config/routes.rb")
|
13
|
+
# end
|
14
|
+
|
15
|
+
# Include a lazy loader via has_widgets.
|
16
|
+
# initializer 'apotomo.add_has_widgets' do |app|
|
17
|
+
# ActionController::Base.extend Apotomo::Rails::ControllerMethodsLoader
|
18
|
+
# end
|
19
|
+
|
20
|
+
initializer 'apotomo-datatable.setup_view_paths', :after => 'apotomo.setup_view_paths' do |app|
|
21
|
+
Apotomo::DatatableWidget.setup_view_paths!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/default apotomo-datatable view
|
2
|
+
-if @merged_options[:widget][:model]
|
3
|
+
%table{:id=>@merged_options[:template][:id],:class=>"display dataTable"}<>
|
4
|
+
-if @merged_options[:template][:header]
|
5
|
+
%thead<>
|
6
|
+
=render({:state=>:head_foot},@merged_options,:header);
|
7
|
+
%tbody<>
|
8
|
+
-if !@merged_options[:plugin][:sAjaxSource] and !@merged_options[:plugin][:aaData]
|
9
|
+
-@merged_options[:widget][:datasource].call().each do |item|
|
10
|
+
%tr<>
|
11
|
+
-item.attributes.each do |key,value|
|
12
|
+
-if !@merged_options[:template][:excluded_columns].include?(key.to_sym) or @merged_options[:template][:included_columns].include?(key.to_sym)
|
13
|
+
%td<>=value
|
14
|
+
-if @merged_options[:template][:footer]
|
15
|
+
%tfoot<>
|
16
|
+
=render({:state=>:head_foot},@merged_options,:footer);
|
17
|
+
-if !@merged_options[:params][:format]
|
18
|
+
:javascript
|
19
|
+
postInit(function() {
|
20
|
+
#{@init_datatable_js}
|
21
|
+
});
|
22
|
+
-"#{@merged_options[:plugin].inspect}"
|
23
|
+
-else
|
24
|
+
="No Model Defined"
|
25
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
%tr<>
|
2
|
+
-merged_options[:widget][:model].columns_hash.each_pair do |name,column|
|
3
|
+
-if !@merged_options[:template][:excluded_columns].include?(name.to_sym) or @merged_options[:template][:included_columns].include?(name.to_sym)
|
4
|
+
%th<>
|
5
|
+
- if merged_options[:template][section].respond_to?(:each_pair)
|
6
|
+
- inputs=merged_options[:template][section][name]
|
7
|
+
- unless inputs
|
8
|
+
- inputs=merged_options[:template][section][:default]
|
9
|
+
- else
|
10
|
+
- inputs=merged_options[:template][section]
|
11
|
+
- unless inputs.respond_to?(:each)
|
12
|
+
- inputs=[inputs]
|
13
|
+
- inputs.each do |opt|
|
14
|
+
- case opt
|
15
|
+
- when :label
|
16
|
+
=name
|
17
|
+
- when :input
|
18
|
+
%br<>
|
19
|
+
%input{:onclick=>"event.stopPropagation();"}<>
|
20
|
+
- when :select
|
21
|
+
%br<>
|
22
|
+
%select{:onclick=>"event.stopPropagation();"}<>
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'apotomo'
|
2
|
+
#include Rails.application.routes.url_helpers
|
3
|
+
|
4
|
+
=begin
|
5
|
+
Apotomo-Datatables
|
6
|
+
|
7
|
+
Basic usage with default options:
|
8
|
+
|
9
|
+
Gemfile
|
10
|
+
gem 'apotomo-datatables'
|
11
|
+
|
12
|
+
items_controller.rb
|
13
|
+
root << items_datatable=widget('apotomo/datatable',:items_datatable)
|
14
|
+
|
15
|
+
index.html.haml
|
16
|
+
#render as HTML like a partial
|
17
|
+
=render_widget :items_datatable,:display
|
18
|
+
|
19
|
+
#render via AJAX
|
20
|
+
%div#parentDiv
|
21
|
+
=link_to "Create table from options set in the URL", items_path+'.js?template[parent]=parentDiv, :remote=>true
|
22
|
+
|
23
|
+
index.js.haml #used to render via AJAX
|
24
|
+
=render_widget :items_datatable,:display
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
class Apotomo::DatatableWidget < Apotomo::Widget
|
29
|
+
DEFAULT_VIEW_PATHS << File.expand_path('../../', __FILE__)
|
30
|
+
|
31
|
+
responds_to_event :data #Used by sAjaxSource plugin option
|
32
|
+
responds_to_event :display
|
33
|
+
responds_to_event :test_evt, :with => :test_evt, :passing => :root
|
34
|
+
|
35
|
+
after_initialize do
|
36
|
+
## set default options and those based on options provided in the has_widgets call in the controller
|
37
|
+
set_options(options)
|
38
|
+
@test_val=0
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_val
|
42
|
+
@test_val
|
43
|
+
end
|
44
|
+
|
45
|
+
def increment_test_val
|
46
|
+
@test_val=@test_val+1
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_evt(event)
|
50
|
+
if @merged_options[:widget][:controller].respond_to?(:apotomo_datatable_event)
|
51
|
+
@evt_test=@merged_options[:widget][:controller].apotomo_datatable_event(event)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def merged_options
|
56
|
+
@merged_options
|
57
|
+
end
|
58
|
+
|
59
|
+
def display(view_options={})
|
60
|
+
if view_options && view_options.respond_to?('each_pair')
|
61
|
+
## merge options provided by the render_widget method call in the view
|
62
|
+
@merged_options.deep_merge!(view_options)
|
63
|
+
end
|
64
|
+
|
65
|
+
## merge options from the URL params
|
66
|
+
merge_url_param_options(params)
|
67
|
+
process_boolean_options
|
68
|
+
|
69
|
+
##Build json string to pass plugin options to the client
|
70
|
+
##and command to merge with client-side plugin_options if provided
|
71
|
+
datatable_options=@merged_options[:plugin].to_json
|
72
|
+
if @merged_options[:template][:plugin_options]
|
73
|
+
datatable_options="$.extend(#{datatable_options},#{@merged_options[:template][:plugin_options]})"
|
74
|
+
end
|
75
|
+
|
76
|
+
#make sure :header and :footer are arrays
|
77
|
+
if @merged_options[:template][:header] && ! @merged_options[:template][:header].respond_to?('each') then @merged_options[:template][:header]=[@merged_options[:template][:header]] end
|
78
|
+
if @merged_options[:template][:footer] && ! @merged_options[:template][:footer].respond_to?('each') then @merged_options[:template][:footer]=[@merged_options[:template][:footer]] end
|
79
|
+
|
80
|
+
|
81
|
+
@init_datatable_js= "$(\"##{@merged_options[:template][:id]}\").dataTable(#{datatable_options});"
|
82
|
+
|
83
|
+
#this is just a test firing an event
|
84
|
+
@evt_test='empty'
|
85
|
+
self.fire :test_evt
|
86
|
+
if @merged_options[:params][:format]=='js'
|
87
|
+
#TODO: search the app's widget path for this template before using the default version
|
88
|
+
@html=render_to_string :file => File.expand_path('../../apotomo/datatable/display_html', __FILE__)
|
89
|
+
#escape double quotes and new lines, then make string safe so it's rendered properly
|
90
|
+
@html=@html.gsub(/[\n\r]/,' ').gsub(/"/,'\\\\"').html_safe
|
91
|
+
render :view=>:display_js
|
92
|
+
# render (replace :view=>:display_html, :selector=>'div#parent')+@init_datatable_js
|
93
|
+
else
|
94
|
+
render :view=>:display_html
|
95
|
+
# render :view=>File.expand_path('./datatable/display_html', __FILE__)
|
96
|
+
# render :view=>'app/widgets/apotomo/datatable/display_html'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# def view_for_state(state)
|
102
|
+
# "app/widgets/apotomo/datatable/#{state}"
|
103
|
+
# end
|
104
|
+
|
105
|
+
def datatable_init_vars
|
106
|
+
end
|
107
|
+
|
108
|
+
def head_foot(merged_options,section)
|
109
|
+
render :locals=>{:merged_options=>merged_options,:section=>section}
|
110
|
+
end
|
111
|
+
|
112
|
+
def data
|
113
|
+
is_searching = (params[:sSearch] and params[:sSearch].length>0)
|
114
|
+
@records=datasource
|
115
|
+
@data={
|
116
|
+
:iTotalRecords=>@merged_options[:widget][:model].count,
|
117
|
+
:iTotalDisplayRecords=>is_searching ? @records.count : @merged_options[:widget][:model].count,
|
118
|
+
:aaData=>@records
|
119
|
+
}
|
120
|
+
render text: @data.to_json
|
121
|
+
end
|
122
|
+
|
123
|
+
def datasource
|
124
|
+
#datatable passes something like
|
125
|
+
#"sEcho"=>"1", "iColumns"=>"5", "sColumns"=>"", "iDisplayStart"=>"0", "iDisplayLength"=>"10", "mDataProp_0"=>"id", "mDataProp_1"=>"text", "mDataProp_2"=>"created_at", "mDataProp_3"=>"updated_at", "mDataProp_4"=>"like", "sSearch"=>"", "bRegex"=>"false", "sSearch_0"=>"", "bRegex_0"=>"false", "bSearchable_0"=>"true", "sSearch_1"=>"", "bRegex_1"=>"false", "bSearchable_1"=>"true", "sSearch_2"=>"", "bRegex_2"=>"false", "bSearchable_2"=>"true", "sSearch_3"=>"", "bRegex_3"=>"false", "bSearchable_3"=>"true", "sSearch_4"=>"", "bRegex_4"=>"false", "bSearchable_4"=>"true", "iSortCol_0"=>"0", "sSortDir_0"=>"asc", "iSortingCols"=>"1", "bSortable_0"=>"true", "bSortable_1"=>"true", "bSortable_2"=>"true", "bSortable_3"=>"true", "bSortable_4"=>"true"
|
126
|
+
filter={}
|
127
|
+
is_searching = (params[:sSearch] and params[:sSearch].length>0)
|
128
|
+
if is_searching
|
129
|
+
filter[:conditions]=[@merged_options[:widget][:model].column_names.join(' LIKE :sSearch OR ')+' LIKE :sSearch',{:sSearch=>"%#{params[:sSearch]}%"}]
|
130
|
+
end
|
131
|
+
if params[:iDisplayStart] and params[:iDisplayLength]
|
132
|
+
filter[:limit]=params[:iDisplayLength]
|
133
|
+
filter[:offset]=params[:iDisplayStart]
|
134
|
+
end
|
135
|
+
if @merged_options[:widget][:controller].respond_to?('apotomo_datatable_datasource')
|
136
|
+
@records=@merged_options[:widget][:controller].apotomo_datatable_datasource(filter)
|
137
|
+
else
|
138
|
+
@records=@merged_options[:widget][:model].find(:all,filter)
|
139
|
+
end
|
140
|
+
return @records
|
141
|
+
end
|
142
|
+
|
143
|
+
def create
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def edit
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def update
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def destroy
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
def set_options(controller_options)
|
160
|
+
=begin
|
161
|
+
Options:
|
162
|
+
|
163
|
+
The @merged_options hash includes primary keys for the widget, templates and client-side plugin
|
164
|
+
@merged_options={:widget=>{...}, :template=>{...}, :plugin=>{...}}
|
165
|
+
|
166
|
+
Default options are generated in Apotomo::DatatableWidget.set_options
|
167
|
+
|
168
|
+
Default options may be overridden from the controller, view, by URL parameters and by a client-side javascript hash, as seen in the example above,
|
169
|
+
with the client-side javascript hash taking the highest precedence
|
170
|
+
client_side_options -> url_param_options -> view_options -> controller_options -> default_options
|
171
|
+
|
172
|
+
URL parameters may only define template and plugin options. Defining widget options from the URL would present a security hole
|
173
|
+
The client-side hash may only define plugin options. Since it is not passed to the server, template and widget options would be irrelevant
|
174
|
+
|
175
|
+
$.extend(@merged_options[:widget].to_json,client_side_options) constitutes the arguments passed to the datatable initialization function.
|
176
|
+
See http://datatables.net/usage/options for options. As such, any option specified in the jquery datatables API may be set in this sub hash
|
177
|
+
|
178
|
+
|
179
|
+
=end
|
180
|
+
#initialize @merged_options, which will contain the final merged hash of options
|
181
|
+
@merged_options={:widget=>{},:template=>{},:plugin=>{}}.with_indifferent_access
|
182
|
+
|
183
|
+
#make sure options (passed by controller in has_widgets) has the basic hash structure
|
184
|
+
controller_options=controller_options.respond_to?(:each_pair) ? controller_options : {}.with_indifferent_access
|
185
|
+
controller_options=@merged_options.deep_merge(controller_options)
|
186
|
+
controller=parent_controller
|
187
|
+
if match=/(\w+?)sController/.match(controller.class.name.to_s)
|
188
|
+
controller_model_name=match[1]
|
189
|
+
end
|
190
|
+
|
191
|
+
#get model first since it is used to build other default options
|
192
|
+
model=nil
|
193
|
+
if controller_options[:widget].has_key?(:model)
|
194
|
+
if controller_options[:widget][:model].respond_to?("columns_hash")
|
195
|
+
model=controller_options[:widget][:model]
|
196
|
+
end
|
197
|
+
elsif controller_model_name
|
198
|
+
#derive the model from the controller name
|
199
|
+
if defined?(controller_model_name) && eval(controller_model_name+'.respond_to?("columns_hash")')
|
200
|
+
model=eval(controller_model_name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
if model==nil then
|
204
|
+
raise "Cannot use apotomo-datatable without a model. Either pass a model in the has_widgets method call or make sure a model exists with a name corresponding to the controller from which has_widgets is called"
|
205
|
+
end
|
206
|
+
|
207
|
+
default_options={
|
208
|
+
:widget=>{
|
209
|
+
:name=>"#{model.name}DatatableWidget",
|
210
|
+
:model=>model,
|
211
|
+
:controller=>controller,
|
212
|
+
:datasource=>self.method(:datasource),
|
213
|
+
:test_option=>'default'
|
214
|
+
},
|
215
|
+
:template=>{
|
216
|
+
#The header and footer have the same set of options.
|
217
|
+
#Each may contain multiple rows. Each row may define an array of options for each cell
|
218
|
+
#:header=
|
219
|
+
|
220
|
+
#Each may be a single value, an array or a hash
|
221
|
+
#If a single value or array, all columns are rendered with the provided options in order
|
222
|
+
#If a hash, each key should match column field names. Each key-value is a value or array as above
|
223
|
+
#single value or array options are: nil: ommited, label: label, input: input column filter, select: select column filter
|
224
|
+
# :header=>{:default=>:label,:name=>[:label,:input],:value=>[:label,:input]},
|
225
|
+
:header=>{:default=>:label},
|
226
|
+
:footer=>nil,
|
227
|
+
:id=>"#{model.name}Datatable",
|
228
|
+
:excluded_columns=>[:id,:created_at,:updated_at], #set to a list of field names to exclude from display
|
229
|
+
:included_columns=>[], #set to a list of columns to include even if they are excluded in :excluded_columns
|
230
|
+
:test_option=>'default'
|
231
|
+
},
|
232
|
+
:plugin=>{
|
233
|
+
:iDisplayStart=>0,
|
234
|
+
:bProcessing=>true,
|
235
|
+
:bJQueryUI=>true,
|
236
|
+
:test_option=>'default'
|
237
|
+
}
|
238
|
+
}.with_indifferent_access
|
239
|
+
default_options[:plugin][:aoColumns]=[]
|
240
|
+
model.column_names.each do |name|
|
241
|
+
default_options[:plugin][:aoColumns].push({'mDataProp'=>name})
|
242
|
+
end
|
243
|
+
|
244
|
+
# merge default options with options provided by the controller
|
245
|
+
@merged_options=default_options.deep_merge(controller_options)
|
246
|
+
@merged_options[:params]=params
|
247
|
+
end
|
248
|
+
|
249
|
+
def merge_url_param_options(url_param_options)
|
250
|
+
#:widget options are not accepted from URL parameters - accepting them would be a security hole
|
251
|
+
if url_param_options.has_key?(:template) && url_param_options[:template].respond_to?('each_pair')
|
252
|
+
@merged_options[:template].deep_merge!(url_param_options[:template])
|
253
|
+
end
|
254
|
+
if url_param_options.has_key?(:plugin) && url_param_options[:plugin].respond_to?('each_pair')
|
255
|
+
@merged_options[:plugin].deep_merge!(url_param_options[:plugin])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def process_boolean_options
|
260
|
+
#some options accept boolean options to indicate the default value (true) or undefined (false or nil)
|
261
|
+
#these must be processed after options from all sources have been merged
|
262
|
+
#Some true values are converted to default parameters for the plugin
|
263
|
+
#Some false values are deleted from the hash to allow the plugin to apply its own default options
|
264
|
+
|
265
|
+
## if options[:plugin][:sAjaxSource] is boolean or nil, derive default if true and delete option value from controller
|
266
|
+
if @merged_options[:plugin].has_key?(:sAjaxSource)
|
267
|
+
sAjaxSource_bool=make_bool(@merged_options[:plugin][:sAjaxSource])
|
268
|
+
if sAjaxSource_bool
|
269
|
+
@merged_options[:plugin][:sAjaxSource]=url_for_event(:data)
|
270
|
+
unless @merged_options[:plugin].has_key?(:bServerSide)
|
271
|
+
@merged_options[:plugin][:bServerSide]=true # User server-side processing: http://datatables.net/ref#bServerSide
|
272
|
+
end
|
273
|
+
else
|
274
|
+
@merged_options[:plugin].delete(:sAjaxSource) #delete false or nil value to prevent invalid option from passing to plugin
|
275
|
+
end
|
276
|
+
end
|
277
|
+
## profide column mapping if using ajax or aaData
|
278
|
+
## provide aaData if requested
|
279
|
+
if sAjaxSource_bool || @merged_options[:plugin][:aaData]==true
|
280
|
+
if @merged_options[:plugin][:aaData]==true
|
281
|
+
records=datasource
|
282
|
+
@merged_options[:plugin][:aaData]=records
|
283
|
+
end
|
284
|
+
else
|
285
|
+
@merged_options[:plugin].delete(:aoColumns)
|
286
|
+
end
|
287
|
+
@merged_options[:plugin][:sAjaxSourceBOOL]=sAjaxSource_bool
|
288
|
+
end
|
289
|
+
|
290
|
+
def make_bool(val)
|
291
|
+
if !!val==val
|
292
|
+
return val
|
293
|
+
else
|
294
|
+
if val.is_a?(String) && val.downcase=="true"
|
295
|
+
return true
|
296
|
+
else
|
297
|
+
return false
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
|