data_tables 0.1.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.
- 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: []
|