ajax-datatables-rails 0.4.0 → 0.4.1

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 (53) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +23 -1132
  3. data/.travis.yml +36 -24
  4. data/Appraisals +14 -8
  5. data/CHANGELOG.md +32 -7
  6. data/Gemfile +2 -0
  7. data/README.md +400 -335
  8. data/Rakefile +2 -1
  9. data/ajax-datatables-rails.gemspec +8 -6
  10. data/doc/webpack.md +50 -0
  11. data/gemfiles/{rails_4.1.15.gemfile → rails_4.1.16.gemfile} +1 -1
  12. data/gemfiles/{rails_4.2.8.gemfile → rails_4.2.10.gemfile} +2 -1
  13. data/gemfiles/{rails_5.0.3.gemfile → rails_5.0.7.gemfile} +1 -1
  14. data/gemfiles/{rails_5.1.1.gemfile → rails_5.1.6.gemfile} +1 -1
  15. data/gemfiles/rails_5.2.0.gemfile +13 -0
  16. data/lib/ajax-datatables-rails.rb +3 -11
  17. data/lib/ajax-datatables-rails/base.rb +44 -24
  18. data/lib/ajax-datatables-rails/config.rb +3 -0
  19. data/lib/ajax-datatables-rails/datatable/column.rb +33 -125
  20. data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +77 -0
  21. data/lib/ajax-datatables-rails/datatable/column/order.rb +29 -0
  22. data/lib/ajax-datatables-rails/datatable/column/search.rb +88 -0
  23. data/lib/ajax-datatables-rails/datatable/datatable.rb +10 -7
  24. data/lib/ajax-datatables-rails/datatable/simple_order.rb +17 -2
  25. data/lib/ajax-datatables-rails/datatable/simple_search.rb +3 -0
  26. data/lib/ajax-datatables-rails/orm/active_record.rb +14 -5
  27. data/lib/ajax-datatables-rails/version.rb +3 -1
  28. data/lib/ajax_datatables_rails.rb +15 -0
  29. data/lib/generators/datatable/config_generator.rb +2 -0
  30. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +5 -0
  31. data/lib/generators/rails/datatable_generator.rb +6 -5
  32. data/lib/generators/rails/templates/datatable.rb +1 -15
  33. data/spec/ajax-datatables-rails/base_spec.rb +23 -26
  34. data/spec/ajax-datatables-rails/datatable/column_spec.rb +68 -23
  35. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +1 -1
  36. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +29 -2
  37. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +1 -1
  38. data/spec/ajax-datatables-rails/extended_spec.rb +3 -3
  39. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +94 -35
  40. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +6 -6
  41. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +43 -0
  42. data/spec/ajax-datatables-rails/orm/active_record_spec.rb +1 -1
  43. data/spec/factories/user.rb +1 -1
  44. data/spec/install_oracle.sh +2 -2
  45. data/spec/spec_helper.rb +8 -3
  46. data/spec/support/datatable_cond_date.rb +5 -0
  47. data/spec/support/datatable_cond_numeric.rb +41 -0
  48. data/spec/support/datatable_cond_proc.rb +11 -0
  49. data/spec/support/datatable_cond_string.rb +23 -0
  50. data/spec/support/datatable_order_nulls_last.rb +5 -0
  51. data/spec/support/test_helpers.rb +13 -88
  52. metadata +28 -13
  53. data/lib/ajax-datatables-rails/datatable/column_date_filter.rb +0 -41
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ class Column
6
+ module DateFilter
7
+
8
+ class DateRange
9
+ attr_reader :begin, :end
10
+
11
+ def initialize(date_start, date_end)
12
+ @begin = date_start
13
+ @end = date_end
14
+ end
15
+
16
+ def exclude_end?
17
+ false
18
+ end
19
+ end
20
+
21
+ # Add delimiter option to handle range search
22
+ def delimiter
23
+ @view_column[:delimiter] || '-'
24
+ end
25
+
26
+ def empty_range_search?
27
+ (formated_value == delimiter) || (range_start.blank? && range_end.blank?)
28
+ end
29
+
30
+ # A range value is in form '<range_start><delimiter><range_end>'
31
+ # This returns <range_start>
32
+ def range_start
33
+ @range_start ||= formated_value.split(delimiter)[0]
34
+ end
35
+
36
+ # A range value is in form '<range_start><delimiter><range_end>'
37
+ # This returns <range_end>
38
+ def range_end
39
+ @range_end ||= formated_value.split(delimiter)[1]
40
+ end
41
+
42
+ # Do a range search
43
+ def date_range_search
44
+ return nil if empty_range_search?
45
+ table[field].between(DateRange.new(range_start_casted, range_end_casted))
46
+ end
47
+
48
+ private
49
+
50
+ def non_regex_search
51
+ if cond == :date_range
52
+ date_range_search
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def range_start_casted
59
+ range_start.blank? ? parse_date('01/01/1970') : parse_date(range_start)
60
+ end
61
+
62
+ def range_end_casted
63
+ range_end.blank? ? Time.current : parse_date("#{range_end} 23:59:59")
64
+ end
65
+
66
+ def parse_date(date)
67
+ if Time.zone
68
+ Time.zone.parse(date)
69
+ else
70
+ Time.parse(date)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ class Column
6
+ module Order
7
+
8
+ def orderable?
9
+ @view_column.fetch(:orderable, true)
10
+ end
11
+
12
+ # Add sort_field option to allow overriding of sort field
13
+ def sort_field
14
+ @view_column[:sort_field] || field
15
+ end
16
+
17
+ def sort_query
18
+ custom_field? ? source : "#{table.name}.#{sort_field}"
19
+ end
20
+
21
+ # Add option to sort null values last
22
+ def nulls_last?
23
+ @view_column.fetch(:nulls_last, false)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ class Column
6
+ module Search
7
+
8
+ def searchable?
9
+ @view_column.fetch(:searchable, true)
10
+ end
11
+
12
+ def cond
13
+ @view_column[:cond] || :like
14
+ end
15
+
16
+ def filter
17
+ @view_column[:cond].call(self, formated_value)
18
+ end
19
+
20
+ def search
21
+ @search ||= SimpleSearch.new(options[:search])
22
+ end
23
+
24
+ def searched?
25
+ search.value.present?
26
+ end
27
+
28
+ def search_query
29
+ search.regexp? ? regex_search : non_regex_search
30
+ end
31
+
32
+ # Add use_regex option to allow bypassing of regex search
33
+ def use_regex?
34
+ @view_column.fetch(:use_regex, true)
35
+ end
36
+
37
+ private
38
+
39
+ # Using multi-select filters in JQuery Datatable auto-enables regex_search.
40
+ # Unfortunately regex_search doesn't work when filtering on primary keys with integer.
41
+ # It generates this kind of query : AND ("regions"."id" ~ '2|3') which throws an error :
42
+ # operator doesn't exist : integer ~ unknown
43
+ # The solution is to bypass regex_search and use non_regex_search with :in operator
44
+ def regex_search
45
+ if use_regex?
46
+ ::Arel::Nodes::Regexp.new((custom_field? ? field : table[field]), ::Arel::Nodes.build_quoted(formated_value))
47
+ else
48
+ non_regex_search
49
+ end
50
+ end
51
+
52
+ def non_regex_search
53
+ case cond
54
+ when Proc
55
+ filter
56
+ when :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in
57
+ numeric_search
58
+ when :null_value
59
+ null_value_search
60
+ when :start_with
61
+ casted_column.matches("#{formated_value}%")
62
+ when :end_with
63
+ casted_column.matches("%#{formated_value}")
64
+ when :like
65
+ casted_column.matches("%#{formated_value}%")
66
+ end
67
+ end
68
+
69
+ def null_value_search
70
+ if formated_value == '!NULL'
71
+ table[field].not_eq(nil)
72
+ else
73
+ table[field].eq(nil)
74
+ end
75
+ end
76
+
77
+ def numeric_search
78
+ if custom_field?
79
+ ::Arel::Nodes::SqlLiteral.new(field).eq(formated_value)
80
+ else
81
+ table[field].send(cond, formated_value)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AjaxDatatablesRails
2
4
  module Datatable
3
5
 
4
- TRUE_VALUE = 'true'.freeze
6
+ TRUE_VALUE = 'true'
5
7
 
6
8
  class Datatable
7
9
  attr_reader :datatable, :options
@@ -55,16 +57,16 @@ module AjaxDatatablesRails
55
57
  per_page != -1
56
58
  end
57
59
 
58
- def offset
59
- (page - 1) * per_page
60
+ def per_page
61
+ options.fetch(:length, 10).to_i
60
62
  end
61
63
 
62
- def page
63
- (options[:start].to_i / per_page) + 1
64
+ def offset
65
+ options.fetch(:start, 0).to_i
64
66
  end
65
67
 
66
- def per_page
67
- options.fetch(:length, 10).to_i
68
+ def page
69
+ (offset / per_page) + 1
68
70
  end
69
71
 
70
72
  def get_param(param)
@@ -74,6 +76,7 @@ module AjaxDatatablesRails
74
76
  options[param].to_unsafe_h.with_indifferent_access
75
77
  end
76
78
  end
79
+
77
80
  end
78
81
  end
79
82
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AjaxDatatablesRails
2
4
  module Datatable
3
5
  class SimpleOrder
@@ -10,7 +12,11 @@ module AjaxDatatablesRails
10
12
  end
11
13
 
12
14
  def query(sort_column)
13
- "#{sort_column} #{direction}"
15
+ if sort_nulls_last?
16
+ "CASE WHEN #{sort_column} IS NULL THEN 1 ELSE 0 END, #{sort_column} #{direction}"
17
+ else
18
+ "#{sort_column} #{direction}"
19
+ end
14
20
  end
15
21
 
16
22
  def column
@@ -18,7 +24,7 @@ module AjaxDatatablesRails
18
24
  end
19
25
 
20
26
  def direction
21
- DIRECTIONS.find { |dir| dir == @options[:dir].upcase } || 'ASC'
27
+ DIRECTIONS.find { |dir| dir == column_direction } || 'ASC'
22
28
  end
23
29
 
24
30
  private
@@ -26,6 +32,15 @@ module AjaxDatatablesRails
26
32
  def column_index
27
33
  @options[:column]
28
34
  end
35
+
36
+ def column_direction
37
+ @options[:dir].upcase
38
+ end
39
+
40
+ def sort_nulls_last?
41
+ column.nulls_last? || AjaxDatatablesRails.config.nulls_last == true
42
+ end
43
+
29
44
  end
30
45
  end
31
46
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AjaxDatatablesRails
2
4
  module Datatable
3
5
  class SimpleSearch
@@ -13,6 +15,7 @@ module AjaxDatatablesRails
13
15
  def regexp?
14
16
  @options[:regex] == TRUE_VALUE
15
17
  end
18
+
16
19
  end
17
20
  end
18
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AjaxDatatablesRails
2
4
  module ORM
3
5
  module ActiveRecord
@@ -10,13 +12,16 @@ module AjaxDatatablesRails
10
12
  records.where(build_conditions)
11
13
  end
12
14
 
15
+ # rubocop:disable Style/EachWithObject
13
16
  def sort_records(records)
14
17
  sort_by = datatable.orders.inject([]) do |queries, order|
15
18
  column = order.column
16
- queries << order.query(column.sort_query) if column
19
+ queries << order.query(column.sort_query) if column && column.orderable?
20
+ queries
17
21
  end
18
- records.order(sort_by.join(", "))
22
+ records.order(Arel.sql(sort_by.join(', ')))
19
23
  end
24
+ # rubocop:enable Style/EachWithObject
20
25
 
21
26
  def paginate_records(records)
22
27
  records.offset(datatable.offset).limit(datatable.per_page)
@@ -33,20 +38,24 @@ module AjaxDatatablesRails
33
38
  end
34
39
 
35
40
  def build_conditions_for_datatable
36
- search_for = datatable.search.value.split(' ')
37
41
  criteria = search_for.inject([]) do |crit, atom|
38
- search = Datatable::SimpleSearch.new({ value: atom, regex: datatable.search.regexp? })
42
+ search = Datatable::SimpleSearch.new(value: atom, regex: datatable.search.regexp?)
39
43
  crit << searchable_columns.map do |simple_column|
40
44
  simple_column.search = search
41
45
  simple_column.search_query
42
46
  end.reduce(:or)
43
- end.reduce(:and)
47
+ end.compact.reduce(:and)
44
48
  criteria
45
49
  end
46
50
 
47
51
  def build_conditions_for_selected_columns
48
52
  search_columns.map(&:search_query).compact.reduce(:and)
49
53
  end
54
+
55
+ def search_for
56
+ datatable.search.value.split(global_search_delimiter)
57
+ end
58
+
50
59
  end
51
60
  end
52
61
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AjaxDatatablesRails
2
- VERSION = '0.4.0'.freeze
4
+ VERSION = '0.4.1'
3
5
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AjaxDatatablesRails
4
+ require 'ajax-datatables-rails/version'
5
+ require 'ajax-datatables-rails/config'
6
+ require 'ajax-datatables-rails/base'
7
+ require 'ajax-datatables-rails/datatable/datatable'
8
+ require 'ajax-datatables-rails/datatable/simple_search'
9
+ require 'ajax-datatables-rails/datatable/simple_order'
10
+ require 'ajax-datatables-rails/datatable/column/search'
11
+ require 'ajax-datatables-rails/datatable/column/order'
12
+ require 'ajax-datatables-rails/datatable/column/date_filter' unless AjaxDatatablesRails.old_rails?
13
+ require 'ajax-datatables-rails/datatable/column'
14
+ require 'ajax-datatables-rails/orm/active_record'
15
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/generators'
2
4
 
3
5
  module Datatable
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  AjaxDatatablesRails.configure do |config|
2
4
  # available options for db_adapter are: :pg, :mysql, :mysql2, :sqlite, :sqlite3
3
5
  # config.db_adapter = :pg
4
6
 
7
+ # Or you can use your rails environment adapter if you want a generic dev and production
8
+ # config.db_adapter = Rails.configuration.database_configuration[Rails.env]['adapter'].to_sym
9
+
5
10
  # available options for orm are: :active_record, :mongoid
6
11
  # config.orm = :active_record
7
12
  end
@@ -1,16 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/generators'
2
4
 
3
5
  module Rails
4
6
  module Generators
5
7
  class DatatableGenerator < ::Rails::Generators::Base
6
8
  desc 'Creates a *_datatable model in the app/datatables directory.'
7
- source_root File.expand_path('../templates', __FILE__)
9
+ source_root File.expand_path('templates', __dir__)
8
10
  argument :name, type: :string
9
11
 
10
12
  def generate_datatable
11
- template 'datatable.rb', File.join(
12
- 'app/datatables', "#{datatable_path}.rb"
13
- )
13
+ template 'datatable.rb', File.join('app', 'datatables', "#{datatable_path}.rb")
14
14
  end
15
15
 
16
16
  def datatable_name
@@ -18,10 +18,11 @@ module Rails
18
18
  end
19
19
 
20
20
  private
21
+
21
22
  def datatable_path
22
23
  "#{name.underscore}_datatable"
23
24
  end
24
25
 
25
26
  end
26
27
  end
27
- end
28
+ end
@@ -19,23 +19,9 @@ class <%= datatable_name %> < AjaxDatatablesRails::Base
19
19
  end
20
20
  end
21
21
 
22
- private
23
-
24
22
  def get_raw_records
25
23
  # insert query here
24
+ # User.all
26
25
  end
27
26
 
28
- # ==== These methods represent the basic operations to perform on records
29
- # and feel free to override them
30
-
31
- # def filter_records(records)
32
- # end
33
-
34
- # def sort_records(records)
35
- # end
36
-
37
- # def paginate_records(records)
38
- # end
39
-
40
- # ==== Insert 'presenter'-like methods below if necessary
41
27
  end