ajax-datatables-rails 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +26 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +1157 -0
  6. data/.travis.yml +68 -0
  7. data/Appraisals +34 -0
  8. data/Gemfile +5 -1
  9. data/LICENSE +17 -18
  10. data/README.md +239 -239
  11. data/Rakefile +1 -1
  12. data/ajax-datatables-rails.gemspec +31 -24
  13. data/gemfiles/rails_4.0.13.gemfile +14 -0
  14. data/gemfiles/rails_4.1.15.gemfile +14 -0
  15. data/gemfiles/rails_4.2.8.gemfile +13 -0
  16. data/gemfiles/rails_5.0.3.gemfile +13 -0
  17. data/gemfiles/rails_5.1.1.gemfile +13 -0
  18. data/lib/ajax-datatables-rails.rb +9 -8
  19. data/lib/ajax-datatables-rails/base.rb +80 -156
  20. data/lib/ajax-datatables-rails/config.rb +8 -5
  21. data/lib/ajax-datatables-rails/datatable/column.rb +169 -0
  22. data/lib/ajax-datatables-rails/datatable/column_date_filter.rb +41 -0
  23. data/lib/ajax-datatables-rails/datatable/datatable.rb +79 -0
  24. data/lib/ajax-datatables-rails/datatable/simple_order.rb +31 -0
  25. data/lib/ajax-datatables-rails/datatable/simple_search.rb +18 -0
  26. data/lib/ajax-datatables-rails/orm/active_record.rb +52 -0
  27. data/lib/ajax-datatables-rails/version.rb +1 -1
  28. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +3 -3
  29. data/lib/generators/rails/datatable_generator.rb +7 -19
  30. data/lib/generators/rails/templates/datatable.rb +26 -14
  31. data/spec/ajax-datatables-rails/base_spec.rb +190 -0
  32. data/spec/ajax-datatables-rails/configuration_spec.rb +43 -0
  33. data/spec/ajax-datatables-rails/datatable/column_spec.rb +109 -0
  34. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +87 -0
  35. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +13 -0
  36. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +17 -0
  37. data/spec/ajax-datatables-rails/extended_spec.rb +20 -0
  38. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +439 -0
  39. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +66 -0
  40. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +34 -0
  41. data/spec/ajax-datatables-rails/orm/active_record_spec.rb +25 -0
  42. data/spec/factories/user.rb +9 -0
  43. data/spec/install_oracle.sh +12 -0
  44. data/spec/spec_helper.rb +75 -3
  45. data/spec/support/schema.rb +14 -0
  46. data/spec/support/test_helpers.rb +174 -0
  47. data/spec/support/test_models.rb +2 -0
  48. metadata +169 -37
  49. data/lib/ajax-datatables-rails/extensions/kaminari.rb +0 -12
  50. data/lib/ajax-datatables-rails/extensions/simple_paginator.rb +0 -12
  51. data/lib/ajax-datatables-rails/extensions/will_paginate.rb +0 -12
  52. data/lib/ajax-datatables-rails/models.rb +0 -6
  53. data/spec/ajax-datatables-rails/ajax_datatables_rails_spec.rb +0 -351
  54. data/spec/ajax-datatables-rails/kaminari_spec.rb +0 -35
  55. data/spec/ajax-datatables-rails/models_spec.rb +0 -10
  56. data/spec/ajax-datatables-rails/simple_paginator_spec.rb +0 -32
  57. data/spec/ajax-datatables-rails/will_paginate_spec.rb +0 -28
  58. data/spec/schema.rb +0 -35
  59. data/spec/test_models.rb +0 -21
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'bundler/gem_tasks'
3
3
  require 'rspec/core/rake_task'
4
4
 
5
5
  RSpec::Core::RakeTask.new(:spec)
6
- task :default => :spec
6
+ task default: :spec
7
7
 
8
8
  task :console do
9
9
  require 'pry'
@@ -1,30 +1,37 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
1
+ # coding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
4
3
  require 'ajax-datatables-rails/version'
5
4
 
6
- Gem::Specification.new do |gem|
7
- gem.name = "ajax-datatables-rails"
8
- gem.version = AjaxDatatablesRails::VERSION
9
- gem.authors = ["Joel Quenneville"]
10
- gem.email = ["joel.quenneville@collegeplus.org"]
11
- gem.description = %q{A gem that simplifies using datatables and hundreds of records via ajax}
12
- gem.summary = %q{A wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app}
13
- gem.homepage = ""
14
- gem.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ajax-datatables-rails'
7
+ s.version = AjaxDatatablesRails::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Joel Quenneville', 'Antonio Antillon']
10
+ s.email = ['joel.quenneville@collegeplus.org', 'antillas21@gmail.com']
11
+ s.homepage = 'https://github.com/jbox-web/ajax-datatables-rails'
12
+ s.summary = %q{A gem that simplifies using datatables and hundreds of records via ajax}
13
+ s.description = %q{A wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app}
14
+ s.license = 'MIT'
15
15
 
16
- gem.files = Dir["{lib,spec}/**/*", "[A-Z]*"] - ["Gemfile.lock"]
17
- gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
- gem.require_path = "lib"
16
+ s.add_dependency 'railties', '>= 4.0'
20
17
 
21
- gem.add_dependency 'railties', '>= 3.1'
18
+ s.add_development_dependency 'rails', '>= 4.0'
19
+ s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'pg'
21
+ s.add_development_dependency 'mysql2'
22
+ s.add_development_dependency 'sqlite3'
23
+ s.add_development_dependency 'activerecord-oracle_enhanced-adapter'
24
+ s.add_development_dependency 'rspec'
25
+ s.add_development_dependency 'generator_spec'
26
+ s.add_development_dependency 'pry'
27
+ s.add_development_dependency 'simplecov'
28
+ s.add_development_dependency 'database_cleaner'
29
+ s.add_development_dependency 'factory_girl'
30
+ s.add_development_dependency 'faker'
31
+ s.add_development_dependency 'appraisal'
22
32
 
23
- gem.add_development_dependency "rspec"
24
- gem.add_development_dependency "generator_spec"
25
- gem.add_development_dependency "pry"
26
- gem.add_development_dependency "rake"
27
- gem.add_development_dependency "sqlite3"
28
- gem.add_development_dependency "rails", ">= 3.1.0"
29
- gem.add_development_dependency "activerecord", ">= 4.1.6"
33
+ s.files = `git ls-files`.split("\n")
34
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
35
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
36
+ s.require_paths = ['lib']
30
37
  end
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.0.13"
6
+ gem "mysql2", "~> 0.3.18"
7
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.5.0"
8
+ gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
9
+
10
+ group :test do
11
+ gem "codeclimate-test-reporter", "~> 1.0.0"
12
+ end
13
+
14
+ gemspec path: "../"
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.1.15"
6
+ gem "mysql2", "~> 0.3.18"
7
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.5.0"
8
+ gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
9
+
10
+ group :test do
11
+ gem "codeclimate-test-reporter", "~> 1.0.0"
12
+ end
13
+
14
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.2.8"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.6.0"
7
+ gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", "~> 1.0.0"
11
+ end
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "5.0.3"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.7.0"
7
+ gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", "~> 1.0.0"
11
+ end
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "5.1.1"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.8.0"
7
+ gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", "~> 1.0.0"
11
+ end
12
+
13
+ gemspec path: "../"
@@ -1,10 +1,11 @@
1
- require 'ajax-datatables-rails/version'
2
- require 'ajax-datatables-rails/config'
3
- require 'ajax-datatables-rails/models'
4
- require 'ajax-datatables-rails/base'
5
- require 'ajax-datatables-rails/extensions/simple_paginator'
6
- require 'ajax-datatables-rails/extensions/kaminari'
7
- require 'ajax-datatables-rails/extensions/will_paginate'
8
-
9
1
  module AjaxDatatablesRails
2
+ require 'ajax-datatables-rails/version'
3
+ require 'ajax-datatables-rails/config'
4
+ require 'ajax-datatables-rails/base'
5
+ require 'ajax-datatables-rails/datatable/datatable'
6
+ require 'ajax-datatables-rails/datatable/simple_search'
7
+ require 'ajax-datatables-rails/datatable/simple_order'
8
+ require 'ajax-datatables-rails/datatable/column_date_filter' unless AjaxDatatablesRails.old_rails?
9
+ require 'ajax-datatables-rails/datatable/column'
10
+ require 'ajax-datatables-rails/orm/active_record'
10
11
  end
@@ -1,214 +1,138 @@
1
1
  module AjaxDatatablesRails
2
+ class NotImplemented < StandardError; end
3
+
2
4
  class Base
3
5
  extend Forwardable
4
- class MethodNotImplementedError < StandardError; end
5
6
 
6
- attr_reader :view, :options, :sortable_columns, :searchable_columns
7
- def_delegator :@view, :params, :params
7
+ attr_reader :view, :options
8
+ def_delegator :@view, :params
8
9
 
9
10
  def initialize(view, options = {})
10
- @view = view
11
+ @view = view
11
12
  @options = options
12
- load_paginator
13
+ load_orm_extension
13
14
  end
14
15
 
15
16
  def config
16
17
  @config ||= AjaxDatatablesRails.config
17
18
  end
18
19
 
19
- def sortable_columns
20
- @sortable_columns ||= []
21
- end
22
-
23
- def searchable_columns
24
- @searchable_columns ||= []
20
+ def datatable
21
+ @datatable ||= Datatable::Datatable.new(self)
25
22
  end
26
23
 
27
- def data
28
- fail(
29
- MethodNotImplementedError,
30
- 'Please implement this method in your class.'
31
- )
24
+ def view_columns
25
+ fail(NotImplemented, view_columns_error_text)
32
26
  end
33
27
 
34
28
  def get_raw_records
35
- fail(
36
- MethodNotImplementedError,
37
- 'Please implement this method in your class.'
38
- )
29
+ fail(NotImplemented, raw_records_error_text)
39
30
  end
40
31
 
41
- def as_json(options = {})
42
- {
43
- :draw => params[:draw].to_i,
44
- :recordsTotal => get_raw_records.count(:all),
45
- :recordsFiltered => filter_records(get_raw_records).count(:all),
46
- :data => data
47
- }
32
+ def data
33
+ fail(NotImplemented, data_error_text)
48
34
  end
49
35
 
50
- def self.deprecated(message, caller = Kernel.caller[1])
51
- warning = caller + ": " + message
52
-
53
- if(respond_to?(:logger) && logger.present?)
54
- logger.warn(warning)
55
- else
56
- warn(warning)
57
- end
36
+ def additional_datas
37
+ {}
58
38
  end
59
39
 
60
- private
61
-
62
- def records
63
- @records ||= fetch_records
40
+ def as_json(*)
41
+ {
42
+ recordsTotal: records_total_count,
43
+ recordsFiltered: records_filtered_count,
44
+ data: sanitize(data)
45
+ }.merge(additional_datas)
64
46
  end
65
47
 
66
- def fetch_records
67
- records = get_raw_records
68
- records = sort_records(records) if params[:order].present?
69
- records = filter_records(records) if params[:search].present?
70
- records = paginate_records(records) unless params[:length].present? && params[:length] == '-1'
71
- records
48
+ def records
49
+ @records ||= retrieve_records
72
50
  end
73
51
 
74
- def sort_records(records)
75
- sort_by = []
76
- params[:order].each_value do |item|
77
- sort_by << "#{sort_column(item)} #{sort_direction(item)}"
52
+ # helper methods
53
+ def connected_columns
54
+ @connected_columns ||= begin
55
+ view_columns.keys.map do |field_name|
56
+ datatable.column_by(:data, field_name.to_s)
57
+ end.compact
78
58
  end
79
- records.order(sort_by.join(", "))
80
- end
81
-
82
- def paginate_records(records)
83
- fail(
84
- MethodNotImplementedError,
85
- 'Please mixin a pagination extension.'
86
- )
87
- end
88
-
89
- def filter_records(records)
90
- records = simple_search(records)
91
- records = composite_search(records)
92
- records
93
- end
94
-
95
- def simple_search(records)
96
- return records unless (params[:search].present? && params[:search][:value].present?)
97
- conditions = build_conditions_for(params[:search][:value])
98
- records = records.where(conditions) if conditions
99
- records
100
59
  end
101
60
 
102
- def composite_search(records)
103
- conditions = aggregate_query
104
- records = records.where(conditions) if conditions
105
- records
106
- end
107
-
108
- def build_conditions_for(query)
109
- search_for = query.split(' ')
110
- criteria = search_for.inject([]) do |criteria, atom|
111
- criteria << searchable_columns.map { |col| search_condition(col, atom) }.reduce(:or)
112
- end.reduce(:and)
113
- criteria
114
- end
115
-
116
- def search_condition(column, value)
117
- if column[0] == column.downcase[0]
118
- ::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'
119
- return deprecated_search_condition(column, value)
120
- else
121
- return new_search_condition(column, value)
61
+ def searchable_columns
62
+ @searchable_columns ||= begin
63
+ connected_columns.select(&:searchable?)
122
64
  end
123
65
  end
124
66
 
125
- def new_search_condition(column, value)
126
- model, column = column.split('.')
127
- model = model.constantize
128
- casted_column = ::Arel::Nodes::NamedFunction.new('CAST', [model.arel_table[column.to_sym].as(typecast)])
129
- casted_column.matches("%#{value}%")
130
- end
131
-
132
- def deprecated_search_condition(column, value)
133
- model, column = column.split('.')
134
- model = model.singularize.titleize.gsub( / /, '' ).constantize
135
-
136
- casted_column = ::Arel::Nodes::NamedFunction.new('CAST', [model.arel_table[column.to_sym].as(typecast)])
137
- casted_column.matches("%#{value}%")
138
- end
139
-
140
- def aggregate_query
141
- conditions = searchable_columns.each_with_index.map do |column, index|
142
- value = params[:columns]["#{index}"][:search][:value] if params[:columns]
143
- search_condition(column, value) unless value.blank?
67
+ def search_columns
68
+ @search_columns ||= begin
69
+ searchable_columns.select(&:searched?)
144
70
  end
145
- conditions.compact.reduce(:and)
146
71
  end
147
72
 
148
- def typecast
149
- case config.db_adapter
150
- when :oracle then 'VARCHAR2(4000)'
151
- when :pg then 'VARCHAR'
152
- when :mysql2 then 'CHAR'
153
- when :sqlite3 then 'TEXT'
73
+ private
74
+
75
+ def sanitize(data)
76
+ data.map do |record|
77
+ if record.is_a?(Array)
78
+ record.map { |td| ERB::Util.html_escape(td) }
79
+ else
80
+ record.update(record){ |_, v| ERB::Util.html_escape(v) }
81
+ end
154
82
  end
155
83
  end
156
84
 
157
- def offset
158
- (page - 1) * per_page
85
+ def retrieve_records
86
+ records = fetch_records
87
+ records = filter_records(records)
88
+ records = sort_records(records) if datatable.orderable?
89
+ records = paginate_records(records) if datatable.paginate?
90
+ records
159
91
  end
160
92
 
161
- def page
162
- (params[:start].to_i / per_page) + 1
93
+ def records_total_count
94
+ get_raw_records.count(:all)
163
95
  end
164
96
 
165
- def per_page
166
- params.fetch(:length, 10).to_i
97
+ def records_filtered_count
98
+ filter_records(get_raw_records).count(:all)
167
99
  end
168
100
 
169
- def sort_column(item)
170
- new_sort_column(item)
171
- rescue
172
- ::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'
173
- deprecated_sort_column(item)
101
+ # Private helper methods
102
+ def load_orm_extension
103
+ case config.orm
104
+ when :mongoid then nil
105
+ when :active_record then extend ORM::ActiveRecord
106
+ else
107
+ nil
108
+ end
174
109
  end
175
110
 
176
- def deprecated_sort_column(item)
177
- sortable_columns[sortable_displayed_columns.index(item[:column])]
178
- end
111
+ def raw_records_error_text
112
+ return <<-eos
179
113
 
180
- def new_sort_column(item)
181
- model, column = sortable_columns[sortable_displayed_columns.index(item[:column])].split('.')
182
- col = [model.constantize.table_name, column].join('.')
114
+ You should implement this method in your class and specify
115
+ how records are going to be retrieved from the database.
116
+ eos
183
117
  end
184
118
 
185
- def sort_direction(item)
186
- options = %w(desc asc)
187
- options.include?(item[:dir]) ? item[:dir].upcase : 'ASC'
188
- end
119
+ def data_error_text
120
+ return <<-eos
189
121
 
190
- def sortable_displayed_columns
191
- @sortable_displayed_columns ||= generate_sortable_displayed_columns
122
+ You should implement this method in your class and return an array
123
+ of arrays, or an array of hashes, as defined in the jQuery.dataTables
124
+ plugin documentation.
125
+ eos
192
126
  end
193
127
 
194
- def generate_sortable_displayed_columns
195
- @sortable_displayed_columns = []
196
- params[:columns].each_value do |column|
197
- @sortable_displayed_columns << column[:data] if column[:orderable] == 'true'
198
- end
199
- @sortable_displayed_columns
200
- end
128
+ def view_columns_error_text
129
+ return <<-eos
201
130
 
202
- def load_paginator
203
- case config.paginator
204
- when :kaminari
205
- extend Extensions::Kaminari
206
- when :will_paginate
207
- extend Extensions::WillPaginate
208
- else
209
- extend Extensions::SimplePaginator
210
- end
211
- self
131
+ You should implement this method in your class and return an array
132
+ of database columns based on the columns displayed in the HTML view.
133
+ These columns should be represented in the ModelName.column_name,
134
+ or aliased_join_table.column_name notation.
135
+ eos
212
136
  end
213
137
  end
214
138
  end