module RailsServersideDatatables module Type COLUMN = :column STRING = :string NUMERIC = :numeric FUNCTION = :function OPERATOR = :operator NULL = :null RAW = :raw end class Nothing end class ExprTreeNode def initialize(token, type, force_brackets = true) @token = token @type = type @arguments = [] @force_brackets = force_brackets end def add_argument(argument) @arguments.push ExprTreeNode.create_by_type(argument) unless argument.is_a? Nothing end def arguments? @arguments.any? end def to_s case @type when Type::RAW return @token.to_s when Type::COLUMN return ExprTreeNode.format_column @token when Type::STRING return ExprTreeNode.format_string @token when Type::NUMERIC return @token.is_a?(Numeric) ? @token.to_s : @token.to_f.to_s when Type::FUNCTION return ExprTreeNode.format_function @token, @arguments when Type::OPERATOR return ExprTreeNode.format_operator @token, @arguments, @force_brackets when Type::NULL return 'NULL' else raise 'Invalid fragment type passed.' end end def self.create_by_type(type) if type.is_a? Numeric return ExprTreeNode.new(type, Type::NUMERIC) elsif type.is_a?(Expression) return type.expression elsif type.nil? return raw('NULL') elsif type.is_a? Symbol return ExprTreeNode.new(type, Type::COLUMN) elsif type.is_a? String return ExprTreeNode.new(type, Type::STRING) elsif type.is_a? ExprTreeNode return type elsif type.is_a? Array f = ExprTreeNode.new('||', Type::OPERATOR) type.each { |e| f.add_argument e } return f else raise 'Invalid type' end end def self.raw(expr) ExprTreeNode.new(expr.to_s, Type::RAW) end private def self.format_column(name) name.to_s.split('.').map { |f| '"' + f + '"' }.join '.' end def self.format_string(name) "#{ActiveRecord::Base.sanitize name}" end def self.format_function(name, arguments) name.to_s.upcase + '(' + arguments.map { |arg| arg.to_s }.join(',') + ')' end def self.format_operator(operator, arguments, brackets) term = arguments.map { |arg| arg.to_s }.join(" #{operator} ") brackets ? '(' + term + ')' : term end end end