data_tables 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/data_tables/data_tables_helper.rb +116 -0
- data/lib/data_tables.rb +210 -0
- metadata +49 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
module DataTablesHelper
|
2
|
+
def datatables(source, opts = {})
|
3
|
+
|
4
|
+
options = opts[:jquery] ? opts[:jquery].dup : {}
|
5
|
+
options[:bJQueryUI] = true unless options.has_key?(:bJQueryUI)
|
6
|
+
options[:bProcessing] = true unless options.has_key?(:bProcessing)
|
7
|
+
options[:bServerSide] = true unless options.has_key?(:bServerSide)
|
8
|
+
options[:bAutoWidth] = false unless options.has_key?(:bAutoWidth)
|
9
|
+
options[:bStateSave] = true unless options.has_key?(:bStateSave)
|
10
|
+
options[:oColVis] ||= {}
|
11
|
+
options[:oColVis][:aiExclude] ||= []
|
12
|
+
unless options[:oColVis][:aiExclude].include?(0)
|
13
|
+
options[:oColVis][:aiExclude].unshift(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
options[:fnInitComplete] ||= "function() {
|
17
|
+
if (eval('typeof ' + initDatatablesTable) == 'function') {
|
18
|
+
initDatatablesTable('#{source}');
|
19
|
+
}
|
20
|
+
}"
|
21
|
+
|
22
|
+
sdom = '<"#datatables_search_hint">lfrtip'
|
23
|
+
sdom = "C<\"clear\">" + sdom if options[:oColVis]
|
24
|
+
sdom = 'T' + sdom if options[:oTableTools]
|
25
|
+
options[:sDom] ||= sdom
|
26
|
+
|
27
|
+
datatable = controller.datatable_source(source)
|
28
|
+
options[:sAjaxSource] = method("#{datatable[:action]}_url".to_sym).call
|
29
|
+
columns = datatable[:attrs].keys.collect { |a| "<th>#{a}</th>" }.join
|
30
|
+
|
31
|
+
index = 0
|
32
|
+
targets = datatable[:attrs].inject([]) do |memo, (column, searchable)|
|
33
|
+
memo << index unless searchable
|
34
|
+
index += 1
|
35
|
+
memo
|
36
|
+
end
|
37
|
+
options[:aoColumnDefs] ||= []
|
38
|
+
options[:aoColumnDefs].unshift({
|
39
|
+
:aTargets => targets,
|
40
|
+
:bSearchable => false,
|
41
|
+
:bSortable => false
|
42
|
+
})
|
43
|
+
|
44
|
+
if options[:html]
|
45
|
+
html_opts = options[:html].collect { |k,v| "#{k}=\"#{v}\"" }.join(' ')
|
46
|
+
end
|
47
|
+
pad_ao_columns(options, datatable[:attrs].keys.size)
|
48
|
+
|
49
|
+
table_header = "<tr>#{columns}</tr>"
|
50
|
+
html = "
|
51
|
+
<script>
|
52
|
+
$(document).ready(function() {
|
53
|
+
var oTable = $('##{datatable[:action]}').dataTable({
|
54
|
+
#{datatables_option_string(options)}
|
55
|
+
});
|
56
|
+
$('tfoot input').keyup( function () {
|
57
|
+
/* Filter on the column (the index) of this element */
|
58
|
+
oTable.fnFilter( this.value, $('tfoot input').index(this) );
|
59
|
+
} );
|
60
|
+
|
61
|
+
});
|
62
|
+
</script>
|
63
|
+
<table id=\"#{datatable[:action]}\" #{html_opts}>
|
64
|
+
<thead>
|
65
|
+
#{table_header}
|
66
|
+
</thead>
|
67
|
+
<tbody>
|
68
|
+
</tbody>
|
69
|
+
</table>
|
70
|
+
"
|
71
|
+
return raw(html)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def datatables_option_string(options, indent = 4)
|
76
|
+
arr = []
|
77
|
+
options.each do |key, value|
|
78
|
+
if value.is_a?(String) && value[0..7] != "function"
|
79
|
+
arr << "#{' ' * indent}#{key}: '#{value}'"
|
80
|
+
elsif value.is_a?(Array)
|
81
|
+
indent += 2
|
82
|
+
item_arr = []
|
83
|
+
value.each do |item|
|
84
|
+
if item.is_a?(Hash)
|
85
|
+
str = "#{' ' * indent}{\n"
|
86
|
+
str += "#{datatables_option_string(item, indent + 2)}\n"
|
87
|
+
str += "#{' ' * indent}}"
|
88
|
+
item_arr << str
|
89
|
+
elsif item.is_a?(String) && item[0..7] != "function"
|
90
|
+
item_arr << "#{' ' * indent}'#{item}'"
|
91
|
+
else
|
92
|
+
item_arr << "#{' ' * indent}#{item}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
indent -= 2
|
96
|
+
arr << "#{' ' * indent}#{key}: [\n#{item_arr.join(",\n")}\n#{' ' * indent}]"
|
97
|
+
elsif value.is_a?(Hash)
|
98
|
+
str = "#{' ' * indent}#{key}: {\n"
|
99
|
+
str += "#{datatables_option_string(value, indent + 2)}\n"
|
100
|
+
str += "#{' ' * indent}}"
|
101
|
+
arr << str
|
102
|
+
else
|
103
|
+
arr << "#{' ' * indent}#{key}: #{value}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
arr.join(",\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
def pad_ao_columns(options, count)
|
111
|
+
return unless options[:aoColumns]
|
112
|
+
|
113
|
+
(count - options[:aoColumns].size).times do
|
114
|
+
options[:aoColumns] << 'null'
|
115
|
+
end
|
116
|
+
end
|
data/lib/data_tables.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'data_tables/data_tables_helper'
|
2
|
+
module DataTablesController
|
3
|
+
def self.included(cls)
|
4
|
+
cls.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def datatables_source(action, model, *attrs)
|
9
|
+
modelCls = Kernel.const_get(model.to_s.split("_").collect(&:capitalize).join)
|
10
|
+
modelAttrs = nil
|
11
|
+
if modelCls < Ohm::Model
|
12
|
+
modelAttrs = Hash[*modelCls.new.attributes.collect { |v| [v.to_s, nil] }.flatten]
|
13
|
+
else
|
14
|
+
modelAttrs = modelCls.new.attributes
|
15
|
+
end
|
16
|
+
columns = []
|
17
|
+
modelAttrs.each_key { |k| columns << k }
|
18
|
+
|
19
|
+
options = {}
|
20
|
+
attrs.each do |option|
|
21
|
+
option.each { |k,v| options[k] = v }
|
22
|
+
end
|
23
|
+
|
24
|
+
# override columns
|
25
|
+
columns = options_to_columns(options) if options[:columns]
|
26
|
+
|
27
|
+
# define columns so they are accessible from the helper
|
28
|
+
define_columns(modelCls, columns, action)
|
29
|
+
|
30
|
+
# define method that returns the data for the table
|
31
|
+
define_datatables_action(self, action, modelCls, columns, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
# named_scope is a combination table that include everything shown in UI.
|
35
|
+
# except is the codition used for Ohm's except method, it should be key-value format,
|
36
|
+
# such as [['name', 'bluesocket'],['id','1']].
|
37
|
+
def define_datatables_action(controller, action, modelCls, columns, options = {})
|
38
|
+
conditions = options[:conditions] || []
|
39
|
+
scope = options[:scope] || :domain
|
40
|
+
named_scope = options[:named_scope]
|
41
|
+
except = options[:except]
|
42
|
+
|
43
|
+
if modelCls < Ohm::Model
|
44
|
+
define_method action.to_sym do
|
45
|
+
if scope == :domain
|
46
|
+
domain = ActiveRecord::Base.connection.schema_search_path.to_s.split(",")[0]
|
47
|
+
return if domain.nil?
|
48
|
+
end
|
49
|
+
search_condition = params[:sSearch].blank? ? nil : params[:sSearch].to_s
|
50
|
+
records = scope == :domain ? modelCls.find(:domain => domain) : modelCls.all
|
51
|
+
if except
|
52
|
+
except.each do |f|
|
53
|
+
records = records.except(f[0].to_sym => f[1])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
total_records = records.size
|
57
|
+
sort_column = params[:iSortCol_0].to_i
|
58
|
+
sort_column = 1 if sort_column == 0
|
59
|
+
current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0) + 1
|
60
|
+
objects = nil
|
61
|
+
if search_condition.nil?
|
62
|
+
objects = records.sort_by(columns[sort_column][:name].to_sym, :order=>"ALPHA " + params[:sSortDir_0].capitalize, :limit=>params[:iDisplayLength].to_i, :start=>(params[:iDisplayStart].to_i))
|
63
|
+
total_display_records = total_records
|
64
|
+
else
|
65
|
+
options = {}
|
66
|
+
domain_id = domain.split("_")[1].to_i if scope == :domain
|
67
|
+
options[:domain] = domain_id .. domain_id if scope == :domain
|
68
|
+
options[:fuzzy] = {columns[sort_column][:name].to_sym => search_condition}
|
69
|
+
objects = Lunar.search(modelCls, options)
|
70
|
+
total_display_records = objects.size
|
71
|
+
objects = objects.sort(:by => columns[sort_column][:name].to_sym, :order=>"ALPHA " + params[:sSortDir_0].capitalize, :limit=>params[:iDisplayLength].to_i, :start=>(params[:iDisplayStart].to_i))
|
72
|
+
end
|
73
|
+
data = objects.collect do |instance|
|
74
|
+
columns.collect { |column| datatables_instance_get_value(instance, column) }
|
75
|
+
end
|
76
|
+
render :text => {:iTotalRecords => total_records,
|
77
|
+
:iTotalDisplayRecords => total_display_records,
|
78
|
+
:aaData => data,
|
79
|
+
:sEcho => params[:sEcho].to_i}.to_json
|
80
|
+
end
|
81
|
+
else
|
82
|
+
# add_search_option will determine whether the search text is empty or not
|
83
|
+
init_conditions = conditions.clone
|
84
|
+
add_search_option = false
|
85
|
+
define_method action.to_sym do
|
86
|
+
condition_local = ''
|
87
|
+
unless params[:sSearch].blank?
|
88
|
+
sort_column_id = params[:iSortCol_0].to_i
|
89
|
+
sort_column_id = 1 if sort_column_id == 0
|
90
|
+
sort_column = columns[sort_column_id]
|
91
|
+
if sort_column && sort_column.has_key?(:attribute)
|
92
|
+
condstr = params[:sSearch].gsub(/_/, '\\\\_').gsub(/%/, '\\\\%')
|
93
|
+
condition_local = "(text(#{sort_column[:name]}) ILIKE '#{condstr}%')"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# We just need one conditions string for search at a time. Every time we input
|
98
|
+
# something else in the search bar we will pop the previous search condition
|
99
|
+
# string and push the new string.
|
100
|
+
if condition_local != ''
|
101
|
+
if add_search_option == false
|
102
|
+
conditions << condition_local
|
103
|
+
add_search_option = true
|
104
|
+
else
|
105
|
+
if conditions != []
|
106
|
+
conditions.pop
|
107
|
+
conditions << condition_local
|
108
|
+
end
|
109
|
+
end
|
110
|
+
else
|
111
|
+
if add_search_option == true
|
112
|
+
if conditions != []
|
113
|
+
conditions.pop
|
114
|
+
add_search_option = false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if named_scope
|
120
|
+
total_records = modelCls.send(named_scope).count :conditions => init_conditions.join(" AND ")
|
121
|
+
total_display_records = modelCls.send(named_scope).count :conditions => conditions.join(" AND ")
|
122
|
+
else
|
123
|
+
total_records = modelCls.count :conditions => init_conditions.join(" AND ")
|
124
|
+
total_display_records = modelCls.count :conditions => conditions.join(" AND ")
|
125
|
+
end
|
126
|
+
sort_column = params[:iSortCol_0].to_i
|
127
|
+
sort_column = 1 if sort_column == 0
|
128
|
+
current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0)+1
|
129
|
+
if named_scope
|
130
|
+
objects = modelCls.send(named_scope).paginate(:page => current_page,
|
131
|
+
:order => "#{columns[sort_column][:name]} #{params[:sSortDir_0]}",
|
132
|
+
:conditions => conditions.join(" AND "),
|
133
|
+
:per_page => params[:iDisplayLength])
|
134
|
+
else
|
135
|
+
objects = modelCls.paginate(:page => current_page,
|
136
|
+
:order => "#{columns[sort_column][:name]} #{params[:sSortDir_0]}",
|
137
|
+
:conditions => conditions.join(" AND "),
|
138
|
+
:per_page => params[:iDisplayLength])
|
139
|
+
end
|
140
|
+
#logger.info("------conditions is #{conditions}")
|
141
|
+
data = objects.collect do |instance|
|
142
|
+
columns.collect { |column| datatables_instance_get_value(instance, column) }
|
143
|
+
end
|
144
|
+
render :text => {:iTotalRecords => total_records,
|
145
|
+
:iTotalDisplayRecords => total_display_records,
|
146
|
+
:aaData => data,
|
147
|
+
:sEcho => params[:sEcho].to_i}.to_json
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
#
|
155
|
+
# Takes a list of columns from options and transforms them
|
156
|
+
#
|
157
|
+
def options_to_columns(options)
|
158
|
+
columns = []
|
159
|
+
options[:columns].each do |column|
|
160
|
+
if column.kind_of? Symbol # a column from the database, we don't need to do anything
|
161
|
+
columns << {:name => column, :attribute => column}
|
162
|
+
elsif column.kind_of? Hash
|
163
|
+
columns << {:name => column[:name], :special => column}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
columns
|
167
|
+
end
|
168
|
+
|
169
|
+
def define_columns(cls, columns, action)
|
170
|
+
define_method "datatable_#{action}_columns".to_sym do
|
171
|
+
columnNames = {}
|
172
|
+
columns.each do |column|
|
173
|
+
columnName = ''
|
174
|
+
if column[:method] or column[:eval]
|
175
|
+
columnName << I18n.t(column[:name], :default => column[:name].to_s)
|
176
|
+
else
|
177
|
+
columnName << I18n.t(column[:name].to_sym, :default => column[:name].to_s)
|
178
|
+
end
|
179
|
+
columnName << ' *' unless column.has_key?(:attribute)
|
180
|
+
columnNames[columnName] = column.has_key?(:attribute) ? true : false
|
181
|
+
end
|
182
|
+
|
183
|
+
columnNames
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# gets the value for a column and row
|
189
|
+
def datatables_instance_get_value(instance, column)
|
190
|
+
if column[:attribute]
|
191
|
+
val = instance.send(column[:attribute].to_sym)
|
192
|
+
return I18n.t(val.to_s.to_sym, :default => val.to_s) if not val.blank?
|
193
|
+
return ''
|
194
|
+
elsif column[:special]
|
195
|
+
special = column[:special]
|
196
|
+
|
197
|
+
if special[:method]
|
198
|
+
return method(special[:method].to_sym).call(instance)
|
199
|
+
elsif special[:eval]
|
200
|
+
proc = lambda { obj = instance; binding }
|
201
|
+
return Kernel.eval(special[:eval], proc.call)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
return "value not found"
|
205
|
+
end
|
206
|
+
|
207
|
+
def datatable_source(name)
|
208
|
+
{:action => name, :attrs => method("datatable_#{name}_columns".to_sym).call}
|
209
|
+
end
|
210
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: data_tables
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Duane Compton
|
9
|
+
- Calvin Bascom
|
10
|
+
- Yi Su
|
11
|
+
- Chris Moos
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
date: 2012-10-10 00:00:00.000000000 Z
|
16
|
+
dependencies: []
|
17
|
+
description: Originally a plugin
|
18
|
+
email: Duane.Compton@gmail.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/data_tables.rb
|
24
|
+
- lib/data_tables/data_tables_helper.rb
|
25
|
+
homepage: http://rubygems.org/gems/data_tables
|
26
|
+
licenses: []
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
none: false
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 1.8.24
|
46
|
+
signing_key:
|
47
|
+
specification_version: 3
|
48
|
+
summary: Rails friendly interface into DataTables
|
49
|
+
test_files: []
|