data_tables 0.1.7 → 0.1.9
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/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +90 -0
- data/Guardfile +25 -0
- data/MIT-LICENSE +20 -0
- data/README +89 -0
- data/Rakefile +23 -0
- data/data_tables.gemspec +20 -0
- data/lib/data_tables.rb +231 -75
- data/lib/data_tables/data_tables_helper.rb +12 -3
- data/spec/data_tables_spec.rb +52 -0
- data/spec/models/condition_model.rb +25 -0
- data/spec/spec_helper.rb +24 -0
- data/tasks/datatables_tasks.rake +4 -0
- data/test/datatables_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- metadata +42 -5
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rdoc
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
data_tables (0.1.9)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activemodel (3.2.12)
|
10
|
+
activesupport (= 3.2.12)
|
11
|
+
builder (~> 3.0.0)
|
12
|
+
activerecord (3.2.12)
|
13
|
+
activemodel (= 3.2.12)
|
14
|
+
activesupport (= 3.2.12)
|
15
|
+
arel (~> 3.0.2)
|
16
|
+
tzinfo (~> 0.3.29)
|
17
|
+
activesupport (3.2.12)
|
18
|
+
i18n (~> 0.6)
|
19
|
+
multi_json (~> 1.0)
|
20
|
+
ansi (1.4.3)
|
21
|
+
arel (3.0.2)
|
22
|
+
builder (3.0.4)
|
23
|
+
coderay (1.0.9)
|
24
|
+
diff-lcs (1.2.1)
|
25
|
+
ffi (1.7.0)
|
26
|
+
formatador (0.2.4)
|
27
|
+
guard (1.7.0)
|
28
|
+
formatador (>= 0.2.4)
|
29
|
+
listen (>= 0.6.0)
|
30
|
+
lumberjack (>= 1.0.2)
|
31
|
+
pry (>= 0.9.10)
|
32
|
+
thor (>= 0.14.6)
|
33
|
+
guard-rspec (2.5.3)
|
34
|
+
guard (>= 1.1)
|
35
|
+
rspec (~> 2.11)
|
36
|
+
hashr (0.0.22)
|
37
|
+
i18n (0.6.1)
|
38
|
+
listen (0.7.3)
|
39
|
+
lumberjack (1.0.3)
|
40
|
+
method_source (0.8.1)
|
41
|
+
mime-types (1.22)
|
42
|
+
multi_json (1.6.1)
|
43
|
+
nest (1.1.2)
|
44
|
+
redis
|
45
|
+
ohm (1.3.1)
|
46
|
+
nest (~> 1.0)
|
47
|
+
redis
|
48
|
+
scrivener (~> 0.0.3)
|
49
|
+
pry (0.9.12)
|
50
|
+
coderay (~> 1.0.5)
|
51
|
+
method_source (~> 0.8)
|
52
|
+
slop (~> 3.4)
|
53
|
+
rake (10.0.4)
|
54
|
+
rb-inotify (0.9.0)
|
55
|
+
ffi (>= 0.5.0)
|
56
|
+
redis (3.0.3)
|
57
|
+
rest-client (1.6.7)
|
58
|
+
mime-types (>= 1.16)
|
59
|
+
rspec (2.13.0)
|
60
|
+
rspec-core (~> 2.13.0)
|
61
|
+
rspec-expectations (~> 2.13.0)
|
62
|
+
rspec-mocks (~> 2.13.0)
|
63
|
+
rspec-core (2.13.1)
|
64
|
+
rspec-expectations (2.13.0)
|
65
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
66
|
+
rspec-mocks (2.13.0)
|
67
|
+
scrivener (0.0.3)
|
68
|
+
slop (3.4.4)
|
69
|
+
thor (0.18.1)
|
70
|
+
tire (0.5.7)
|
71
|
+
activemodel (>= 3.0)
|
72
|
+
activesupport
|
73
|
+
ansi
|
74
|
+
hashr (~> 0.0.19)
|
75
|
+
multi_json (~> 1.3)
|
76
|
+
rake
|
77
|
+
rest-client (~> 1.6)
|
78
|
+
tzinfo (0.3.37)
|
79
|
+
|
80
|
+
PLATFORMS
|
81
|
+
ruby
|
82
|
+
|
83
|
+
DEPENDENCIES
|
84
|
+
activerecord
|
85
|
+
data_tables!
|
86
|
+
guard-rspec
|
87
|
+
ohm
|
88
|
+
rb-inotify (~> 0.9)
|
89
|
+
rspec
|
90
|
+
tire
|
data/Guardfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec' do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
watch(%r{spec/models/(.+)\.rb$}) { "spec" }
|
9
|
+
|
10
|
+
# Rails example
|
11
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
12
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
13
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
14
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
15
|
+
watch('config/routes.rb') { "spec/routing" }
|
16
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
17
|
+
|
18
|
+
# Capybara features specs
|
19
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
20
|
+
|
21
|
+
# Turnip features and steps
|
22
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
23
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
24
|
+
end
|
25
|
+
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 [name of plugin creator]
|
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
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
Datatables
|
2
|
+
==========
|
3
|
+
|
4
|
+
Rails plugin for adding JQuery DataTables to your application.
|
5
|
+
|
6
|
+
DataTables: http://datatables.net/index
|
7
|
+
|
8
|
+
Requirements:
|
9
|
+
|
10
|
+
JQuery, JQuery UI, and the DataTables JQuery plugin
|
11
|
+
|
12
|
+
For Ohm Support:
|
13
|
+
redis, Ohm, Lunar
|
14
|
+
|
15
|
+
Features:
|
16
|
+
|
17
|
+
Easy definition of a table for an ActiveRecord and Ohm model.
|
18
|
+
Supports pagination.
|
19
|
+
|
20
|
+
|
21
|
+
TODO
|
22
|
+
=======
|
23
|
+
|
24
|
+
Add tests.
|
25
|
+
Add sorting.
|
26
|
+
|
27
|
+
|
28
|
+
Example
|
29
|
+
=======
|
30
|
+
|
31
|
+
In your controller, set up a datatables source. This is how the data is pulled from the server and returned with AJAX.
|
32
|
+
|
33
|
+
Initializer
|
34
|
+
============
|
35
|
+
ActionView::Base.send :include, DataTablesHelper
|
36
|
+
ActionController::Base.send :include, DataTablesController
|
37
|
+
|
38
|
+
Controller
|
39
|
+
===========
|
40
|
+
|
41
|
+
datatables_source :users_source, :user, :columns => [
|
42
|
+
:username, :fullname, {:name => "updated_at", :eval => 'obj.updated_at.getlocal.rfc2822'},
|
43
|
+
{:name => "Options", :method => :user_options_column}], :numResults => 10
|
44
|
+
|
45
|
+
This defines a table, named users_source, for the User model.
|
46
|
+
|
47
|
+
The columns are (in order):
|
48
|
+
|
49
|
+
username, fullname, updated_at, and options
|
50
|
+
|
51
|
+
There are two special ways to display the data for a column/row.
|
52
|
+
|
53
|
+
eval:
|
54
|
+
|
55
|
+
Evaluates a string, "obj" is an instance of your model in the table (in this case, a User object).
|
56
|
+
|
57
|
+
|
58
|
+
method:
|
59
|
+
|
60
|
+
Calls a method in your controller with the instance of your model as the parameter.
|
61
|
+
|
62
|
+
When defining a method for a column, an example method in your controller would be:
|
63
|
+
|
64
|
+
def user_options_column(user)
|
65
|
+
"<a href=\"#{url_for :action => 'view', :id => user.id}\">View User</a>"
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
Routes
|
70
|
+
=========
|
71
|
+
|
72
|
+
Because DataTables uses AJAX to load the data in the table, you must define a route to it. The first parameter of datatables_source is a *named route*. The rails plugin uses this to link the HTML for your DataTable.
|
73
|
+
|
74
|
+
Example
|
75
|
+
=========
|
76
|
+
|
77
|
+
map.users_source '/datatables/user', :controller => :user, :action => :users_source
|
78
|
+
|
79
|
+
|
80
|
+
Displaying the table
|
81
|
+
=========================
|
82
|
+
|
83
|
+
Displaying a table is probably the easiest part. In a view for your controller, you just do the following:
|
84
|
+
|
85
|
+
<%= datatables :users_source %>
|
86
|
+
|
87
|
+
|
88
|
+
Copyright (c) 2010 Chris Moos, released under the MIT license
|
89
|
+
Copyright (c) 2012 Duane Compton, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the datatables plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the datatables plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'Datatables'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
data/data_tables.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'data_tables'
|
5
|
+
s.version = '0.1.9'
|
6
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
7
|
+
s.summary = "Rails friendly interface into DataTables"
|
8
|
+
s.description = "DataTables for Rails"
|
9
|
+
s.authors = ["Duane Compton", "Calvin Bascom", "Yi Su", "Chris Moos", "Adrian Mejia"]
|
10
|
+
s.email = 'Duane.Compton@gmail.com'
|
11
|
+
s.homepage = 'http://rubygems.org/gems/data_tables'
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_development_dependency 'rspec', '~> 2.10'
|
19
|
+
end
|
20
|
+
|
data/lib/data_tables.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "data_tables/data_tables_helper"
|
2
|
+
|
2
3
|
module DataTablesController
|
3
4
|
def self.included(cls)
|
4
5
|
cls.extend(ClassMethods)
|
@@ -44,38 +45,103 @@ module DataTablesController
|
|
44
45
|
named_scope = options[:named_scope]
|
45
46
|
named_scope_args = options[:named_scope_args]
|
46
47
|
except = options[:except]
|
48
|
+
es_block = options[:es_block]
|
47
49
|
|
50
|
+
#
|
51
|
+
# ------- Ohm ----------- #
|
52
|
+
#
|
48
53
|
if modelCls < Ohm::Model
|
49
54
|
define_method action.to_sym do
|
55
|
+
logger.debug "[tire] (datatable:#{__LINE__}) #{action.to_sym} #{modelCls} < Ohm::Model"
|
56
|
+
|
50
57
|
if scope == :domain
|
51
58
|
domain = ActiveRecord::Base.connection.schema_search_path.to_s.split(",")[0]
|
52
59
|
return if domain.nil?
|
53
60
|
end
|
54
61
|
search_condition = params[:sSearch].blank? ? nil : params[:sSearch].to_s
|
55
|
-
|
56
|
-
if except
|
57
|
-
except.each do |f|
|
58
|
-
records = records.except(f[0].to_sym => f[1])
|
59
|
-
end
|
60
|
-
end
|
61
|
-
total_records = records.size
|
62
|
+
|
62
63
|
sort_column = params[:iSortCol_0].to_i
|
63
64
|
sort_column = 1 if sort_column == 0
|
64
65
|
current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0) + 1
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
66
|
+
per_page = params[:iDisplayLength] || 10
|
67
|
+
per_page = per_page.to_i
|
68
|
+
sort_dir = params[:sSortDir_0] || 'desc'
|
69
|
+
column_name_sym = columns[sort_column][:name].to_sym
|
70
|
+
|
71
|
+
objects = []
|
72
|
+
total_display_records = 0
|
73
|
+
total_records = 0
|
74
|
+
|
75
|
+
if defined? Tire
|
76
|
+
#
|
77
|
+
# ----------- Elasticsearch/Tire for Ohm ----------- #
|
78
|
+
#
|
79
|
+
elastic_index_name = "#{Tire::Model::Search.index_prefix}#{modelCls.to_s.underscore}"
|
80
|
+
logger.debug "*** (datatable:#{__LINE__}) Using tire for search #{modelCls} (#{elastic_index_name})"
|
81
|
+
|
82
|
+
search_condition = elasticsearch_sanitation(search_condition, except)
|
83
|
+
just_excepts = except ? elasticsearch_sanitation(nil, except) : "*"
|
84
|
+
logger.debug "*** search_condition = #{search_condition}; sort by #{column_name_sym}:#{sort_dir}; domain=`#{domain.inspect}'"
|
85
|
+
|
86
|
+
retried = 0
|
87
|
+
if Tire.index(elastic_index_name){exists?}.response.code != 404
|
88
|
+
begin
|
89
|
+
controller_instance = self
|
90
|
+
results = Tire.search(elastic_index_name) do
|
91
|
+
# retry #2 exclude search terms (and sorting) from search query
|
92
|
+
if retried < 2
|
93
|
+
query { string search_condition }
|
94
|
+
else
|
95
|
+
query { string just_excepts }
|
96
|
+
end
|
97
|
+
|
98
|
+
# retry #1 exclude sorting from search query
|
99
|
+
sort{ by column_name_sym, sort_dir } if retried < 1
|
100
|
+
|
101
|
+
filter(:term, domain: domain) unless domain.blank?
|
102
|
+
if es_block.is_a?(Symbol)
|
103
|
+
controller_instance.send(es_block, self)
|
104
|
+
else
|
105
|
+
es_block.call(self) if es_block.respond_to?(:call)
|
106
|
+
end
|
107
|
+
from (current_page-1) * per_page
|
108
|
+
size per_page
|
109
|
+
end.results
|
110
|
+
|
111
|
+
objects = results.map{ |r| modelCls[r._id] }.compact
|
112
|
+
total_display_records = results.total
|
113
|
+
|
114
|
+
|
115
|
+
total_records = Tire.search(elastic_index_name, search_type: 'count') do
|
116
|
+
query { string just_excepts }
|
117
|
+
filter(:term, domain: domain) unless domain.blank?
|
118
|
+
es_block.call(self) if es_block.respond_to?(:call)
|
119
|
+
end.results.total
|
120
|
+
rescue Tire::Search::SearchRequestFailed => e
|
121
|
+
if retried < 2
|
122
|
+
retried += 1
|
123
|
+
logger.info "Will retry(#{retried}) again because #{e.inspect}"
|
124
|
+
retry
|
125
|
+
end
|
126
|
+
logger.info "*** ERROR: Tire::Search::SearchRequestFailed => #{e.inspect}"
|
127
|
+
end
|
72
128
|
else
|
73
|
-
|
74
|
-
:order => "ALPHA " + params[:sSortDir_0].capitalize,
|
75
|
-
:limit => [params[:iDisplayStart].to_i, params[:iDisplayLength].to_i])
|
129
|
+
logger.debug "Index #{elastic_index_name} does not exists yet in ES."
|
76
130
|
end
|
77
|
-
total_display_records = total_records
|
78
131
|
else
|
132
|
+
#
|
133
|
+
# -------- Redis/Lunar search --------------- #
|
134
|
+
#
|
135
|
+
logger.debug "*** (datatable:#{__LINE__}) Using Redis/Lunar for search #{modelCls} (#{elastic_index_name})"
|
136
|
+
records = scope == :domain ? modelCls.find(:domain => domain) : modelCls.all
|
137
|
+
if except
|
138
|
+
except.each do |f|
|
139
|
+
records = records.except(f[0].to_sym => f[1])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
total_records = records.size
|
143
|
+
|
144
|
+
logger.debug "*** (datatable:#{__LINE__}) NOT using tire for search"
|
79
145
|
options = {}
|
80
146
|
domain_id = domain.split("_")[1].to_i if scope == :domain
|
81
147
|
options[:domain] = domain_id .. domain_id if scope == :domain
|
@@ -92,7 +158,9 @@ module DataTablesController
|
|
92
158
|
:order => "ALPHA " + params[:sSortDir_0].capitalize,
|
93
159
|
:limit => [params[:iDisplayStart].to_i, params[:iDisplayLength].to_i])
|
94
160
|
end
|
161
|
+
# -------- Redis/Lunar search --------------- #
|
95
162
|
end
|
163
|
+
|
96
164
|
data = objects.collect do |instance|
|
97
165
|
columns.collect { |column| datatables_instance_get_value(instance, column) }
|
98
166
|
end
|
@@ -101,74 +169,150 @@ module DataTablesController
|
|
101
169
|
:aaData => data,
|
102
170
|
:sEcho => params[:sEcho].to_i}.to_json
|
103
171
|
end
|
104
|
-
|
172
|
+
# ------- /Ohm ----------- #
|
173
|
+
else # Non-ohm models
|
105
174
|
# add_search_option will determine whether the search text is empty or not
|
106
175
|
init_conditions = conditions.clone
|
107
176
|
add_search_option = false
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
177
|
+
|
178
|
+
if modelCls.ancestors.any?{|ancestor| ancestor.name == "Tire::Model::Search"}
|
179
|
+
#
|
180
|
+
# ------- Elasticsearch ----------- #
|
181
|
+
#
|
182
|
+
define_method action.to_sym do
|
183
|
+
domain_name = ActiveRecord::Base.connection.schema_search_path.to_s.split(",")[0]
|
184
|
+
logger.debug "*** Using ElasticSearch for #{modelCls.name}"
|
185
|
+
objects = []
|
186
|
+
|
187
|
+
condstr = ""
|
188
|
+
unless params[:sSearch].blank?
|
189
|
+
sort_column_id = params[:iSortCol_0].to_i
|
190
|
+
sort_column_id = 1 if sort_column_id == 0
|
191
|
+
sort_column = columns[sort_column_id]
|
192
|
+
if sort_column && sort_column.has_key?(:attribute)
|
193
|
+
condstr = params[:sSearch].gsub(/_/, '\\\\_').gsub(/%/, '\\\\%')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
sort_column = params[:iSortCol_0].to_i
|
198
|
+
current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0)+1
|
199
|
+
per_page = params[:iDisplayLength] || 10
|
200
|
+
column_name = columns[sort_column][:name] || 'message'
|
201
|
+
sort_dir = params[:sSortDir_0] || 'desc'
|
202
|
+
|
203
|
+
condstr = elasticsearch_sanitation(condstr, except)
|
204
|
+
|
205
|
+
begin
|
206
|
+
query = Proc.new do
|
207
|
+
query { string(condstr) }
|
208
|
+
filter(:term, domain: domain_name) unless domain_name.blank?
|
209
|
+
es_block.call(self) if es_block.respond_to?(:call)
|
210
|
+
end
|
211
|
+
|
212
|
+
results = modelCls.search(page: current_page,
|
213
|
+
per_page: per_page,
|
214
|
+
sort: "#{column_name}:#{sort_dir}",
|
215
|
+
&query)
|
216
|
+
objects = results.to_a
|
217
|
+
total_display_records = results.total
|
218
|
+
total_records = modelCls.search(search_type: 'count') do
|
219
|
+
filter(:term, domain: domain_name) unless domain_name.blank?
|
220
|
+
es_block.call(self) if es_block.respond_to?(:call)
|
221
|
+
end.total
|
222
|
+
rescue Tire::Search::SearchRequestFailed => e
|
223
|
+
logger.debug "[Tire::Search::SearchRequestFailed] #{e.inspect}\n#{e.backtrace.join("\n")}"
|
224
|
+
objects = []
|
225
|
+
total_display_records = 0
|
226
|
+
total_records = 0
|
117
227
|
end
|
228
|
+
|
229
|
+
data = objects.collect do |instance|
|
230
|
+
columns.collect { |column| datatables_instance_get_value(instance, column) }
|
231
|
+
end
|
232
|
+
|
233
|
+
render :text => {:iTotalRecords => total_records,
|
234
|
+
:iTotalDisplayRecords => total_display_records,
|
235
|
+
:aaData => data,
|
236
|
+
:sEcho => params[:sEcho].to_i}.to_json
|
118
237
|
end
|
238
|
+
# ------- /Elasticsearch ----------- #
|
239
|
+
else
|
240
|
+
#
|
241
|
+
# ------- Postgres ----------- #
|
242
|
+
#
|
243
|
+
logger.debug "(datatable) #{action.to_sym} #{modelCls} < ActiveRecord"
|
119
244
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
245
|
+
define_method action.to_sym do
|
246
|
+
condition_local = ''
|
247
|
+
unless params[:sSearch].blank?
|
248
|
+
sort_column_id = params[:iSortCol_0].to_i
|
249
|
+
sort_column_id = 1 if sort_column_id == 0
|
250
|
+
sort_column = columns[sort_column_id]
|
251
|
+
condstr = params[:sSearch].strip.gsub(/_/, '\\\\_').gsub(/%/, '\\\\%')
|
252
|
+
|
253
|
+
search_columns = options[:columns].map{|e| e.class == Symbol ? e : nil }.compact
|
254
|
+
condition_local = search_columns.map do |column_name|
|
255
|
+
" ((text(#{column_name}) ILIKE '%#{condstr}%')) "
|
256
|
+
end.compact.join(" OR ")
|
257
|
+
condition_local = " ( #{condition_local} ) " unless condition_local.blank?
|
258
|
+
end
|
259
|
+
|
260
|
+
# We just need one conditions string for search at a time. Every time we input
|
261
|
+
# something else in the search bar we will pop the previous search condition
|
262
|
+
# string and push the new string.
|
263
|
+
if condition_local != ''
|
264
|
+
if add_search_option == false
|
130
265
|
conditions << condition_local
|
266
|
+
add_search_option = true
|
267
|
+
else
|
268
|
+
if conditions != []
|
269
|
+
conditions.pop
|
270
|
+
conditions << condition_local
|
271
|
+
end
|
131
272
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
273
|
+
else
|
274
|
+
if add_search_option == true
|
275
|
+
if conditions != []
|
276
|
+
conditions.pop
|
277
|
+
add_search_option = false
|
278
|
+
end
|
138
279
|
end
|
139
280
|
end
|
140
|
-
end
|
141
281
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
282
|
+
if named_scope
|
283
|
+
args = named_scope_args ? Array(self.send(named_scope_args)) : []
|
284
|
+
total_records = modelCls.send(named_scope, *args).count :conditions => init_conditions.join(" AND ")
|
285
|
+
total_display_records = modelCls.send(named_scope, *args).count :conditions => conditions.join(" AND ")
|
286
|
+
else
|
287
|
+
total_records = modelCls.count :conditions => init_conditions.join(" AND ")
|
288
|
+
total_display_records = modelCls.count :conditions => conditions.join(" AND ")
|
289
|
+
end
|
290
|
+
sort_column = params[:iSortCol_0].to_i
|
291
|
+
sort_column = 1 if sort_column == 0
|
292
|
+
current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0)+1
|
293
|
+
if named_scope
|
294
|
+
objects = modelCls.send(named_scope, *args).paginate(:page => current_page,
|
295
|
+
:order => "#{columns[sort_column][:name]} #{params[:sSortDir_0]}",
|
296
|
+
:conditions => conditions.join(" AND "),
|
297
|
+
:per_page => params[:iDisplayLength])
|
298
|
+
else
|
299
|
+
objects = modelCls.paginate(:page => current_page,
|
300
|
+
:order => "#{columns[sort_column][:name]} #{params[:sSortDir_0]}",
|
301
|
+
:conditions => conditions.join(" AND "),
|
302
|
+
:per_page => params[:iDisplayLength])
|
303
|
+
end
|
304
|
+
#logger.info("------conditions is #{conditions}")
|
305
|
+
data = objects.collect do |instance|
|
306
|
+
columns.collect { |column| datatables_instance_get_value(instance, column) }
|
307
|
+
end
|
308
|
+
render :text => {:iTotalRecords => total_records,
|
309
|
+
:iTotalDisplayRecords => total_display_records,
|
310
|
+
:aaData => data,
|
311
|
+
:sEcho => params[:sEcho].to_i}.to_json
|
312
|
+
#
|
313
|
+
# ------- /Postgres ----------- #
|
314
|
+
#
|
167
315
|
end
|
168
|
-
render :text => {:iTotalRecords => total_records,
|
169
|
-
:iTotalDisplayRecords => total_display_records,
|
170
|
-
:aaData => data,
|
171
|
-
:sEcho => params[:sEcho].to_i}.to_json
|
172
316
|
end
|
173
317
|
end
|
174
318
|
end
|
@@ -184,7 +328,7 @@ module DataTablesController
|
|
184
328
|
if column.kind_of? Symbol # a column from the database, we don't need to do anything
|
185
329
|
columns << {:name => column, :attribute => column}
|
186
330
|
elsif column.kind_of? Hash
|
187
|
-
|
331
|
+
col_hash = { :name => column[:name], :special => column }
|
188
332
|
col_hash[:attribute] = column[:attribute] if column[:attribute]
|
189
333
|
columns << col_hash
|
190
334
|
end
|
@@ -211,6 +355,18 @@ module DataTablesController
|
|
211
355
|
end
|
212
356
|
end
|
213
357
|
|
358
|
+
def elasticsearch_sanitation(search_string, except)
|
359
|
+
logger.debug "*** elasticsearch_sanitation.before = `#{search_string}'"
|
360
|
+
search_string = '*' if search_string.blank?
|
361
|
+
search_string.strip!
|
362
|
+
numerical_search = (search_string.split.count > 1) ? "" : "OR *#{search_string.gsub(":","\\:")}*"
|
363
|
+
search_string = "(\"*#{search_string}*\" #{numerical_search}) " unless search_string =~ /(\*|\")/
|
364
|
+
exceptions = except.map { |f| "NOT #{f[0]}:\"#{f[1]}\""}.join(" AND ") if except
|
365
|
+
search_string += " AND " + exceptions if exceptions
|
366
|
+
logger.debug "*** elasticsearch_sanitation.after = `#{search_string}'"
|
367
|
+
search_string
|
368
|
+
end
|
369
|
+
|
214
370
|
# gets the value for a column and row
|
215
371
|
def datatables_instance_get_value(instance, column)
|
216
372
|
if column[:special]
|
@@ -8,24 +8,33 @@ module DataTablesHelper
|
|
8
8
|
options[:bAutoWidth] = false unless options.has_key?(:bAutoWidth)
|
9
9
|
options[:bStateSave] = true unless options.has_key?(:bStateSave)
|
10
10
|
options[:oColVis] ||= {}
|
11
|
+
options[:bFilter] = true
|
11
12
|
options[:oColVis][:aiExclude] ||= []
|
12
13
|
unless options[:oColVis][:aiExclude].include?(0)
|
13
14
|
options[:oColVis][:aiExclude].unshift(0)
|
14
15
|
end
|
15
16
|
|
17
|
+
options[:bFilter] = opts[:search] unless opts[:search].nil?
|
18
|
+
|
16
19
|
options[:fnInitComplete] ||= "function() {
|
17
20
|
if (eval('typeof ' + initDatatablesTable) == 'function') {
|
18
21
|
initDatatablesTable('#{source}');
|
19
22
|
}
|
20
23
|
}"
|
21
24
|
|
22
|
-
sdom = '<"#datatables_search_hint">lfrtip'
|
25
|
+
sdom = options[:bFilter] ? '<"#datatables_search_hint">lfrtip' : 'lrtip'
|
23
26
|
sdom = "C<\"clear\">" + sdom if options[:oColVis]
|
24
27
|
sdom = 'T' + sdom if options[:oTableTools]
|
25
28
|
options[:sDom] ||= sdom
|
26
29
|
|
30
|
+
# Rails.logger.info("*****#{options.inspect}")
|
31
|
+
|
32
|
+
# options[:sDom].delete(:f)
|
33
|
+
|
27
34
|
datatable = controller.datatable_source(source)
|
28
|
-
|
35
|
+
url_query_params = opts[:urlQueryParams] || {}
|
36
|
+
options[:sAjaxSource] = opts[:sAjaxSource] ||
|
37
|
+
method("#{datatable[:action]}_url".to_sym).call(url_query_params)
|
29
38
|
columns = datatable[:attrs].keys.collect { |a| "<th>#{a}</th>" }.join
|
30
39
|
|
31
40
|
index = 0
|
@@ -36,7 +45,7 @@ module DataTablesHelper
|
|
36
45
|
index += 1
|
37
46
|
memo
|
38
47
|
end
|
39
|
-
options[:aaSorting]
|
48
|
+
options[:aaSorting] ||= [[first_searchable_column_index, 'asc']]
|
40
49
|
options[:aoColumnDefs] ||= []
|
41
50
|
options[:aoColumnDefs].unshift({
|
42
51
|
:aTargets => targets,
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'data_tables' do
|
4
|
+
before :each do
|
5
|
+
if DEBUG
|
6
|
+
ExceptOptionController.any_instance.stub_chain(:logger, :debug){ |*args| puts args.first }
|
7
|
+
ExceptOptionController.any_instance.stub_chain(:logger, :info){ |*args| puts args.first }
|
8
|
+
else
|
9
|
+
ExceptOptionController.any_instance.stub(:logger).and_return(double("Logger").as_null_object)
|
10
|
+
end
|
11
|
+
ExceptOptionController.any_instance.stub(:params).and_return({})
|
12
|
+
ExceptOptionController.any_instance.stub(:render)
|
13
|
+
ActiveRecord::Base.stub_chain(:connection, :schema_search_path).and_return("public")
|
14
|
+
end
|
15
|
+
|
16
|
+
def save_elasticsearch(index_name, data)
|
17
|
+
Tire.index(index_name) do
|
18
|
+
create unless exists?
|
19
|
+
data.each do |datum|
|
20
|
+
store datum
|
21
|
+
end
|
22
|
+
refresh
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:instance){ExceptOptionController.new.dummy_class_source}
|
27
|
+
let(:index_name){ "#{Tire::Model::Search.index_prefix}dummy_class" }
|
28
|
+
|
29
|
+
context "Redis Models" do
|
30
|
+
context "with ElasticSearch" do
|
31
|
+
{"iTotalDisplayRecords" => 2, "iTotalRecords" => 2}.each do |k,v|
|
32
|
+
it "respects datatables' except to calculate #{k} (#{v})" do
|
33
|
+
# create index
|
34
|
+
data = [{ id: 5002, name: 'not_valid', domain: 'public' },
|
35
|
+
{ id: 561, name: 'Native AP VLAN', domain: 'public' },
|
36
|
+
{ id: 56, name: 'valid', domain: 'public' }]
|
37
|
+
save_elasticsearch(index_name, data)
|
38
|
+
|
39
|
+
ExceptOptionController.any_instance.should_receive(:render) do |*args|
|
40
|
+
arg = JSON.parse(args.first[:text])
|
41
|
+
arg[k].should == v
|
42
|
+
end
|
43
|
+
instance
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context "Elastic Search Models" do
|
49
|
+
end
|
50
|
+
context "Postgres Models" do
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class DummyClass < Ohm::Model
|
2
|
+
end
|
3
|
+
|
4
|
+
class ConditionModel
|
5
|
+
include DataTablesController
|
6
|
+
datatables_source(:dummy_class_source, :dummy_class,
|
7
|
+
:columns => [
|
8
|
+
{:name => "Actions",
|
9
|
+
:method => :datatables_actions_column},
|
10
|
+
:name, :vlan, :cidr
|
11
|
+
],
|
12
|
+
:conditions => ['id!=5002',"name!= 'Native AP VLAN'"])
|
13
|
+
end
|
14
|
+
|
15
|
+
class ExceptOptionController
|
16
|
+
include DataTablesController
|
17
|
+
datatables_source(:dummy_class_source, :dummy_class,
|
18
|
+
:columns => [
|
19
|
+
{:name => "Actions",
|
20
|
+
:method => :datatables_actions_column},
|
21
|
+
:name, :state, :cidr, :vlan,
|
22
|
+
{:name =>"Access Points",
|
23
|
+
:method => :location_statuses_accesspoint_list}
|
24
|
+
],:except => [['name','Native AP VLAN']])
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'ohm'
|
4
|
+
require 'tire'
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__) + '/..'))
|
8
|
+
Dir[File.join(APP_ROOT, "lib/*.rb")].each {|f| require f}
|
9
|
+
Dir[File.join(APP_ROOT, "lib/data_tables/*.rb")].each {|f| require f}
|
10
|
+
Dir[File.join(APP_ROOT, "spec/models/*.rb")].each {|f| require f}
|
11
|
+
|
12
|
+
DEBUG = false
|
13
|
+
|
14
|
+
Tire.configure { logger STDOUT } if DEBUG
|
15
|
+
Tire::Model::Search.index_prefix('test_datatable_')
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.before(:each) do
|
19
|
+
Tire.index("#{Tire::Model::Search.index_prefix}*"){delete}
|
20
|
+
end
|
21
|
+
config.after(:each) do
|
22
|
+
#Tire.index("#{Tire::Model::Search.index_prefix}*"){delete}
|
23
|
+
end
|
24
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -2,26 +2,58 @@
|
|
2
2
|
name: data_tables
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.9
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Duane Compton
|
9
9
|
- Calvin Bascom
|
10
10
|
- Yi Su
|
11
11
|
- Chris Moos
|
12
|
+
- Adrian Mejia
|
12
13
|
autorequire:
|
13
14
|
bindir: bin
|
14
15
|
cert_chain: []
|
15
|
-
date:
|
16
|
-
dependencies:
|
17
|
-
|
16
|
+
date: 2013-07-03 00:00:00.000000000 Z
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
type: :development
|
20
|
+
name: rspec
|
21
|
+
prerelease: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.10'
|
27
|
+
none: false
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.10'
|
33
|
+
none: false
|
34
|
+
description: DataTables for Rails
|
18
35
|
email: Duane.Compton@gmail.com
|
19
36
|
executables: []
|
20
37
|
extensions: []
|
21
38
|
extra_rdoc_files: []
|
22
39
|
files:
|
40
|
+
- .gitignore
|
41
|
+
- .rspec
|
42
|
+
- Gemfile
|
43
|
+
- Gemfile.lock
|
44
|
+
- Guardfile
|
45
|
+
- MIT-LICENSE
|
46
|
+
- README
|
47
|
+
- Rakefile
|
48
|
+
- data_tables.gemspec
|
23
49
|
- lib/data_tables.rb
|
24
50
|
- lib/data_tables/data_tables_helper.rb
|
51
|
+
- spec/data_tables_spec.rb
|
52
|
+
- spec/models/condition_model.rb
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
- tasks/datatables_tasks.rake
|
55
|
+
- test/datatables_test.rb
|
56
|
+
- test/test_helper.rb
|
25
57
|
homepage: http://rubygems.org/gems/data_tables
|
26
58
|
licenses: []
|
27
59
|
post_install_message:
|
@@ -46,4 +78,9 @@ rubygems_version: 1.8.24
|
|
46
78
|
signing_key:
|
47
79
|
specification_version: 3
|
48
80
|
summary: Rails friendly interface into DataTables
|
49
|
-
test_files:
|
81
|
+
test_files:
|
82
|
+
- spec/data_tables_spec.rb
|
83
|
+
- spec/models/condition_model.rb
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
- test/datatables_test.rb
|
86
|
+
- test/test_helper.rb
|