require 'ransack/nodes' require 'ransack/context' require 'ransack/naming' module Ransack class Search include Naming attr_reader :base, :context delegate :object, :klass, :to => :context delegate :new_grouping, :new_condition, :build_grouping, :build_condition, :translate, :to => :base def initialize(object, params = {}, options = {}) params = {} unless params.is_a?(Hash) (params ||= {}).delete_if { |k, v| [*v].all?{|i| i.blank? && i != false } } @context = Context.for(object, options) @context.auth_object = options[:auth_object] @base = Nodes::Grouping.new(@context, 'and') build(params.with_indifferent_access) end def result(opts = {}) @context.evaluate(self, opts) end def build(params) collapse_multiparameter_attributes!(params).each do |key, value| case key when 's', 'sorts' send("#{key}=", value) else base.send("#{key}=", value) if base.attribute_method?(key) end end self end def sorts=(args) case args when Array args.each do |sort| if sort.kind_of? Hash sort = Nodes::Sort.new(@context).build(sort) else sort = Nodes::Sort.extract(@context, sort) end self.sorts << sort end when Hash args.each do |index, attrs| sort = Nodes::Sort.new(@context).build(attrs) self.sorts << sort end when String self.sorts = [args] else raise ArgumentError, "Invalid argument (#{args.class}) supplied to sorts=" end end alias :s= :sorts= def sorts @sorts ||= [] end alias :s :sorts def build_sort(opts = {}) new_sort(opts).tap do |sort| self.sorts << sort end end def new_sort(opts = {}) Nodes::Sort.new(@context).build(opts) end def method_missing(method_id, *args) method_name = method_id.to_s writer = method_name.sub!(/\=$/, '') if base.attribute_method?(method_name) base.send(method_id, *args) else super end end def inspect "Ransack::Search" end private def collapse_multiparameter_attributes!(attrs) attrs.keys.each do |k| if k.include?("(") real_attribute, position = k.split(/\(|\)/) cast = %w(a s i).include?(position.last) ? position.last : nil position = position.to_i - 1 value = attrs.delete(k) attrs[real_attribute] ||= [] attrs[real_attribute][position] = if cast (value.blank? && cast == 'i') ? nil : value.send("to_#{cast}") else value end elsif Hash === attrs[k] collapse_multiparameter_attributes!(attrs[k]) end end attrs end end end