module Filtered::Base::ClassMethods

Public Instance Methods

field(field_name, options = {}, &block) click to toggle source

Defines a field in a filter.

When you provide no options, it will by default add a simple `where(year: [“2010”, “2011”])` clause to the query.

class CarFilter < ApplicationFilter

  field :year

end

Or with a block which is passed with the current field value. Note that block must return proc which will be merged in the query:

class CarFilter < ApplicationFilter

  field :year do |value|
    -> { where(year: "20#{value}") }
  end

end

The second argument to a block is filter object itself:

class CarFilter < ApplicationFilter

  attr_accessor :user

  field :year, allow_blank: true do |value, filter|
    -> { where(year: value, user: filter.user) }
  end

end

Options:

  • :default - Specifies a method (e.g. default: :default_year), proc (e.g. default: Proc.new { |filter| filter.default_year }) or object (e.g default: "2012") to call to determine default value. It will be called only if the field not passed into filter constructor.

  • :allow_nil - Add the field into query if field value is nil.

  • :allow_blank - Add the field into query if the value is blank.

  • :if - Specifies a method or proc to call to determine if the field addition to query should occur (e.g. if: :allow_year, or if: Proc.new { |year| %w(2018 2019).include?(year) }). The method, or proc should return a true or false value.

  • :unless - Specifies a method or proc to call to determine if the field addition to query should not occur (e.g. if: :allow_year, or if: Proc.new { |year| (1999..2005).include?(year) }). The method, or proc should return a true or false value.

# File lib/filtered/base.rb, line 55
def field(field_name, options = {}, &block)
  field_name = field_name.to_sym

  field_definition = FieldDefinition.new.tap do |fd|
    fd.query_updater = if block_given?
      # TODO look for methods to validate that block returns proc
      block
    else
      # AR ref
      ->(value) { -> { where(field_name => value) } }
    end


    raise Error, "'if' can't be used with 'allow_nil' or 'allow_blank'" if options[:if] && (options[:allow_nil] || options[:allow_blank])

    fd.acceptance_computer = if options[:if].is_a?(Proc)
      options[:if]
    elsif options[:if].is_a?(Symbol)
      -> (value, filter) { filter.send(options[:if], value) }
    elsif options[:if].nil?
      # TODO checking that value is blank just comparing to empty string is very naive
      ->(value) { (options[:allow_nil] || !value.nil?) && (options[:allow_blank] || value != "") }
    else
      raise Error, "Unsupported argument #{options[:if].class} for 'if'. Pass proc or method name"
    end


    raise Error, "'unless' can't be used with 'allow_nil' or 'allow_blank'" if options[:unless] && (options[:allow_nil] || options[:allow_blank])

    fd.decline_computer = if options[:unless].is_a?(Proc)
      options[:unless]
    elsif options[:unless].is_a?(Symbol)
      -> (value, filter) { filter.send(options[:unless], value) }
    elsif options[:unless].nil?
      -> { false }
    else
      raise Error, "Unsupported argument #{options[:unless].class} for 'unless'. Pass proc or method name"
    end


    fd.default_computer = if options[:default].is_a?(Proc)
      options[:default]
    elsif options[:default].is_a?(Symbol)
      -> (filter) { filter.send(options[:default]) }
    elsif options[:default]
      -> (_) { options[:default] }
    end
  end

  field_definitions[field_name] = field_definition

  define_method field_name do
    fields[field_name]
  end
end
field_definitions() click to toggle source
# File lib/filtered/base.rb, line 111
def field_definitions
  instance_variable_get(:"@field_definitions")
end