ajax-datatables-rails 0.4.3 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +120 -0
  3. data/.rubocop.yml +17 -7
  4. data/Appraisals +15 -20
  5. data/CHANGELOG.md +54 -1
  6. data/Gemfile +0 -5
  7. data/Guardfile +16 -0
  8. data/README.md +238 -112
  9. data/Rakefile +1 -0
  10. data/ajax-datatables-rails.gemspec +24 -20
  11. data/bin/_guard-core +29 -0
  12. data/bin/appraisal +29 -0
  13. data/bin/bundle +114 -0
  14. data/bin/guard +29 -0
  15. data/bin/rake +29 -0
  16. data/bin/rspec +29 -0
  17. data/bin/rubocop +29 -0
  18. data/doc/migrate.md +97 -0
  19. data/doc/webpack.md +7 -2
  20. data/gemfiles/{rails_5.2.0.gemfile → rails_5.2.4.gemfile} +3 -5
  21. data/gemfiles/{rails_5.0.7.gemfile → rails_6.0.3.gemfile} +4 -6
  22. data/gemfiles/{rails_5.1.6.gemfile → rails_6.1.0.gemfile} +4 -6
  23. data/lib/ajax-datatables-rails.rb +12 -1
  24. data/lib/ajax-datatables-rails/active_record.rb +7 -0
  25. data/lib/ajax-datatables-rails/base.rb +47 -46
  26. data/lib/ajax-datatables-rails/datatable.rb +6 -0
  27. data/lib/ajax-datatables-rails/datatable/column.rb +65 -20
  28. data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +12 -21
  29. data/lib/ajax-datatables-rails/datatable/column/order.rb +1 -1
  30. data/lib/ajax-datatables-rails/datatable/column/search.rb +37 -22
  31. data/lib/ajax-datatables-rails/datatable/datatable.rb +16 -7
  32. data/lib/ajax-datatables-rails/datatable/simple_order.rb +23 -10
  33. data/lib/ajax-datatables-rails/datatable/simple_search.rb +2 -0
  34. data/lib/ajax-datatables-rails/error.rb +9 -0
  35. data/lib/ajax-datatables-rails/orm.rb +6 -0
  36. data/lib/ajax-datatables-rails/orm/active_record.rb +11 -12
  37. data/lib/ajax-datatables-rails/version.rb +13 -1
  38. data/lib/generators/rails/templates/datatable.rb +1 -1
  39. data/spec/ajax-datatables-rails/base_spec.rb +129 -93
  40. data/spec/ajax-datatables-rails/datatable/column_spec.rb +105 -37
  41. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +71 -31
  42. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +36 -14
  43. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +4 -2
  44. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +315 -272
  45. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +9 -8
  46. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +17 -14
  47. data/spec/factories/user.rb +3 -1
  48. data/spec/install_oracle.sh +9 -3
  49. data/spec/spec_helper.rb +33 -28
  50. data/spec/support/datatables/complex_datatable.rb +31 -0
  51. data/spec/support/datatables/complex_datatable_array.rb +16 -0
  52. data/spec/support/{datatable_cond_date.rb → datatables/datatable_cond_date.rb} +2 -0
  53. data/spec/support/{datatable_cond_numeric.rb → datatables/datatable_cond_numeric.rb} +3 -1
  54. data/spec/support/{datatable_cond_proc.rb → datatables/datatable_cond_proc.rb} +2 -0
  55. data/spec/support/{datatable_cond_string.rb → datatables/datatable_cond_string.rb} +9 -1
  56. data/spec/support/datatables/datatable_cond_unknown.rb +7 -0
  57. data/spec/support/{datatable_order_nulls_last.rb → datatables/datatable_order_nulls_last.rb} +2 -0
  58. data/spec/support/{test_helpers.rb → helpers/params.rb} +17 -42
  59. data/spec/support/{test_models.rb → models/user.rb} +2 -0
  60. data/spec/support/schema.rb +3 -1
  61. metadata +76 -75
  62. data/.travis.yml +0 -80
  63. data/gemfiles/rails_4.0.13.gemfile +0 -14
  64. data/gemfiles/rails_4.1.16.gemfile +0 -14
  65. data/gemfiles/rails_4.2.10.gemfile +0 -14
  66. data/lib/ajax-datatables-rails/config.rb +0 -31
  67. data/lib/ajax_datatables_rails.rb +0 -15
  68. data/lib/generators/datatable/config_generator.rb +0 -19
  69. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +0 -12
  70. data/spec/ajax-datatables-rails/configuration_spec.rb +0 -43
  71. data/spec/ajax-datatables-rails/extended_spec.rb +0 -20
  72. data/spec/ajax-datatables-rails/orm/active_record_spec.rb +0 -25
data/doc/webpack.md CHANGED
@@ -5,7 +5,9 @@ We assume here that Bootstrap and FontAwesome are already installed with Webpack
5
5
  Inspired by https://datatables.net/download and completed :
6
6
 
7
7
  Add npm packages :
8
-
8
+ ```sh
9
+ $ yarn add imports-loader
10
+ ```
9
11
  ```sh
10
12
  $ yarn add datatables.net
11
13
  $ yarn add datatables.net-bs
@@ -22,7 +24,10 @@ In `config/webpack/loaders/datatables.js` :
22
24
  ```js
23
25
  module.exports = {
24
26
  test: /datatables\.net.*/,
25
- loader: 'imports-loader?define=>false'
27
+ loader: 'imports-loader',
28
+ options: {
29
+ additionalCode: 'var define = false;'
30
+ }
26
31
  }
27
32
  ```
28
33
 
@@ -2,12 +2,10 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "5.2.0"
5
+ gem "rails", "5.2.4"
6
6
  gem "activerecord-oracle_enhanced-adapter", "~> 5.2.0"
7
+ gem "sqlite3", "~> 1.3.0"
8
+ gem "mysql2"
7
9
  gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
10
 
9
- group :test do
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
11
  gemspec path: "../"
@@ -2,12 +2,10 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "5.0.7"
6
- gem "activerecord-oracle_enhanced-adapter", "~> 1.7.0"
5
+ gem "rails", "6.0.3"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 6.0.0"
7
+ gem "sqlite3", "~> 1.4.0"
8
+ gem "mysql2"
7
9
  gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
10
 
9
- group :test do
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
11
  gemspec path: "../"
@@ -2,12 +2,10 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "5.1.6"
6
- gem "activerecord-oracle_enhanced-adapter", "~> 1.8.0"
5
+ gem "rails", "6.1.0"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 6.1.0"
7
+ gem "sqlite3", "~> 1.4.0"
8
+ gem "mysql2"
7
9
  gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
8
10
 
9
- group :test do
10
- gem "codeclimate-test-reporter", "~> 1.0.0"
11
- end
12
-
13
11
  gemspec path: "../"
@@ -1,3 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ajax_datatables_rails'
3
+ require 'zeitwerk'
4
+ loader = Zeitwerk::Loader.for_gem
5
+ generators = "#{__dir__}/generators"
6
+ loader.ignore(generators)
7
+ loader.inflector.inflect(
8
+ 'orm' => 'ORM',
9
+ 'ajax-datatables-rails' => 'AjaxDatatablesRails'
10
+ )
11
+ loader.setup
12
+
13
+ module AjaxDatatablesRails
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ class ActiveRecord < AjaxDatatablesRails::Base
5
+ include AjaxDatatablesRails::ORM::ActiveRecord
6
+ end
7
+ end
@@ -2,23 +2,21 @@
2
2
 
3
3
  module AjaxDatatablesRails
4
4
  class Base
5
- extend Forwardable
6
5
 
7
- attr_reader :view, :options
8
- def_delegator :@view, :params
6
+ class_attribute :db_adapter, default: ::ActiveRecord::Base.connection.adapter_name.downcase.to_sym
7
+ class_attribute :nulls_last, default: false
9
8
 
10
- GLOBAL_SEARCH_DELIMITER = ' '
9
+ attr_reader :params, :options, :datatable
11
10
 
12
- def initialize(view, options = {})
13
- @view = view
14
- @options = options
15
- load_orm_extension
16
- end
11
+ GLOBAL_SEARCH_DELIMITER = ' '
17
12
 
18
- def datatable
19
- @datatable ||= Datatable::Datatable.new(self)
13
+ def initialize(params, options = {})
14
+ @params = params
15
+ @options = options
16
+ @datatable = Datatable::Datatable.new(self)
20
17
  end
21
18
 
19
+ # User defined methods
22
20
  def view_columns
23
21
  raise(NotImplementedError, view_columns_error_text)
24
22
  end
@@ -31,22 +29,49 @@ module AjaxDatatablesRails
31
29
  raise(NotImplementedError, data_error_text)
32
30
  end
33
31
 
32
+ # ORM defined methods
33
+ def fetch_records
34
+ get_raw_records
35
+ end
36
+
37
+ def filter_records(records)
38
+ raise(NotImplementedError)
39
+ end
40
+
41
+ def sort_records(records)
42
+ raise(NotImplementedError)
43
+ end
44
+
45
+ def paginate_records(records)
46
+ raise(NotImplementedError)
47
+ end
48
+
49
+ # User overides
34
50
  def additional_data
35
51
  {}
36
52
  end
37
53
 
54
+ # JSON structure sent to jQuery DataTables
38
55
  def as_json(*)
39
56
  {
40
- recordsTotal: records_total_count,
57
+ recordsTotal: records_total_count,
41
58
  recordsFiltered: records_filtered_count,
42
- data: sanitize(data)
43
- }.merge(get_additional_data)
59
+ data: sanitize_data(data),
60
+ }.merge(additional_data)
44
61
  end
45
62
 
46
- def records
47
- @records ||= retrieve_records
63
+ # User helper methods
64
+ def column_id(name)
65
+ view_columns.keys.index(name.to_sym)
66
+ end
67
+
68
+ def column_data(column)
69
+ id = column_id(column)
70
+ params.dig('columns', id.to_s, 'search', 'value')
48
71
  end
49
72
 
73
+ private
74
+
50
75
  # helper methods
51
76
  def connected_columns
52
77
  @connected_columns ||= begin
@@ -68,26 +93,7 @@ module AjaxDatatablesRails
68
93
  end
69
94
  end
70
95
 
71
- private
72
-
73
- # This method is necessary for smooth transition from
74
- # `additinonal_datas` method to `additional_data`
75
- # without breaking change.
76
- def get_additional_data
77
- if respond_to?(:additional_datas)
78
- puts <<-WARNING
79
- `additional_datas` has been deprecated and
80
- will be removed in next major version update!
81
- Please use `additional_data` instead.
82
- WARNING
83
-
84
- additional_datas
85
- else
86
- additional_data
87
- end
88
- end
89
-
90
- def sanitize(data)
96
+ def sanitize_data(data)
91
97
  data.map do |record|
92
98
  if record.is_a?(Array)
93
99
  record.map { |td| ERB::Util.html_escape(td) }
@@ -97,6 +103,11 @@ module AjaxDatatablesRails
97
103
  end
98
104
  end
99
105
 
106
+ # called from within #data
107
+ def records
108
+ @records ||= retrieve_records
109
+ end
110
+
100
111
  def retrieve_records
101
112
  records = fetch_records
102
113
  records = filter_records(records)
@@ -113,16 +124,6 @@ module AjaxDatatablesRails
113
124
  filter_records(fetch_records).count(:all)
114
125
  end
115
126
 
116
- # Private helper methods
117
- def load_orm_extension
118
- case AjaxDatatablesRails.config.orm
119
- when :active_record
120
- extend ORM::ActiveRecord
121
- when :mongoid
122
- nil
123
- end
124
- end
125
-
126
127
  def global_search_delimiter
127
128
  GLOBAL_SEARCH_DELIMITER
128
129
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ end
6
+ end
@@ -4,28 +4,23 @@ module AjaxDatatablesRails
4
4
  module Datatable
5
5
  class Column
6
6
 
7
- DB_ADAPTER_TYPE_CAST = {
8
- mysql: 'CHAR',
9
- mysql2: 'CHAR',
10
- sqlite: 'TEXT',
11
- sqlite3: 'TEXT',
12
- oracle: 'VARCHAR2(4000)',
13
- oracleenhanced: 'VARCHAR2(4000)'
14
- }.freeze
15
-
16
- attr_reader :datatable, :index, :options
17
- attr_writer :search
18
-
19
7
  include Search
20
8
  include Order
21
- prepend DateFilter unless AjaxDatatablesRails.old_rails?
9
+ include DateFilter
22
10
 
11
+ attr_reader :datatable, :index, :options
12
+ attr_writer :search
23
13
 
24
14
  def initialize(datatable, index, options)
25
15
  @datatable = datatable
26
16
  @index = index
27
17
  @options = options
28
- @view_column = datatable.view_columns[options[:data].to_sym]
18
+ @view_column = datatable.view_columns[column_name]
19
+ validate_settings!
20
+ end
21
+
22
+ def column_name
23
+ @column_name ||= options[:data]&.to_sym
29
24
  end
30
25
 
31
26
  def data
@@ -52,26 +47,76 @@ module AjaxDatatablesRails
52
47
  !source.include?('.')
53
48
  end
54
49
 
55
- # Add formater option to allow modification of the value
50
+ # Add formatter option to allow modification of the value
56
51
  # before passing it to the database
57
- def formater
58
- @view_column[:formater]
52
+ def formatter
53
+ @view_column[:formatter]
59
54
  end
60
55
 
61
- def formated_value
62
- formater ? formater.call(search.value) : search.value
56
+ def formatted_value
57
+ formatter ? formatter.call(search.value) : search.value
63
58
  end
64
59
 
65
60
  private
66
61
 
62
+ TYPE_CAST_DEFAULT = 'VARCHAR'
63
+ TYPE_CAST_MYSQL = 'CHAR'
64
+ TYPE_CAST_SQLITE = 'TEXT'
65
+ TYPE_CAST_ORACLE = 'VARCHAR2(4000)'
66
+ TYPE_CAST_SQLSERVER = 'VARCHAR(4000)'
67
+
68
+ DB_ADAPTER_TYPE_CAST = {
69
+ mysql: TYPE_CAST_MYSQL,
70
+ mysql2: TYPE_CAST_MYSQL,
71
+ sqlite: TYPE_CAST_SQLITE,
72
+ sqlite3: TYPE_CAST_SQLITE,
73
+ oracle: TYPE_CAST_ORACLE,
74
+ oracleenhanced: TYPE_CAST_ORACLE,
75
+ sqlserver: TYPE_CAST_SQLSERVER,
76
+ }.freeze
77
+
78
+ private_constant :TYPE_CAST_DEFAULT
79
+ private_constant :TYPE_CAST_MYSQL
80
+ private_constant :TYPE_CAST_SQLITE
81
+ private_constant :TYPE_CAST_ORACLE
82
+ private_constant :TYPE_CAST_SQLSERVER
83
+ private_constant :DB_ADAPTER_TYPE_CAST
84
+
67
85
  def type_cast
68
- @type_cast ||= (DB_ADAPTER_TYPE_CAST[AjaxDatatablesRails.config.db_adapter] || 'VARCHAR')
86
+ @type_cast ||= DB_ADAPTER_TYPE_CAST.fetch(datatable.db_adapter, TYPE_CAST_DEFAULT)
69
87
  end
70
88
 
71
89
  def casted_column
72
90
  @casted_column ||= ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(type_cast)])
73
91
  end
74
92
 
93
+ def validate_settings!
94
+ raise AjaxDatatablesRails::Error::InvalidSearchColumn, "Unknown column. Check that `data` field is filled on JS side with the column name" if column_name.empty?
95
+ raise AjaxDatatablesRails::Error::InvalidSearchColumn, "Check that column '#{column_name}' exists in view_columns" unless valid_search_column?(column_name)
96
+ raise AjaxDatatablesRails::Error::InvalidSearchCondition, cond unless valid_search_condition?(cond)
97
+ end
98
+
99
+ def valid_search_column?(column_name)
100
+ !datatable.view_columns[column_name].nil?
101
+ end
102
+
103
+ VALID_SEARCH_CONDITIONS = [
104
+ # String condition
105
+ :start_with, :end_with, :like, :string_eq, :string_in, :null_value,
106
+ # Numeric condition
107
+ :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in,
108
+ # Date condition
109
+ :date_range
110
+ ].freeze
111
+
112
+ private_constant :VALID_SEARCH_CONDITIONS
113
+
114
+ def valid_search_condition?(cond)
115
+ return true if cond.is_a?(Proc)
116
+
117
+ VALID_SEARCH_CONDITIONS.include?(cond)
118
+ end
119
+
75
120
  end
76
121
  end
77
122
  end
@@ -5,6 +5,8 @@ module AjaxDatatablesRails
5
5
  class Column
6
6
  module DateFilter
7
7
 
8
+ RANGE_DELIMITER = '-'
9
+
8
10
  class DateRange
9
11
  attr_reader :begin, :end
10
12
 
@@ -20,55 +22,44 @@ module AjaxDatatablesRails
20
22
 
21
23
  # Add delimiter option to handle range search
22
24
  def delimiter
23
- @view_column[:delimiter] || '-'
24
- end
25
-
26
- def empty_range_search?
27
- (formated_value == delimiter) || (range_start.blank? && range_end.blank?)
25
+ @delimiter ||= @view_column.fetch(:delimiter, RANGE_DELIMITER)
28
26
  end
29
27
 
30
28
  # A range value is in form '<range_start><delimiter><range_end>'
31
29
  # This returns <range_start>
32
30
  def range_start
33
- @range_start ||= formated_value.split(delimiter)[0]
31
+ @range_start ||= formatted_value.split(delimiter)[0]
34
32
  end
35
33
 
36
34
  # A range value is in form '<range_start><delimiter><range_end>'
37
35
  # This returns <range_end>
38
36
  def range_end
39
- @range_end ||= formated_value.split(delimiter)[1]
37
+ @range_end ||= formatted_value.split(delimiter)[1]
38
+ end
39
+
40
+ def empty_range_search?
41
+ (formatted_value == delimiter) || (range_start.blank? && range_end.blank?)
40
42
  end
41
43
 
42
44
  # Do a range search
43
45
  def date_range_search
44
46
  return nil if empty_range_search?
47
+
45
48
  table[field].between(DateRange.new(range_start_casted, range_end_casted))
46
49
  end
47
50
 
48
51
  private
49
52
 
50
- def non_regex_search
51
- if cond == :date_range
52
- date_range_search
53
- else
54
- super
55
- end
56
- end
57
-
58
53
  def range_start_casted
59
54
  range_start.blank? ? parse_date('01/01/1970') : parse_date(range_start)
60
55
  end
61
56
 
62
57
  def range_end_casted
63
- range_end.blank? ? Time.current : parse_date("#{range_end} 23:59:59")
58
+ range_end.blank? ? parse_date('9999-12-31 23:59:59') : parse_date("#{range_end} 23:59:59")
64
59
  end
65
60
 
66
61
  def parse_date(date)
67
- if Time.zone
68
- Time.zone.parse(date)
69
- else
70
- Time.parse(date)
71
- end
62
+ Time.zone ? Time.zone.parse(date) : Time.parse(date)
72
63
  end
73
64
 
74
65
  end