datatables-net 0.4.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +73 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +618 -0
- data/Rakefile +14 -0
- data/lib/ajax-datatables-rails.rb +11 -0
- data/lib/ajax-datatables-rails/base.rb +205 -0
- data/lib/ajax-datatables-rails/config.rb +24 -0
- data/lib/ajax-datatables-rails/datatable/column.rb +106 -0
- data/lib/ajax-datatables-rails/datatable/datatable.rb +69 -0
- data/lib/ajax-datatables-rails/datatable/simple_order.rb +31 -0
- data/lib/ajax-datatables-rails/datatable/simple_search.rb +19 -0
- data/lib/ajax-datatables-rails/orm/active_record.rb +52 -0
- data/lib/ajax-datatables-rails/version.rb +3 -0
- data/lib/generators/datatable/config_generator.rb +17 -0
- data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +7 -0
- data/lib/generators/rails/datatable_generator.rb +27 -0
- data/lib/generators/rails/templates/datatable.rb +41 -0
- data/spec/ajax-datatables-rails/base_spec.rb +140 -0
- data/spec/ajax-datatables-rails/configuration_spec.rb +43 -0
- data/spec/ajax-datatables-rails/datatable/column_spec.rb +65 -0
- data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +97 -0
- data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +12 -0
- data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +16 -0
- data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +154 -0
- data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +51 -0
- data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +42 -0
- data/spec/ajax-datatables-rails/orm/active_record_spec.rb +34 -0
- data/spec/schema.rb +43 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/test_helpers.rb +66 -0
- data/spec/test_models.rb +20 -0
- metadata +202 -0
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
task :console do
|
9
|
+
require 'pry'
|
10
|
+
require 'rails'
|
11
|
+
require 'ajax-datatables-rails'
|
12
|
+
ARGV.clear
|
13
|
+
Pry.start
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ajax-datatables-rails/version'
|
2
|
+
require 'ajax-datatables-rails/config'
|
3
|
+
require 'ajax-datatables-rails/base'
|
4
|
+
require 'ajax-datatables-rails/datatable/datatable'
|
5
|
+
require 'ajax-datatables-rails/datatable/simple_search'
|
6
|
+
require 'ajax-datatables-rails/datatable/simple_order'
|
7
|
+
require 'ajax-datatables-rails/datatable/column'
|
8
|
+
require 'ajax-datatables-rails/orm/active_record'
|
9
|
+
|
10
|
+
module AjaxDatatablesRails
|
11
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module AjaxDatatablesRails
|
2
|
+
class NotImplemented < StandardError; end
|
3
|
+
|
4
|
+
class Base
|
5
|
+
extend Forwardable
|
6
|
+
<<<<<<< HEAD
|
7
|
+
include ActiveRecord::Sanitization::ClassMethods
|
8
|
+
class MethodNotImplementedError < StandardError; end
|
9
|
+
=======
|
10
|
+
>>>>>>> v-0-4-0
|
11
|
+
|
12
|
+
attr_reader :view, :options
|
13
|
+
def_delegator :@view, :params, :params
|
14
|
+
|
15
|
+
def initialize(view, options = {})
|
16
|
+
@view = view
|
17
|
+
@options = options
|
18
|
+
load_orm_extension
|
19
|
+
end
|
20
|
+
|
21
|
+
def config
|
22
|
+
@config ||= AjaxDatatablesRails.config
|
23
|
+
end
|
24
|
+
|
25
|
+
def datatable
|
26
|
+
@datatable ||= Datatable::Datatable.new self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Must overrited methods
|
30
|
+
def view_columns
|
31
|
+
fail(NotImplemented, view_columns_error_text)
|
32
|
+
end
|
33
|
+
|
34
|
+
def data
|
35
|
+
fail(NotImplemented, data_error_text)
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_raw_records
|
39
|
+
fail(NotImplemented, raw_records_error_text)
|
40
|
+
end
|
41
|
+
|
42
|
+
def as_json
|
43
|
+
{
|
44
|
+
recordsTotal: total_records,
|
45
|
+
recordsFiltered: filter_records,
|
46
|
+
data: data
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def total_records
|
51
|
+
get_raw_records.count(:all)
|
52
|
+
end
|
53
|
+
|
54
|
+
def filtered_records
|
55
|
+
get_raw_records.model.from("(#{filter_records(get_raw_records).except(:limit, :offset, :order).to_sql}) AS foo").count
|
56
|
+
end
|
57
|
+
|
58
|
+
def records
|
59
|
+
@records ||= retrieve_records
|
60
|
+
end
|
61
|
+
|
62
|
+
# helper methods
|
63
|
+
def searchable_columns
|
64
|
+
@searchable_columns ||= begin
|
65
|
+
connected_columns.select &:searchable?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def search_columns
|
70
|
+
@search_columns ||= begin
|
71
|
+
searchable_columns.select { |column| column.search.value.present? }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def connected_columns
|
76
|
+
@connected_columns ||= begin
|
77
|
+
view_columns.keys.map do |field_name|
|
78
|
+
datatable.column_by(:data, field_name.to_s)
|
79
|
+
end.compact
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
# view_columns can be an Array or Hash. we have to support all these formats of defining columns
|
85
|
+
def connect_view_columns
|
86
|
+
# @connect_view_columns ||= begin
|
87
|
+
# adapted_options =
|
88
|
+
# case view_columns
|
89
|
+
# when Hash
|
90
|
+
# when Array
|
91
|
+
# cols = {}
|
92
|
+
# view_columns.each_with_index({}) do |index, source|
|
93
|
+
# cols[index.to_s] = { source: source }
|
94
|
+
# end
|
95
|
+
# cols
|
96
|
+
# else
|
97
|
+
# view_columns
|
98
|
+
# end
|
99
|
+
# ActiveSupport::HashWithIndifferentAccess.new adapted_options
|
100
|
+
# end
|
101
|
+
end
|
102
|
+
|
103
|
+
def retrieve_records
|
104
|
+
records = fetch_records
|
105
|
+
records = filter_records(records)
|
106
|
+
records = sort_records(records) if datatable.orderable?
|
107
|
+
records = paginate_records(records) if datatable.paginate?
|
108
|
+
records
|
109
|
+
end
|
110
|
+
|
111
|
+
# Private helper methods
|
112
|
+
def load_orm_extension
|
113
|
+
case config.orm
|
114
|
+
when :mongoid then nil
|
115
|
+
when :active_record then extend ORM::ActiveRecord
|
116
|
+
else
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
<<<<<<< HEAD
|
122
|
+
def new_search_condition(column, value)
|
123
|
+
model, column = column.split('.')
|
124
|
+
model = model.constantize
|
125
|
+
casted_column = ::Arel::Nodes::NamedFunction.new('CAST', [model.arel_table[column.to_sym].as(typecast)])
|
126
|
+
casted_column.matches("%#{sanitize_sql_like(value)}%")
|
127
|
+
end
|
128
|
+
|
129
|
+
def deprecated_search_condition(column, value)
|
130
|
+
model, column = column.split('.')
|
131
|
+
model = model.singularize.titleize.gsub( / /, '' ).constantize
|
132
|
+
|
133
|
+
casted_column = ::Arel::Nodes::NamedFunction.new('CAST', [model.arel_table[column.to_sym].as(typecast)])
|
134
|
+
casted_column.matches("%#{sanitize_sql_like(value)}%")
|
135
|
+
end
|
136
|
+
|
137
|
+
def aggregate_query
|
138
|
+
conditions = searchable_columns.each_with_index.map do |column, index|
|
139
|
+
value = params[:columns]["#{index}"][:search][:value] if params[:columns]
|
140
|
+
search_condition(column, value) unless value.blank?
|
141
|
+
end
|
142
|
+
conditions.compact.reduce(:and)
|
143
|
+
end
|
144
|
+
|
145
|
+
def typecast
|
146
|
+
case config.db_adapter
|
147
|
+
when :oracle then 'VARCHAR2(4000)'
|
148
|
+
when :pg then 'VARCHAR'
|
149
|
+
when :mysql2 then 'CHAR'
|
150
|
+
when :sqlite3 then 'TEXT'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def offset
|
155
|
+
(page - 1) * per_page
|
156
|
+
end
|
157
|
+
|
158
|
+
def page
|
159
|
+
(params[:start].to_i / per_page) + 1
|
160
|
+
end
|
161
|
+
|
162
|
+
def per_page
|
163
|
+
params.fetch(:length, 10).to_i
|
164
|
+
end
|
165
|
+
|
166
|
+
def sort_column(item)
|
167
|
+
new_sort_column(item)
|
168
|
+
rescue
|
169
|
+
::AjaxDatatablesRails::Base.deprecated '[DEPRECATED] Using table_name.column_name notation is deprecated. Please refer to: https://github.com/antillas21/ajax-datatables-rails#searchable-and-sortable-columns-syntax'
|
170
|
+
deprecated_sort_column(item)
|
171
|
+
end
|
172
|
+
|
173
|
+
def deprecated_sort_column(item)
|
174
|
+
sortable_columns[sortable_displayed_columns.index(item[:column])]
|
175
|
+
end
|
176
|
+
=======
|
177
|
+
def raw_records_error_text
|
178
|
+
return <<-eos
|
179
|
+
|
180
|
+
You should implement this method in your class and specify
|
181
|
+
how records are going to be retrieved from the database.
|
182
|
+
eos
|
183
|
+
end
|
184
|
+
|
185
|
+
def data_error_text
|
186
|
+
return <<-eos
|
187
|
+
>>>>>>> v-0-4-0
|
188
|
+
|
189
|
+
You should implement this method in your class and return an array
|
190
|
+
of arrays, or an array of hashes, as defined in the jQuery.dataTables
|
191
|
+
plugin documentation.
|
192
|
+
eos
|
193
|
+
end
|
194
|
+
|
195
|
+
def view_columns_error_text
|
196
|
+
return <<-eos
|
197
|
+
|
198
|
+
You should implement this method in your class and return an array
|
199
|
+
of database columns based on the columns displayed in the HTML view.
|
200
|
+
These columns should be represented in the ModelName.column_name,
|
201
|
+
or aliased_join_table.column_name notation.
|
202
|
+
eos
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support/configurable'
|
2
|
+
|
3
|
+
module AjaxDatatablesRails
|
4
|
+
|
5
|
+
# configure AjaxDatatablesRails global settings
|
6
|
+
# AjaxDatatablesRails.configure do |config|
|
7
|
+
# config.db_adapter = :pg
|
8
|
+
# end
|
9
|
+
def self.configure &block
|
10
|
+
yield @config ||= AjaxDatatablesRails::Configuration.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# AjaxDatatablesRails global settings
|
14
|
+
def self.config
|
15
|
+
@config ||= AjaxDatatablesRails::Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
class Configuration
|
19
|
+
include ActiveSupport::Configurable
|
20
|
+
|
21
|
+
config_accessor(:orm) { :active_record }
|
22
|
+
config_accessor(:db_adapter) { :pg }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module AjaxDatatablesRails
|
2
|
+
module Datatable
|
3
|
+
class Column
|
4
|
+
attr_reader :datatable, :index, :options
|
5
|
+
|
6
|
+
def initialize(datatable, index, options)
|
7
|
+
@datatable, @index, @options = datatable, index, options
|
8
|
+
@view_column = datatable.view_columns[options["data"].to_sym]
|
9
|
+
end
|
10
|
+
|
11
|
+
def data
|
12
|
+
options[:data].presence || options[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def searchable?
|
16
|
+
options[:searchable] == TRUE_VALUE
|
17
|
+
end
|
18
|
+
|
19
|
+
def orderable?
|
20
|
+
options[:orderable] == TRUE_VALUE
|
21
|
+
end
|
22
|
+
|
23
|
+
def search
|
24
|
+
@search ||= SimpleSearch.new(options[:search])
|
25
|
+
end
|
26
|
+
|
27
|
+
def search= value
|
28
|
+
@search = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def cond
|
32
|
+
@view_column[:cond] || :like
|
33
|
+
end
|
34
|
+
|
35
|
+
def filter value
|
36
|
+
@view_column[:cond].call self
|
37
|
+
end
|
38
|
+
|
39
|
+
def source
|
40
|
+
@view_column[:source]
|
41
|
+
end
|
42
|
+
|
43
|
+
def table
|
44
|
+
model = source.split('.').first.constantize
|
45
|
+
model.arel_table rescue model
|
46
|
+
end
|
47
|
+
|
48
|
+
def field
|
49
|
+
source.split('.').last.to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
def search_query
|
53
|
+
search.regexp? ? regex_search : non_regex_search
|
54
|
+
end
|
55
|
+
|
56
|
+
def sort_query
|
57
|
+
if custom_field?
|
58
|
+
source
|
59
|
+
else
|
60
|
+
"#{ table.name }.#{ field }"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def custom_field?
|
66
|
+
!source.include?('.')
|
67
|
+
end
|
68
|
+
|
69
|
+
def config
|
70
|
+
@config ||= AjaxDatatablesRails.config
|
71
|
+
end
|
72
|
+
|
73
|
+
def regex_search
|
74
|
+
::Arel::Nodes::Regexp.new((custom_field? ? field : table[field]), ::Arel::Nodes.build_quoted(search.value))
|
75
|
+
end
|
76
|
+
|
77
|
+
def non_regex_search
|
78
|
+
case cond
|
79
|
+
when Proc
|
80
|
+
filter search.value
|
81
|
+
when :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in
|
82
|
+
if custom_field?
|
83
|
+
::Arel::Nodes::SqlLiteral.new(field).eq(search.value)
|
84
|
+
else
|
85
|
+
table[field].send(cond, search.value)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
casted_column = ::Arel::Nodes::NamedFunction.new(
|
89
|
+
'CAST', [table[field].as(typecast)]
|
90
|
+
)
|
91
|
+
casted_column.matches("%#{ search.value }%")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def typecast
|
96
|
+
case config.db_adapter
|
97
|
+
when :mysql, :mysql2 then 'CHAR'
|
98
|
+
when :sqlite, :sqlite3 then 'TEXT'
|
99
|
+
else
|
100
|
+
'VARCHAR'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module AjaxDatatablesRails
|
2
|
+
module Datatable
|
3
|
+
|
4
|
+
TRUE_VALUE = 'true'
|
5
|
+
|
6
|
+
class Datatable
|
7
|
+
attr_reader :datatable, :options
|
8
|
+
|
9
|
+
def initialize datatable
|
10
|
+
@datatable = datatable
|
11
|
+
@options = datatable.params
|
12
|
+
end
|
13
|
+
|
14
|
+
# ----------------- ORDER METHODS --------------------
|
15
|
+
|
16
|
+
def orderable?
|
17
|
+
options[:order].present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def orders
|
21
|
+
@orders ||= options[:order].map { |index, order_options| SimpleOrder.new(self, order_options) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def order_by(how, what)
|
25
|
+
orders.find { |simple_order| simple_order.send(how) == what }
|
26
|
+
end
|
27
|
+
|
28
|
+
# ----------------- SEARCH METHODS --------------------
|
29
|
+
|
30
|
+
def searchable?
|
31
|
+
options[:search].present? && options[:search][:value].present?
|
32
|
+
end
|
33
|
+
|
34
|
+
def search
|
35
|
+
@search ||= SimpleSearch.new(options[:search])
|
36
|
+
end
|
37
|
+
|
38
|
+
# ----------------- COLUMN METHODS --------------------
|
39
|
+
|
40
|
+
def columns
|
41
|
+
@columns ||= options[:columns].map do |index, column_options|
|
42
|
+
Column.new(datatable, index, column_options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def column_by how, what
|
47
|
+
columns.find { |simple_column| simple_column.send(how) == what }
|
48
|
+
end
|
49
|
+
|
50
|
+
# ----------------- OPTIONS METHODS --------------------
|
51
|
+
|
52
|
+
def paginate?
|
53
|
+
per_page != -1
|
54
|
+
end
|
55
|
+
|
56
|
+
def offset
|
57
|
+
(page - 1) * per_page
|
58
|
+
end
|
59
|
+
|
60
|
+
def page
|
61
|
+
(options[:start].to_i / per_page) + 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def per_page
|
65
|
+
options.fetch(:length, 10).to_i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AjaxDatatablesRails
|
2
|
+
module Datatable
|
3
|
+
class SimpleOrder
|
4
|
+
attr_reader :datatable, :options
|
5
|
+
|
6
|
+
DIRECTIONS = %w(DESC ASC)
|
7
|
+
|
8
|
+
def initialize(datatable, options)
|
9
|
+
@datatable = datatable
|
10
|
+
@options = options || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def query sort_column
|
14
|
+
"#{ sort_column } #{ dir }"
|
15
|
+
end
|
16
|
+
|
17
|
+
def column
|
18
|
+
datatable.column_by(:index, column_index)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def dir
|
23
|
+
DIRECTIONS.find { |direction| direction == options[:dir].upcase } || 'ASC'
|
24
|
+
end
|
25
|
+
|
26
|
+
def column_index
|
27
|
+
options[:column]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|