require_relative 'column' module RailsServersideDatatables class DataTable def initialize( query, parameters ) @columns = [] @parameters = parameters @query = query @total_records = query.size end def add_column( definition, structured = {}, &block ) if definition.is_a? Column @columns.push definition else if block.nil? @columns.push Column.new( "cc_#{@columns.size}".to_sym, definition[:expression], structured ) else @columns.push( Column.new( "cc_#{@columns.size}".to_sym, definition[:expression], structured ) do |value, structured| block.call(value, structured) end) end @columns.last.display_name = definition.fetch( :display_name, "#{@columns.size}" ) end end def as_json return configuration if config_request? apply_select apply_column_filters apply_global_filter @records_filtered = @query.size apply_order apply_offset apply_limit { draw: @parameters[ :draw ] || 1, recordsTotal: @total_records, recordsFiltered: @records_filtered, data: @query.map { |record| @columns.map { |col| col.value( record ) } } } end def config_request? @parameters[ 'dt_config' ] == 'true' end protected def configuration { columns: @columns.map { |c| c.configuration } } end def apply_offset @query = @query.offset( offset ) end def apply_limit @query = @query.limit( limit ) end def apply_select @query = @query.select( ExpressionList.new( @columns.map { |c| c.select }.flatten ).to_s ) end def apply_column_filters conditions = ExprTreeNode.new( 'AND', Type::OPERATOR, true ) column_filters.each { |filter| conditions.add_argument filter } @query = @query.where( conditions.to_s ) if conditions.arguments? end def apply_global_filter unless global_filter? return end filter = ExprTreeNode.new( 'OR', Type::OPERATOR, false ) global_filter.each { |f| filter.add_argument f } if filter.arguments? @query = @query.where( filter.to_s ) end end def apply_order criteria = order if criteria.expression.arguments? @query = @query.order( order.to_s ) end end def offset @parameters[:start] || 0 end def limit @parameters[:length] || 100 end def column_filters ( @parameters[:columns] || {} ).to_a.map do |column| filter = ( column.last[ :search ] || { value: nil } )[ :value ] if filter.present? @columns[ column.last[:data].to_i ].filter( filter ) else nil end end.reject { |v| v.nil? } end def global_filter_text (@parameters[ :search ] || { value: nil } )[ :value ] end def global_filter? global_filter_text.present? end def global_filter @columns.map { |c| c.filter( global_filter_text ) } end def order ExpressionList.new( ( @parameters[ :order ] || {} ).values.map { |column| @columns[ column[:column].to_i ].order( column[:dir] ) } ) end end end