trk_datatables 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +56 -0
- data/LICENSE.txt +21 -0
- data/README.md +267 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/trk_datatables.rb +15 -0
- data/lib/trk_datatables/active_record.rb +110 -0
- data/lib/trk_datatables/base.rb +189 -0
- data/lib/trk_datatables/column_key_options.rb +165 -0
- data/lib/trk_datatables/dt_params.rb +156 -0
- data/lib/trk_datatables/preferences.rb +35 -0
- data/lib/trk_datatables/render_html.rb +133 -0
- data/lib/trk_datatables/version.rb +3 -0
- data/trk_datatables.gemspec +53 -0
- data/yarn.lock +4 -0
- metadata +210 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'trk_datatables'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'trk_datatables/version'
|
2
|
+
# modules
|
3
|
+
require 'trk_datatables/preferences.rb'
|
4
|
+
|
5
|
+
require 'trk_datatables/base'
|
6
|
+
require 'trk_datatables/active_record'
|
7
|
+
require 'trk_datatables/dt_params'
|
8
|
+
require 'trk_datatables/column_key_options.rb'
|
9
|
+
require 'trk_datatables/render_html.rb'
|
10
|
+
|
11
|
+
# libs
|
12
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
13
|
+
require 'active_support/core_ext/hash/keys'
|
14
|
+
require 'active_support/core_ext/string/inflections'
|
15
|
+
require 'active_support/core_ext/string/output_safety'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module TrkDatatables
|
2
|
+
class ActiveRecord < Base
|
3
|
+
# Global search. All columns are typecasted to string. Search string is
|
4
|
+
# splited by space and "and"-ed.
|
5
|
+
def filter_by_search_all(filtered_items)
|
6
|
+
conditions = @dt_params.search_all.split(' ').map do |search_string|
|
7
|
+
@column_key_options.searchable_and_global_search.map do |column_key_option|
|
8
|
+
filter_column_as_string column_key_option, search_string
|
9
|
+
end.reduce(:or) # any searchable column is 'or'-ed
|
10
|
+
end.reduce(:and) # 'and' for each search_string
|
11
|
+
|
12
|
+
filtered_items.where conditions
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter_by_columns(filtered_items)
|
16
|
+
conditions = @dt_params.dt_columns.each_with_object([]) do |dt_column, cond|
|
17
|
+
next unless dt_column[:searchable] && dt_column[:search_value].present?
|
18
|
+
|
19
|
+
# check both params and configuration
|
20
|
+
column_key_option = @column_key_options[dt_column[:index]]
|
21
|
+
next if column_key_option[:column_options][ColumnKeyOptions::SEARCH_OPTION] == false
|
22
|
+
|
23
|
+
cond << build_condition_for_column(column_key_option, dt_column[:search_value])
|
24
|
+
end.reduce(:and) # 'and' for each searchable column
|
25
|
+
|
26
|
+
filtered_items.where conditions
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_condition_for_column(column_key_option, search_value)
|
30
|
+
# nil is when we use action columns, usually not column searchable
|
31
|
+
return nil if column_key_option[:column_type_in_db].nil?
|
32
|
+
|
33
|
+
select_options = column_key_option[:column_options][ColumnKeyOptions::SELECT_OPTIONS]
|
34
|
+
if select_options.present?
|
35
|
+
filter_column_as_in(column_key_option, search_value)
|
36
|
+
elsif %i[date datetime integer float].include?(column_key_option[:column_type_in_db]) && \
|
37
|
+
search_value.include?(BETWEEN_SEPARATOR)
|
38
|
+
from, to = search_value.split BETWEEN_SEPARATOR
|
39
|
+
filter_column_as_between(column_key_option, from, to)
|
40
|
+
else
|
41
|
+
filter_column_as_string(column_key_option, search_value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def filter_column_as_string(column_key_option, search_value)
|
46
|
+
search_value.split(' ').map do |search_string|
|
47
|
+
casted_column = ::Arel::Nodes::NamedFunction.new(
|
48
|
+
'CAST',
|
49
|
+
[_arel_column(column_key_option).as(@column_key_options.string_cast)]
|
50
|
+
)
|
51
|
+
casted_column.matches("%#{search_string}%")
|
52
|
+
end.reduce(:and)
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_column_as_between(column_key_option, from, to)
|
56
|
+
from, to = _parse_from_to(from, to, column_key_option)
|
57
|
+
if from.present? && to.present?
|
58
|
+
_arel_column(column_key_option).between(from..to)
|
59
|
+
elsif from.present?
|
60
|
+
_arel_column(column_key_option).gteq(from)
|
61
|
+
elsif to.present?
|
62
|
+
_arel_column(column_key_option).lteq(to)
|
63
|
+
# else
|
64
|
+
# nil will result in true relation
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def filter_column_as_in(column_key_option, search_value)
|
69
|
+
_arel_column(column_key_option).in search_value.split(MULTIPLE_OPTION_SEPARATOR)
|
70
|
+
end
|
71
|
+
|
72
|
+
def _parse_from_to(from, to, column_key_option)
|
73
|
+
case column_key_option[:column_type_in_db]
|
74
|
+
# when :integer, :float
|
75
|
+
# we do not need to cast from string since range will do automatically
|
76
|
+
when :date, :datetime
|
77
|
+
from = _parse_in_zone(from) if from.present?
|
78
|
+
to = _parse_in_zone(to) if to.present?
|
79
|
+
end
|
80
|
+
[from, to]
|
81
|
+
end
|
82
|
+
|
83
|
+
# rubocop:disable Rails/TimeZone
|
84
|
+
def _parse_in_zone(time)
|
85
|
+
# without rails we will parse without zone so make sure params are correct
|
86
|
+
Time.zone ? Time.zone.parse(time) : Time.parse(time)
|
87
|
+
end
|
88
|
+
# rubocop:enable Rails/TimeZone
|
89
|
+
|
90
|
+
def order_and_paginate_items(filtered_items)
|
91
|
+
filtered_items = order_items filtered_items
|
92
|
+
filtered_items = filtered_items.offset(@dt_params.dt_offset).limit(dt_per_page_or_default)
|
93
|
+
filtered_items
|
94
|
+
end
|
95
|
+
|
96
|
+
def order_items(filtered_items)
|
97
|
+
order_by = dt_orders_or_default.each_with_object([]) do |dt_order, queries|
|
98
|
+
column_key_option = @column_key_options[dt_order[:column_index]]
|
99
|
+
next if column_key_option[:column_options][ColumnKeyOptions::ORDER_OPTION] == false
|
100
|
+
|
101
|
+
queries << "#{column_key_option[:column_key]} #{dt_order[:direction]}"
|
102
|
+
end
|
103
|
+
filtered_items.order(Arel.sql(order_by.join(', ')))
|
104
|
+
end
|
105
|
+
|
106
|
+
def _arel_column(column_key_option)
|
107
|
+
column_key_option[:table_class].arel_table[column_key_option[:column_name]]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module TrkDatatables
|
2
|
+
BETWEEN_SEPARATOR = ' - '.freeze
|
3
|
+
MULTIPLE_OPTION_SEPARATOR = '|'.freeze
|
4
|
+
DEFAULT_ORDER = [{ column_index: 0, direction: :desc }].freeze
|
5
|
+
DEFAULT_PAGE_LENGTH = 10
|
6
|
+
|
7
|
+
class Error < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class Base
|
11
|
+
include TrkDatatables::Preferences
|
12
|
+
attr_accessor :column_key_options
|
13
|
+
|
14
|
+
def initialize(view)
|
15
|
+
@view = view
|
16
|
+
@dt_params = DtParams.new view.params
|
17
|
+
@column_key_options = ColumnKeyOptions.new columns, global_search_columns
|
18
|
+
|
19
|
+
# if @dt_params.dt_columns.size != @column_key_options.size
|
20
|
+
# raise Error, "dt_columns size of columns is #{@dt_params.dt_columns.size} \
|
21
|
+
# but column_key_options size is #{@column_key_options.size}"
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get all items from db
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# def all_items
|
29
|
+
# Post.joins(:users).published
|
30
|
+
# end
|
31
|
+
# @return [ActiveRecord::Relation]
|
32
|
+
def all_items
|
33
|
+
raise NotImplementedError, "You should implement #{__method__} method"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Define columns of a table
|
37
|
+
# For simplest version you can notate column_keys as Array of strings
|
38
|
+
# @example
|
39
|
+
# def column
|
40
|
+
# %w[posts.id posts.status users.name]
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# When you need customisation of some columns, you need to define Hash of column_key => { column_options }
|
44
|
+
# @example
|
45
|
+
# def columns
|
46
|
+
# {
|
47
|
+
# 'posts.id': {},
|
48
|
+
# 'posts.status' => { search: false },
|
49
|
+
# 'users.name' => { order: false },
|
50
|
+
# }
|
51
|
+
# end
|
52
|
+
# @return Array of Hash
|
53
|
+
def columns
|
54
|
+
raise NotImplementedError, "You should implement #{__method__} method #{link_to_rdoc self.class, __method__}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Define columns that are not returned to page but only used as mathing for
|
58
|
+
# global search
|
59
|
+
# @example
|
60
|
+
# def global_search_columns
|
61
|
+
# %w[name email].map {|col| "users.#{col}" } + %w[posts.body]
|
62
|
+
# end
|
63
|
+
def global_search_columns
|
64
|
+
[]
|
65
|
+
end
|
66
|
+
|
67
|
+
# Define page data
|
68
|
+
# @example
|
69
|
+
# def rows(page_items)
|
70
|
+
# page_items.map do |post|
|
71
|
+
# post_status = @view.content_tag :span, post.status, class: "label label-#{@view.convert_status_to_class post.status}"
|
72
|
+
# [
|
73
|
+
# post.id,
|
74
|
+
# post_status,
|
75
|
+
# @view.link_to(post.user.name, post.user)
|
76
|
+
# ]
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
def rows(_page_items)
|
80
|
+
raise NotImplementedError, "You should implement #{__method__} method"
|
81
|
+
end
|
82
|
+
|
83
|
+
def filter_by_search_all(_all)
|
84
|
+
raise 'filter_by_columns_is_defined_in_specific_orm'
|
85
|
+
end
|
86
|
+
|
87
|
+
def filter_by_columns(_all)
|
88
|
+
raise 'filter_by_columns_is_defined_in_specific_orm' \
|
89
|
+
"\n Extent from TrkDatatables::ActiveRecord instead of TrkDatatables::Base"
|
90
|
+
end
|
91
|
+
|
92
|
+
def order_and_paginate_items(_filtered_items)
|
93
|
+
raise 'order_and_paginate_items_is_defined_in_specific_orm'
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns dt_orders or default
|
97
|
+
# @return
|
98
|
+
# [
|
99
|
+
# { column_index: 0, direction: :desc },
|
100
|
+
# ]
|
101
|
+
def dt_orders_or_default
|
102
|
+
return @dt_orders_or_default if defined? @dt_orders_or_default
|
103
|
+
|
104
|
+
if @dt_params.dt_orders.present?
|
105
|
+
@dt_orders_or_default = @dt_params.dt_orders
|
106
|
+
set_preference :order, @dt_params.dt_orders
|
107
|
+
else
|
108
|
+
@dt_orders_or_default = get_preference(:order) || DEFAULT_ORDER
|
109
|
+
end
|
110
|
+
@dt_orders_or_default
|
111
|
+
end
|
112
|
+
|
113
|
+
def dt_per_page_or_default
|
114
|
+
return @dt_per_page_or_default if defined? @dt_per_page_or_default
|
115
|
+
|
116
|
+
@dt_per_page_or_default = \
|
117
|
+
if @dt_params.dt_per_page.present?
|
118
|
+
set_preference :per_page, @dt_params.dt_per_page
|
119
|
+
@dt_params.dt_per_page
|
120
|
+
else
|
121
|
+
get_preference(:per_page) || DEFAULT_PAGE_LENGTH
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Set params for columns. This is class method so you do not need datatable
|
126
|
+
# instance.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# link_to 'Published posts for my@email.com',
|
130
|
+
# posts_path(PostsDatatable.params('posts.status': :published,
|
131
|
+
# 'users.email: 'my@email.com')
|
132
|
+
#
|
133
|
+
# You can always use your params for filtering outside of datatable
|
134
|
+
# @example
|
135
|
+
# link_to 'Published posts for user1',
|
136
|
+
# posts_path(PostsDatatable.params_set('posts.status': :published).merge(user_id: user1.id))
|
137
|
+
def self.params_set(attr)
|
138
|
+
datatable = new OpenStruct.new(params: {})
|
139
|
+
result = {}
|
140
|
+
attr.each do |column_key, value|
|
141
|
+
value = value.join MULTIPLE_OPTION_SEPARATOR if value.is_a? Array
|
142
|
+
column_index = datatable.index_by_column_key column_key
|
143
|
+
result = result.deep_merge DtParams.param_set column_index, value
|
144
|
+
end
|
145
|
+
result
|
146
|
+
end
|
147
|
+
|
148
|
+
# We need this method publicly available since we use it for class method
|
149
|
+
# params_set
|
150
|
+
def index_by_column_key(column_key)
|
151
|
+
@column_key_options.index_by_column_key column_key
|
152
|
+
end
|
153
|
+
|
154
|
+
# Helper to populate column search from params, used in
|
155
|
+
# RenderHtml#thead
|
156
|
+
# @example
|
157
|
+
# @datatable.param_get('users.email')
|
158
|
+
def param_get(column_key)
|
159
|
+
column_index = index_by_column_key column_key
|
160
|
+
@dt_params.param_get column_index
|
161
|
+
end
|
162
|
+
|
163
|
+
# _attr is given by Rails template, prefix, layout... not used
|
164
|
+
def as_json(_attr = nil)
|
165
|
+
# get the value if it is not a relation
|
166
|
+
all_count = all_items.count
|
167
|
+
filtered_items = filter_by_search_all filter_by_columns all_items
|
168
|
+
ordered_paginated_filtered_items = order_and_paginate_items filtered_items
|
169
|
+
@dt_params.as_json(
|
170
|
+
all_count,
|
171
|
+
filtered_items.count,
|
172
|
+
rows(ordered_paginated_filtered_items)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def link_to_rdoc(klass, method)
|
177
|
+
"http://localhost:8808/docs/TrkDatatables/#{klass.name}##{method}-instance_method"
|
178
|
+
end
|
179
|
+
|
180
|
+
def render_html(search_link = nil, html_options = {})
|
181
|
+
if search_link.is_a? Hash
|
182
|
+
html_options = search_link
|
183
|
+
search_link = nil
|
184
|
+
end
|
185
|
+
render = RenderHtml.new(search_link, self, html_options)
|
186
|
+
render.result
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module TrkDatatables
|
2
|
+
# rubocop:disable ClassLength
|
3
|
+
class ColumnKeyOptions
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
# All options that you can use for columns:
|
7
|
+
#
|
8
|
+
# search: if you want to enable global and column search, default is true
|
9
|
+
# order: `:asc` or `:desc` or `false`, default is `:desc`
|
10
|
+
# @code
|
11
|
+
# def columns
|
12
|
+
# {
|
13
|
+
# 'users.name': { search: false }
|
14
|
+
# }
|
15
|
+
SEARCH_OPTION = :search
|
16
|
+
ORDER_OPTION = :order
|
17
|
+
TITLE_OPTION = :title
|
18
|
+
SELECT_OPTIONS = :select_options
|
19
|
+
# this will load date picker
|
20
|
+
# SEARCH_OPTION_DATE_VALUE = :date
|
21
|
+
# SEARCH_OPTION_DATETIME_VALUE = :datetime
|
22
|
+
COLUMN_OPTIONS = [SEARCH_OPTION, ORDER_OPTION, TITLE_OPTION, SELECT_OPTIONS].freeze
|
23
|
+
|
24
|
+
STRING_TYPE_CAST_POSTGRES = 'VARCHAR'.freeze
|
25
|
+
STRING_TYPE_CAST_MYSQL = 'CHAR'.freeze
|
26
|
+
STRING_TYPE_CAST_SQLITE = 'TEXT'.freeze
|
27
|
+
STRING_TYPE_CAST_ORACLE = 'VARCHAR2(4000)'.freeze
|
28
|
+
|
29
|
+
DB_ADAPTER_STRING_TYPE_CAST = {
|
30
|
+
psql: STRING_TYPE_CAST_POSTGRES,
|
31
|
+
mysql: STRING_TYPE_CAST_MYSQL,
|
32
|
+
mysql2: STRING_TYPE_CAST_MYSQL,
|
33
|
+
sqlite: STRING_TYPE_CAST_SQLITE,
|
34
|
+
sqlite3: STRING_TYPE_CAST_SQLITE,
|
35
|
+
oracle: STRING_TYPE_CAST_ORACLE,
|
36
|
+
oracleenhanced: STRING_TYPE_CAST_ORACLE
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
attr_accessor :string_cast
|
40
|
+
|
41
|
+
# @return
|
42
|
+
# {
|
43
|
+
# column_key: :'users.name',
|
44
|
+
# column_options: { order: false, select_options: User.statuses },
|
45
|
+
# table_class: User,
|
46
|
+
# column_name: :name,
|
47
|
+
# column_type_in_db: :string,
|
48
|
+
# title: 'Name',
|
49
|
+
# html_options: { class: 'my-class' },
|
50
|
+
# }
|
51
|
+
def initialize(cols, global_search_cols)
|
52
|
+
# if someone use Array instead of hash, we will use first element
|
53
|
+
if cols.is_a? Array
|
54
|
+
cols = cols.each_with_object({}) do |column_key, hash|
|
55
|
+
hash[column_key.to_sym] = {}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
_set_data(cols)
|
59
|
+
_set_global_search_cols(global_search_cols)
|
60
|
+
@string_cast = _determine_string_type_cast
|
61
|
+
end
|
62
|
+
|
63
|
+
def _set_data(cols)
|
64
|
+
@data = cols.each_with_object([]) do |(column_key, column_options), arr|
|
65
|
+
raise Error, 'Column options needs to be a Hash' unless column_options.is_a? Hash
|
66
|
+
|
67
|
+
column_options.assert_valid_keys(*COLUMN_OPTIONS)
|
68
|
+
table_name, column_name = column_key.to_s.split '.'
|
69
|
+
raise Error, 'Column key needs to have one dot table.column' if table_name.present? && column_name.nil?
|
70
|
+
|
71
|
+
if table_name.blank?
|
72
|
+
column_name = 'actions' # some default name for a title
|
73
|
+
else
|
74
|
+
table_class = table_name.singularize.camelcase.constantize
|
75
|
+
column_type_in_db = _determine_db_type_for_column(table_class, column_name)
|
76
|
+
end
|
77
|
+
arr << {
|
78
|
+
column_key: column_key.to_sym,
|
79
|
+
column_options: column_options,
|
80
|
+
table_class: table_class,
|
81
|
+
column_name: column_name,
|
82
|
+
column_type_in_db: column_type_in_db,
|
83
|
+
# the following are used for RenderHtml
|
84
|
+
title: column_options[TITLE_OPTION] || column_name.humanize,
|
85
|
+
html_options: html_options(column_options, column_type_in_db),
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def _set_global_search_cols(global_search_cols)
|
91
|
+
@global_search_cols = global_search_cols.each_with_object([]) do |column_key, arr|
|
92
|
+
table_name, column_name = column_key.to_s.split '.'
|
93
|
+
table_class = table_name.singularize.camelcase.constantize
|
94
|
+
column_type_in_db = _determine_db_type_for_column(table_class, column_name)
|
95
|
+
arr << {
|
96
|
+
column_key: column_key.to_sym,
|
97
|
+
column_options: {},
|
98
|
+
table_class: table_class,
|
99
|
+
column_name: column_name,
|
100
|
+
column_type_in_db: column_type_in_db,
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# This is helper
|
106
|
+
def _determine_string_type_cast # :nodoc:
|
107
|
+
raise NotImplementedError unless defined?(::ActiveRecord::Base)
|
108
|
+
|
109
|
+
DB_ADAPTER_STRING_TYPE_CAST[::ActiveRecord::Base.connection_config[:adapter].to_sym]
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return
|
113
|
+
# :string, :integer, :date, :datetime
|
114
|
+
def _determine_db_type_for_column(table_class, column_name)
|
115
|
+
raise NotImplementedError unless defined?(::ActiveRecord::Base)
|
116
|
+
|
117
|
+
ar_column = table_class.columns_hash[column_name]
|
118
|
+
raise Error, "Can't find column #{column_name} in #{table_class.name}" unless ar_column
|
119
|
+
|
120
|
+
ar_column.type
|
121
|
+
end
|
122
|
+
|
123
|
+
def searchable
|
124
|
+
@data.reject do |column_key_option|
|
125
|
+
column_key_option[:column_options][SEARCH_OPTION] == false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def searchable_and_global_search
|
130
|
+
searchable + @global_search_cols
|
131
|
+
end
|
132
|
+
|
133
|
+
def [](index)
|
134
|
+
raise Error, "You asked for column index=#{index} but there is only #{@data.size} columns" if index >= @data.size
|
135
|
+
|
136
|
+
@data[index]
|
137
|
+
end
|
138
|
+
|
139
|
+
def each(&block)
|
140
|
+
@data.each(&block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def size
|
144
|
+
@data.size
|
145
|
+
end
|
146
|
+
|
147
|
+
def index_by_column_key(column_key)
|
148
|
+
i = @data.find_index do |column_key_option|
|
149
|
+
column_key_option[:column_key] == column_key.to_sym
|
150
|
+
end
|
151
|
+
raise Error, "Can't find index for #{column_key} in #{@data.map { |d| d[:column_key] }.join(', ')}" if i.nil?
|
152
|
+
|
153
|
+
i
|
154
|
+
end
|
155
|
+
|
156
|
+
def html_options(column_options, column_type_in_db)
|
157
|
+
res = {}
|
158
|
+
res['data-searchable'] = false if column_options[SEARCH_OPTION] == false
|
159
|
+
res['data-orderable'] = false if column_options[ORDER_OPTION] == false
|
160
|
+
res['data-datatable-range'] = true if %i[date datetime].include? column_type_in_db
|
161
|
+
res
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# rubocop:enable ClassLength
|
165
|
+
end
|