datatables-net 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +73 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +618 -0
  6. data/Rakefile +14 -0
  7. data/lib/ajax-datatables-rails.rb +11 -0
  8. data/lib/ajax-datatables-rails/base.rb +205 -0
  9. data/lib/ajax-datatables-rails/config.rb +24 -0
  10. data/lib/ajax-datatables-rails/datatable/column.rb +106 -0
  11. data/lib/ajax-datatables-rails/datatable/datatable.rb +69 -0
  12. data/lib/ajax-datatables-rails/datatable/simple_order.rb +31 -0
  13. data/lib/ajax-datatables-rails/datatable/simple_search.rb +19 -0
  14. data/lib/ajax-datatables-rails/orm/active_record.rb +52 -0
  15. data/lib/ajax-datatables-rails/version.rb +3 -0
  16. data/lib/generators/datatable/config_generator.rb +17 -0
  17. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +7 -0
  18. data/lib/generators/rails/datatable_generator.rb +27 -0
  19. data/lib/generators/rails/templates/datatable.rb +41 -0
  20. data/spec/ajax-datatables-rails/base_spec.rb +140 -0
  21. data/spec/ajax-datatables-rails/configuration_spec.rb +43 -0
  22. data/spec/ajax-datatables-rails/datatable/column_spec.rb +65 -0
  23. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +97 -0
  24. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +12 -0
  25. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +16 -0
  26. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +154 -0
  27. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +51 -0
  28. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +42 -0
  29. data/spec/ajax-datatables-rails/orm/active_record_spec.rb +34 -0
  30. data/spec/schema.rb +43 -0
  31. data/spec/spec_helper.rb +10 -0
  32. data/spec/test_helpers.rb +66 -0
  33. data/spec/test_models.rb +20 -0
  34. metadata +202 -0
@@ -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