data_tables 0.1.7 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|