ajax-datatables-rails 1.2.0 → 1.3.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +120 -0
  3. data/.rubocop.yml +3 -1
  4. data/Appraisals +7 -13
  5. data/CHANGELOG.md +23 -2
  6. data/README.md +51 -70
  7. data/ajax-datatables-rails.gemspec +11 -5
  8. data/doc/migrate.md +44 -0
  9. data/doc/webpack.md +4 -1
  10. data/gemfiles/{rails_5.2.3.gemfile → rails_5.2.4.gemfile} +1 -1
  11. data/gemfiles/{rails_6.0.1.gemfile → rails_6.0.3.gemfile} +1 -1
  12. data/gemfiles/{rails_5.0.7.gemfile → rails_6.1.0.gemfile} +3 -3
  13. data/lib/ajax-datatables-rails.rb +0 -16
  14. data/lib/ajax-datatables-rails/base.rb +38 -13
  15. data/lib/ajax-datatables-rails/datatable.rb +6 -0
  16. data/lib/ajax-datatables-rails/datatable/column.rb +59 -21
  17. data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +1 -1
  18. data/lib/ajax-datatables-rails/datatable/column/search.rb +2 -2
  19. data/lib/ajax-datatables-rails/datatable/datatable.rb +17 -3
  20. data/lib/ajax-datatables-rails/datatable/simple_order.rb +7 -5
  21. data/lib/ajax-datatables-rails/error.rb +9 -0
  22. data/lib/ajax-datatables-rails/orm.rb +6 -0
  23. data/lib/ajax-datatables-rails/orm/active_record.rb +9 -10
  24. data/lib/ajax-datatables-rails/version.rb +1 -1
  25. data/spec/ajax-datatables-rails/base_spec.rb +77 -120
  26. data/spec/ajax-datatables-rails/datatable/column_spec.rb +30 -10
  27. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +61 -22
  28. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +11 -9
  29. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +352 -257
  30. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +4 -4
  31. data/spec/install_oracle.sh +4 -4
  32. data/spec/spec_helper.rb +10 -19
  33. data/spec/support/datatables/complex_datatable.rb +29 -0
  34. data/spec/support/datatables/complex_datatable_array.rb +14 -0
  35. data/spec/support/{datatable_cond_date.rb → datatables/datatable_cond_date.rb} +0 -0
  36. data/spec/support/{datatable_cond_numeric.rb → datatables/datatable_cond_numeric.rb} +0 -0
  37. data/spec/support/{datatable_cond_proc.rb → datatables/datatable_cond_proc.rb} +0 -0
  38. data/spec/support/{datatable_cond_string.rb → datatables/datatable_cond_string.rb} +0 -0
  39. data/spec/support/datatables/datatable_cond_unknown.rb +5 -0
  40. data/spec/support/{datatable_order_nulls_last.rb → datatables/datatable_order_nulls_last.rb} +0 -0
  41. data/spec/support/{test_helpers.rb → helpers/params.rb} +8 -46
  42. data/spec/support/{test_models.rb → models/user.rb} +0 -0
  43. metadata +53 -49
  44. data/.travis.yml +0 -57
  45. data/gemfiles/rails_5.1.7.gemfile +0 -11
  46. data/lib/ajax-datatables-rails/configuration.rb +0 -10
  47. data/lib/generators/datatable/config_generator.rb +0 -19
  48. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +0 -9
  49. data/spec/ajax-datatables-rails/configuration_spec.rb +0 -34
  50. data/spec/ajax-datatables-rails/extended_spec.rb +0 -19
@@ -1,5 +1,35 @@
1
+ ## To migrate from `v1.0.x` to `v1.3.0`
2
+
3
+ The *v1.3.0* version has some breaking changes :
4
+
5
+ * `AjaxDatatablesRails.config.db_adapter=` is removed and is configured per datatable class now. It defaults to Rails DB adapter. (fixes [#364](https://github.com/jbox-web/ajax-datatables-rails/issues/364))
6
+
7
+ This change is transparent for everyone. Just remove `AjaxDatatablesRails.config.db_adapter=` from your configuration (if exists) and it should work fine.
8
+
9
+ Now you can use AjaxDatatablesRails in multi-db environments.
10
+
11
+ * `AjaxDatatablesRails.config.nulls_last=` is removed and is configured per datatable class now (or by column). It defaults to false.
12
+
13
+ This change is easy to mitigate : add `self.nulls_last = true` in [`ApplicationDatatable`](https://github.com/jbox-web/ajax-datatables-rails#create-a-master-parent-class-easy) and remove `AjaxDatatablesRails.config.nulls_last=`
14
+
15
+ ```ruby
16
+ class ApplicationDatatable < AjaxDatatablesRails::ActiveRecord
17
+ self.nulls_last = true
18
+ # puts commonly used methods here
19
+ end
20
+ ```
21
+
22
+ * `AjaxDatatablesRails.config` is removed with no replacement
23
+
24
+ Fix the two changes above and remove any configuration file about AjaxDatatablesRails. The gem is now configless :)
25
+
1
26
  ## To migrate from `v0.4.x` to `v1.0.0`
2
27
 
28
+ The *v1.0.0* version is a **major break** from *v0.4*.
29
+
30
+ * Datatables no longer inherits from `AjaxDatatablesRails::Base` but from `AjaxDatatablesRails::ActiveRecord` (this solves [#228](https://github.com/jbox-web/ajax-datatables-rails/issues/228))
31
+ * The `view_context` is no longer injected in Datatables but only the `params` hash (see the [example](#4-setup-the-controller-action)). This will break calls to helpers methods.
32
+
3
33
  1) To mitigate the first change (Datatables no longer inherits from `AjaxDatatablesRails::Base` but from `AjaxDatatablesRails::ActiveRecord`)
4
34
 
5
35
  Create a new `ApplicationDatatable` class and make all your classes inherits from it :
@@ -51,3 +81,17 @@ end
51
81
  This way, you can still use `def_delegators` in your datatables [as in the documentation](https://github.com/jbox-web/ajax-datatables-rails#using-view-helpers).
52
82
 
53
83
  Note that the recommanded way is to use [Draper gem](https://github.com/drapergem/draper) to separate filtering logic from view/presentation logic [as in the documentation](https://github.com/jbox-web/ajax-datatables-rails#using-view-decorators).
84
+
85
+ ## To migrate from `v0.3.x` to `v0.4.x`
86
+
87
+ The *v0.4* version is a **major break** from *v0.3*.
88
+
89
+ The core has been rewriten to remove dependency on [Kaminari](https://github.com/kaminari/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate).
90
+
91
+ It also brings a new (more natural) way of defining columns, based on hash definitions (and not arrays) and add some filtering options for column search.
92
+
93
+ To migrate on the v0.4 you'll need to :
94
+
95
+ * update your DataTables classes to remove all the `extend` directives
96
+ * switch to hash definitions of `view_columns`
97
+ * update your views to declare your columns bindings ([See here](https://github.com/jbox-web/ajax-datatables-rails#5-wire-up-the-javascript))
@@ -24,7 +24,10 @@ In `config/webpack/loaders/datatables.js` :
24
24
  ```js
25
25
  module.exports = {
26
26
  test: /datatables\.net.*/,
27
- loader: 'imports-loader?define=>false'
27
+ loader: 'imports-loader',
28
+ options: {
29
+ additionalCode: 'var define = false;'
30
+ }
28
31
  }
29
32
  ```
30
33
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "5.2.3"
5
+ gem "rails", "5.2.4"
6
6
  gem "activerecord-oracle_enhanced-adapter", "~> 5.2.0"
7
7
  gem "sqlite3", "~> 1.3.0"
8
8
  gem "mysql2"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "6.0.1"
5
+ gem "rails", "6.0.3"
6
6
  gem "activerecord-oracle_enhanced-adapter", "~> 6.0.0"
7
7
  gem "sqlite3", "~> 1.4.0"
8
8
  gem "mysql2"
@@ -2,9 +2,9 @@
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"
7
- gem "sqlite3", "~> 1.3.0"
5
+ gem "rails", "6.1.0"
6
+ gem "activerecord-oracle_enhanced-adapter", "~> 6.1.0"
7
+ gem "sqlite3", "~> 1.4.0"
8
8
  gem "mysql2"
9
9
  gem "ruby-oci8" if ENV["DB_ADAPTER"] == "oracle_enhanced"
10
10
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/configurable'
4
-
5
3
  require 'zeitwerk'
6
4
  loader = Zeitwerk::Loader.for_gem
7
5
  generators = "#{__dir__}/generators"
@@ -13,18 +11,4 @@ loader.inflector.inflect(
13
11
  loader.setup
14
12
 
15
13
  module AjaxDatatablesRails
16
- # Configure AjaxDatatablesRails global settings
17
- #
18
- # AjaxDatatablesRails.configure do |config|
19
- # config.db_adapter = :postgresql
20
- # end
21
-
22
- def self.configure
23
- yield @config ||= AjaxDatatablesRails::Configuration.new
24
- end
25
-
26
- # AjaxDatatablesRails global settings
27
- def self.config
28
- @config ||= AjaxDatatablesRails::Configuration.new
29
- end
30
14
  end
@@ -3,6 +3,9 @@
3
3
  module AjaxDatatablesRails
4
4
  class Base
5
5
 
6
+ class_attribute :db_adapter, default: ActiveRecord::Base.connection.adapter_name.downcase.to_sym
7
+ class_attribute :nulls_last, default: false
8
+
6
9
  attr_reader :params, :options, :datatable
7
10
 
8
11
  GLOBAL_SEARCH_DELIMITER = ' '
@@ -13,6 +16,7 @@ module AjaxDatatablesRails
13
16
  @datatable = Datatable::Datatable.new(self)
14
17
  end
15
18
 
19
+ # User defined methods
16
20
  def view_columns
17
21
  raise(NotImplementedError, view_columns_error_text)
18
22
  end
@@ -25,10 +29,29 @@ module AjaxDatatablesRails
25
29
  raise(NotImplementedError, data_error_text)
26
30
  end
27
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
28
50
  def additional_data
29
51
  {}
30
52
  end
31
53
 
54
+ # JSON structure sent to jQuery DataTables
32
55
  def as_json(*)
33
56
  {
34
57
  recordsTotal: records_total_count,
@@ -37,10 +60,18 @@ module AjaxDatatablesRails
37
60
  }.merge(additional_data)
38
61
  end
39
62
 
40
- def records
41
- @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')
42
71
  end
43
72
 
73
+ private
74
+
44
75
  # helper methods
45
76
  def connected_columns
46
77
  @connected_columns ||= begin
@@ -62,8 +93,6 @@ module AjaxDatatablesRails
62
93
  end
63
94
  end
64
95
 
65
- private
66
-
67
96
  def sanitize_data(data)
68
97
  data.map do |record|
69
98
  if record.is_a?(Array)
@@ -74,6 +103,11 @@ module AjaxDatatablesRails
74
103
  end
75
104
  end
76
105
 
106
+ # called from within #data
107
+ def records
108
+ @records ||= retrieve_records
109
+ end
110
+
77
111
  def retrieve_records
78
112
  records = fetch_records
79
113
  records = filter_records(records)
@@ -94,15 +128,6 @@ module AjaxDatatablesRails
94
128
  GLOBAL_SEARCH_DELIMITER
95
129
  end
96
130
 
97
- def column_id(name)
98
- view_columns.keys.index(name.to_sym)
99
- end
100
-
101
- def column_data(column)
102
- id = column_id(column)
103
- params.dig('columns', id.to_s, 'search', 'value')
104
- end
105
-
106
131
  def raw_records_error_text
107
132
  <<-ERROR
108
133
 
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ end
6
+ end
@@ -4,35 +4,23 @@ module AjaxDatatablesRails
4
4
  module Datatable
5
5
  class Column
6
6
 
7
- TYPE_CAST_DEFAULT = 'VARCHAR'
8
- TYPE_CAST_MYSQL = 'CHAR'
9
- TYPE_CAST_SQLITE = 'TEXT'
10
- TYPE_CAST_ORACLE = 'VARCHAR2(4000)'
11
- TYPE_CAST_SQLSERVER = 'VARCHAR(4000)'
12
-
13
- DB_ADAPTER_TYPE_CAST = {
14
- mysql: TYPE_CAST_MYSQL,
15
- mysql2: TYPE_CAST_MYSQL,
16
- sqlite: TYPE_CAST_SQLITE,
17
- sqlite3: TYPE_CAST_SQLITE,
18
- oracle: TYPE_CAST_ORACLE,
19
- oracleenhanced: TYPE_CAST_ORACLE,
20
- sqlserver: TYPE_CAST_SQLSERVER,
21
- }.freeze
22
-
23
- attr_reader :datatable, :index, :options
24
- attr_writer :search
25
-
26
7
  include Search
27
8
  include Order
28
9
  include DateFilter
29
10
 
11
+ attr_reader :datatable, :index, :options
12
+ attr_writer :search
30
13
 
31
14
  def initialize(datatable, index, options)
32
15
  @datatable = datatable
33
16
  @index = index
34
17
  @options = options
35
- @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
36
24
  end
37
25
 
38
26
  def data
@@ -71,14 +59,64 @@ module AjaxDatatablesRails
71
59
 
72
60
  private
73
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
+
74
85
  def type_cast
75
- @type_cast ||= DB_ADAPTER_TYPE_CAST.fetch(AjaxDatatablesRails.config.db_adapter, TYPE_CAST_DEFAULT)
86
+ @type_cast ||= DB_ADAPTER_TYPE_CAST.fetch(datatable.db_adapter, TYPE_CAST_DEFAULT)
76
87
  end
77
88
 
78
89
  def casted_column
79
90
  @casted_column ||= ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(type_cast)])
80
91
  end
81
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
+
82
120
  end
83
121
  end
84
122
  end
@@ -55,7 +55,7 @@ module AjaxDatatablesRails
55
55
  end
56
56
 
57
57
  def range_end_casted
58
- 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")
59
59
  end
60
60
 
61
61
  def parse_date(date)
@@ -61,8 +61,6 @@ module AjaxDatatablesRails
61
61
  filter
62
62
  when :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in
63
63
  searchable_integer? ? raw_search(cond) : empty_search
64
- when :null_value
65
- null_value_search
66
64
  when :start_with
67
65
  casted_column.matches("#{formatted_value}%")
68
66
  when :end_with
@@ -73,6 +71,8 @@ module AjaxDatatablesRails
73
71
  raw_search(:eq)
74
72
  when :string_in
75
73
  raw_search(:in)
74
+ when :null_value
75
+ null_value_search
76
76
  when :date_range
77
77
  date_range_search
78
78
  end
@@ -4,7 +4,7 @@ module AjaxDatatablesRails
4
4
  module Datatable
5
5
 
6
6
  class Datatable
7
- attr_reader :datatable, :options
7
+ attr_reader :options
8
8
 
9
9
  def initialize(datatable)
10
10
  @datatable = datatable
@@ -41,7 +41,7 @@ module AjaxDatatablesRails
41
41
 
42
42
  def columns
43
43
  @columns ||= get_param(:columns).map do |index, column_options|
44
- Column.new(datatable, index, column_options)
44
+ Column.new(@datatable, index, column_options)
45
45
  end
46
46
  end
47
47
 
@@ -70,7 +70,21 @@ module AjaxDatatablesRails
70
70
  def get_param(param)
71
71
  return {} if options[param].nil?
72
72
 
73
- options[param].to_unsafe_h.with_indifferent_access
73
+ if options[param].is_a? Array
74
+ hash = {}
75
+ options[param].each_with_index { |value, index| hash[index] = value }
76
+ hash
77
+ else
78
+ options[param].to_unsafe_h.with_indifferent_access
79
+ end
80
+ end
81
+
82
+ def db_adapter
83
+ @datatable.db_adapter
84
+ end
85
+
86
+ def nulls_last
87
+ @datatable.nulls_last
74
88
  end
75
89
 
76
90
  end
@@ -9,8 +9,10 @@ module AjaxDatatablesRails
9
9
  DIRECTIONS = [DIRECTION_ASC, DIRECTION_DESC].freeze
10
10
 
11
11
  def initialize(datatable, options = {})
12
- @datatable = datatable
13
- @options = options
12
+ @datatable = datatable
13
+ @options = options
14
+ @adapter = datatable.db_adapter
15
+ @nulls_last = datatable.nulls_last
14
16
  end
15
17
 
16
18
  def query(sort_column)
@@ -36,19 +38,19 @@ module AjaxDatatablesRails
36
38
  end
37
39
 
38
40
  def sort_nulls_last?
39
- column.nulls_last? || AjaxDatatablesRails.config.nulls_last == true
41
+ column.nulls_last? || @nulls_last == true
40
42
  end
41
43
 
42
44
  def nulls_last_sql
43
45
  return unless sort_nulls_last?
44
46
 
45
- case AjaxDatatablesRails.config.db_adapter
47
+ case @adapter
46
48
  when :pg, :postgresql, :postgres, :oracle
47
49
  'NULLS LAST'
48
50
  when :mysql, :mysql2, :sqlite, :sqlite3
49
51
  'IS NULL'
50
52
  else
51
- raise 'unsupported database adapter'
53
+ raise "unsupported database adapter: #{@adapter}"
52
54
  end
53
55
  end
54
56