lib/ajax-datatables-rails/datatable/column.rb
# frozen_string_literal: true
module AjaxDatatablesRails
module Datatable
class Column
include Search
include Order
include DateFilter
attr_reader :datatable, :index, :options, :column_name
attr_writer :search
def initialize(datatable, index, options)
@datatable = datatable
@index = index
@options = options
@column_name = options[:data]&.to_sym
@view_column = datatable.view_columns[@column_name]
@model = nil
@field = nil
@type_cast = nil
@casted_column = nil
@search = nil
@delimiter = nil
@range_start = nil
@range_end = nil
validate_settings!
end
def data
options[:data].presence || options[:name]
end
def source
@view_column[:source]
end
def table
model.respond_to?(:arel_table) ? model.arel_table : model
end
def model
@model ||= custom_field? ? source : source.split('.').first.constantize
end
def field
@field ||= custom_field? ? source : source.split('.').last.to_sym
end
def custom_field?
!source.include?('.')
end
# Add formatter option to allow modification of the value
# before passing it to the database
def formatter
@view_column[:formatter]
end
def formatted_value
formatter ? formatter.call(search.value) : search.value
end
private
TYPE_CAST_DEFAULT = 'VARCHAR'
TYPE_CAST_MYSQL = 'CHAR'
TYPE_CAST_SQLITE = 'TEXT'
TYPE_CAST_ORACLE = 'VARCHAR2(4000)'
TYPE_CAST_SQLSERVER = 'VARCHAR(4000)'
DB_ADAPTER_TYPE_CAST = {
mysql: TYPE_CAST_MYSQL,
mysql2: TYPE_CAST_MYSQL,
trilogy: TYPE_CAST_MYSQL,
sqlite: TYPE_CAST_SQLITE,
sqlite3: TYPE_CAST_SQLITE,
oracle: TYPE_CAST_ORACLE,
oracleenhanced: TYPE_CAST_ORACLE,
oracle_enhanced: TYPE_CAST_ORACLE,
sqlserver: TYPE_CAST_SQLSERVER,
}.freeze
private_constant :TYPE_CAST_DEFAULT
private_constant :TYPE_CAST_MYSQL
private_constant :TYPE_CAST_SQLITE
private_constant :TYPE_CAST_ORACLE
private_constant :TYPE_CAST_SQLSERVER
private_constant :DB_ADAPTER_TYPE_CAST
def type_cast
@type_cast ||= DB_ADAPTER_TYPE_CAST.fetch(datatable.db_adapter, TYPE_CAST_DEFAULT)
end
def casted_column
@casted_column ||= ::Arel::Nodes::NamedFunction.new('CAST', [table[field].as(type_cast)])
end
def validate_settings!
raise AjaxDatatablesRails::Error::InvalidSearchColumn, 'Unknown column. Check that `data` field is filled on JS side with the column name' if column_name.empty?
raise AjaxDatatablesRails::Error::InvalidSearchColumn, "Check that column '#{column_name}' exists in view_columns" unless valid_search_column?(column_name)
raise AjaxDatatablesRails::Error::InvalidSearchCondition, cond unless valid_search_condition?(cond)
end
def valid_search_column?(column_name)
!datatable.view_columns[column_name].nil?
end
VALID_SEARCH_CONDITIONS = [
# String condition
:start_with, :end_with, :like, :string_eq, :string_in, :null_value,
# Numeric condition
:eq, :not_eq, :lt, :gt, :lteq, :gteq, :in,
# Date condition
:date_range
].freeze
private_constant :VALID_SEARCH_CONDITIONS
def valid_search_condition?(cond)
return true if cond.is_a?(Proc)
VALID_SEARCH_CONDITIONS.include?(cond)
end
end
end
end