ajax-datatables-rails 0.3.1 → 0.4.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 (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
@@ -4,9 +4,9 @@ module AjaxDatatablesRails
4
4
 
5
5
  # configure AjaxDatatablesRails global settings
6
6
  # AjaxDatatablesRails.configure do |config|
7
- # config.db_adapter = :pg
7
+ # config.db_adapter = :postgresql
8
8
  # end
9
- def self.configure &block
9
+ def self.configure
10
10
  yield @config ||= AjaxDatatablesRails::Configuration.new
11
11
  end
12
12
 
@@ -15,11 +15,14 @@ module AjaxDatatablesRails
15
15
  @config ||= AjaxDatatablesRails::Configuration.new
16
16
  end
17
17
 
18
+ def self.old_rails?
19
+ Rails::VERSION::MAJOR == 4 && (Rails::VERSION::MINOR == 1 || Rails::VERSION::MINOR == 0)
20
+ end
21
+
18
22
  class Configuration
19
23
  include ActiveSupport::Configurable
20
24
 
21
- # default db_adapter is pg (postgresql)
22
- config_accessor(:db_adapter) { :pg }
23
- config_accessor(:paginator) { :simple_paginator }
25
+ config_accessor(:orm) { :active_record }
26
+ config_accessor(:db_adapter) { :postgresql }
24
27
  end
25
28
  end
@@ -0,0 +1,169 @@
1
+ require 'ostruct'
2
+
3
+ module AjaxDatatablesRails
4
+ module Datatable
5
+ class Column
6
+ attr_reader :datatable, :index, :options
7
+
8
+ unless AjaxDatatablesRails.old_rails?
9
+ prepend ColumnDateFilter
10
+ end
11
+
12
+ def initialize(datatable, index, options)
13
+ @datatable, @index, @options = datatable, index, options
14
+ @view_column = datatable.view_columns[options["data"].to_sym]
15
+ end
16
+
17
+ def data
18
+ options[:data].presence || options[:name]
19
+ end
20
+
21
+ def searchable?
22
+ @view_column.fetch(:searchable, true)
23
+ end
24
+
25
+ def orderable?
26
+ @view_column.fetch(:orderable, true)
27
+ end
28
+
29
+ def search
30
+ @search ||= SimpleSearch.new(options[:search])
31
+ end
32
+
33
+ def searched?
34
+ search.value.present?
35
+ end
36
+
37
+ def search=(value)
38
+ @search = value
39
+ end
40
+
41
+ def cond
42
+ @view_column[:cond] || :like
43
+ end
44
+
45
+ def filter(value)
46
+ @view_column[:cond].call(value)
47
+ end
48
+
49
+ def source
50
+ @view_column[:source]
51
+ end
52
+
53
+ # Add sort_field option to allow overriding of sort field
54
+ def sort_field
55
+ @view_column[:sort_field] || field
56
+ end
57
+
58
+ # Add formater option to allow modification of the value
59
+ # before passing it to the database
60
+ def formater
61
+ @view_column[:formater]
62
+ end
63
+
64
+ # Add use_regex option to allow bypassing of regex search
65
+ def use_regex?
66
+ @view_column.fetch(:use_regex, true)
67
+ end
68
+
69
+ # Add delimiter option to handle range search
70
+ def delimiter
71
+ @view_column[:delimiter] || '-'
72
+ end
73
+
74
+ def table
75
+ model = source.split('.').first.constantize
76
+ model.arel_table rescue model
77
+ end
78
+
79
+ def field
80
+ source.split('.').last.to_sym
81
+ end
82
+
83
+ def search_query
84
+ search.regexp? ? regex_search : non_regex_search
85
+ end
86
+
87
+ def sort_query
88
+ custom_field? ? source : "#{table.name}.#{sort_field}"
89
+ end
90
+
91
+ private
92
+
93
+ def custom_field?
94
+ !source.include?('.')
95
+ end
96
+
97
+ def config
98
+ @config ||= AjaxDatatablesRails.config
99
+ end
100
+
101
+ def formated_value
102
+ formater ? formater.call(search.value) : search.value
103
+ end
104
+
105
+ # Using multi-select filters in JQuery Datatable auto-enables regex_search.
106
+ # Unfortunately regex_search doesn't work when filtering on primary keys with integer.
107
+ # It generates this kind of query : AND ("regions"."id" ~ '2|3') which throws an error :
108
+ # operator doesn't exist : integer ~ unknown
109
+ # The solution is to bypass regex_search and use non_regex_search with :in operator
110
+ def regex_search
111
+ if use_regex?
112
+ ::Arel::Nodes::Regexp.new((custom_field? ? field : table[field]), ::Arel::Nodes.build_quoted(formated_value))
113
+ else
114
+ non_regex_search
115
+ end
116
+ end
117
+
118
+ def non_regex_search
119
+ case cond
120
+ when Proc
121
+ filter(formated_value)
122
+ when :eq, :not_eq, :lt, :gt, :lteq, :gteq, :in
123
+ numeric_search
124
+ when :null_value
125
+ null_value_search
126
+ when :start_with
127
+ casted_column.matches("#{formated_value}%")
128
+ when :end_with
129
+ casted_column.matches("%#{formated_value}")
130
+ when :like
131
+ casted_column.matches("%#{formated_value}%")
132
+ else
133
+ nil
134
+ end
135
+ end
136
+
137
+ def typecast
138
+ case config.db_adapter
139
+ when :oracle, :oracleenhanced then 'VARCHAR2(4000)'
140
+ when :mysql, :mysql2 then 'CHAR'
141
+ when :sqlite, :sqlite3 then 'TEXT'
142
+ else
143
+ 'VARCHAR'
144
+ end
145
+ end
146
+
147
+ def casted_column
148
+ ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(typecast)])
149
+ end
150
+
151
+ def null_value_search
152
+ if formated_value == '!NULL'
153
+ table[field].not_eq(nil)
154
+ else
155
+ table[field].eq(nil)
156
+ end
157
+ end
158
+
159
+ def numeric_search
160
+ if custom_field?
161
+ ::Arel::Nodes::SqlLiteral.new(field).eq(formated_value)
162
+ else
163
+ table[field].send(cond, formated_value)
164
+ end
165
+ end
166
+
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,41 @@
1
+ module AjaxDatatablesRails
2
+ module Datatable
3
+ module ColumnDateFilter
4
+
5
+ def empty_range_search?
6
+ (formated_value == delimiter) || (range_start.blank? && range_end.blank?)
7
+ end
8
+
9
+ # A range value is in form '<range_start><delimiter><range_end>'
10
+ # This returns <range_start>
11
+ def range_start
12
+ @range_start ||= formated_value.split(delimiter)[0]
13
+ end
14
+
15
+ # A range value is in form '<range_start><delimiter><range_end>'
16
+ # This returns <range_end>
17
+ def range_end
18
+ @range_end ||= formated_value.split(delimiter)[1]
19
+ end
20
+
21
+ # Do a range search
22
+ def date_range_search
23
+ return nil if empty_range_search?
24
+ new_start = range_start.blank? ? DateTime.parse('01/01/1970') : DateTime.parse(range_start)
25
+ new_end = range_end.blank? ? DateTime.current : DateTime.parse("#{range_end} 23:59:59")
26
+ table[field].between(OpenStruct.new(begin: new_start, end: new_end))
27
+ end
28
+
29
+ private
30
+
31
+ def non_regex_search
32
+ if cond == :date_range
33
+ date_range_search
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,79 @@
1
+ module AjaxDatatablesRails
2
+ module Datatable
3
+
4
+ TRUE_VALUE = 'true'.freeze
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 ||= get_param(:order).map do |_, order_options|
22
+ SimpleOrder.new(self, order_options)
23
+ end
24
+ end
25
+
26
+ def order_by(how, what)
27
+ orders.find { |simple_order| simple_order.send(how) == what }
28
+ end
29
+
30
+ # ----------------- SEARCH METHODS --------------------
31
+
32
+ def searchable?
33
+ options[:search].present? && options[:search][:value].present?
34
+ end
35
+
36
+ def search
37
+ @search ||= SimpleSearch.new(options[:search])
38
+ end
39
+
40
+ # ----------------- COLUMN METHODS --------------------
41
+
42
+ def columns
43
+ @columns ||= get_param(:columns).map do |index, column_options|
44
+ Column.new(datatable, index, column_options)
45
+ end
46
+ end
47
+
48
+ def column_by(how, what)
49
+ columns.find { |simple_column| simple_column.send(how) == what }
50
+ end
51
+
52
+ # ----------------- OPTIONS METHODS --------------------
53
+
54
+ def paginate?
55
+ per_page != -1
56
+ end
57
+
58
+ def offset
59
+ (page - 1) * per_page
60
+ end
61
+
62
+ def page
63
+ (options[:start].to_i / per_page) + 1
64
+ end
65
+
66
+ def per_page
67
+ options.fetch(:length, 10).to_i
68
+ end
69
+
70
+ def get_param(param)
71
+ if AjaxDatatablesRails.old_rails?
72
+ options[param]
73
+ else
74
+ options[param].to_unsafe_h.with_indifferent_access
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,31 @@
1
+ module AjaxDatatablesRails
2
+ module Datatable
3
+ class SimpleOrder
4
+
5
+ DIRECTIONS = %w[DESC ASC].freeze
6
+
7
+ def initialize(datatable, options = {})
8
+ @datatable = datatable
9
+ @options = options
10
+ end
11
+
12
+ def query(sort_column)
13
+ "#{sort_column} #{direction}"
14
+ end
15
+
16
+ def column
17
+ @datatable.column_by(:index, column_index)
18
+ end
19
+
20
+ def direction
21
+ DIRECTIONS.find { |dir| dir == @options[:dir].upcase } || 'ASC'
22
+ end
23
+
24
+ private
25
+
26
+ def column_index
27
+ @options[:column]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module AjaxDatatablesRails
2
+ module Datatable
3
+ class SimpleSearch
4
+
5
+ def initialize(options = {})
6
+ @options = options
7
+ end
8
+
9
+ def value
10
+ @options[:value]
11
+ end
12
+
13
+ def regexp?
14
+ @options[:regex] == TRUE_VALUE
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,52 @@
1
+ module AjaxDatatablesRails
2
+ module ORM
3
+ module ActiveRecord
4
+
5
+ def fetch_records
6
+ get_raw_records
7
+ end
8
+
9
+ def filter_records(records)
10
+ records.where(build_conditions)
11
+ end
12
+
13
+ def sort_records(records)
14
+ sort_by = datatable.orders.inject([]) do |queries, order|
15
+ column = order.column
16
+ queries << order.query(column.sort_query) if column
17
+ end
18
+ records.order(sort_by.join(", "))
19
+ end
20
+
21
+ def paginate_records(records)
22
+ records.offset(datatable.offset).limit(datatable.per_page)
23
+ end
24
+
25
+ # ----------------- SEARCH HELPER METHODS --------------------
26
+
27
+ def build_conditions
28
+ if datatable.searchable?
29
+ build_conditions_for_datatable
30
+ else
31
+ build_conditions_for_selected_columns
32
+ end
33
+ end
34
+
35
+ def build_conditions_for_datatable
36
+ search_for = datatable.search.value.split(' ')
37
+ criteria = search_for.inject([]) do |crit, atom|
38
+ search = Datatable::SimpleSearch.new({ value: atom, regex: datatable.search.regexp? })
39
+ crit << searchable_columns.map do |simple_column|
40
+ simple_column.search = search
41
+ simple_column.search_query
42
+ end.reduce(:or)
43
+ end.reduce(:and)
44
+ criteria
45
+ end
46
+
47
+ def build_conditions_for_selected_columns
48
+ search_columns.map(&:search_query).compact.reduce(:and)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module AjaxDatatablesRails
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  AjaxDatatablesRails.configure do |config|
2
- # available options for db_adapter are: :oracle, :pg, :mysql2, :sqlite3
2
+ # available options for db_adapter are: :pg, :mysql, :mysql2, :sqlite, :sqlite3
3
3
  # config.db_adapter = :pg
4
4
 
5
- # available options for paginator are: :simple_paginator, :kaminari, :will_paginate
6
- # config.paginator = :simple_paginator
5
+ # available options for orm are: :active_record, :mongoid
6
+ # config.orm = :active_record
7
7
  end
@@ -5,35 +5,23 @@ module Rails
5
5
  class DatatableGenerator < ::Rails::Generators::Base
6
6
  desc 'Creates a *_datatable model in the app/datatables directory.'
7
7
  source_root File.expand_path('../templates', __FILE__)
8
- argument :name, :type => :string
8
+ argument :name, type: :string
9
9
 
10
10
  def generate_datatable
11
- file_prefix = set_filename(name)
12
- @datatable_name = set_datatable_name(name)
13
11
  template 'datatable.rb', File.join(
14
- 'app/datatables', "#{file_prefix}_datatable.rb"
12
+ 'app/datatables', "#{datatable_path}.rb"
15
13
  )
16
14
  end
17
15
 
18
- private
19
-
20
- def set_filename(name)
21
- name.include?('_') ? name : name.to_s.underscore
22
- end
23
-
24
- def set_datatable_name(name)
25
- name.include?('_') ? build_name(name) : capitalize(name)
16
+ def datatable_name
17
+ datatable_path.classify
26
18
  end
27
19
 
28
- def build_name(name)
29
- pieces = name.split('_')
30
- pieces.map(&:titleize).join
20
+ private
21
+ def datatable_path
22
+ "#{name.underscore}_datatable"
31
23
  end
32
24
 
33
- def capitalize(name)
34
- return name if name[0] == name[0].upcase
35
- name.capitalize
36
- end
37
25
  end
38
26
  end
39
27
  end