WIP: move away from contextualize, toward bind

This commit is contained in:
Ernie Miller 2011-04-09 20:55:28 -04:00
parent 10d720104d
commit ecd42f4a83
9 changed files with 105 additions and 29 deletions

View File

@ -11,6 +11,18 @@ module Ransack
Search.new(self, params)
end
def ransacker(name, opts = {}, &block)
unless method_defined?(:_ransackers)
class_attribute :_ransackers
self._ransackers ||= {}
end
opts[:type] ||= :string
opts[:call] ||= block || lambda {|parent| parent.table[name]}
_ransackers[name.to_s] = opts
end
end
end
end

View File

@ -17,7 +17,7 @@ module Ransack
def attribute_method?(str, klass = @klass)
exists = false
if column = get_column(str, klass)
if get_ransacker(str, klass) || get_column(str, klass)
exists = true
elsif (segments = str.split(/_/)).size > 1
remainder = []
@ -33,20 +33,10 @@ module Ransack
exists
end
def type_for(attr)
return nil unless attr
name = attr.name.to_s
table = attr.relation.table_name
unless @engine.connection_pool.table_exists?(table)
raise "No table named #{table} exists"
end
@engine.connection_pool.columns_hash[table][name].type
def table_for(parent)
parent.table
end
private
def klassify(obj)
if Class === obj && ::ActiveRecord::Base > obj
obj
@ -59,11 +49,26 @@ module Ransack
end
end
def get_attribute(str, parent = @base)
attribute = nil
def type_for(attr)
return nil unless attr
name = attr.name.to_s
table = attr.relation.table_name
if column = get_column(str, parent)
attribute = parent.table[str]
unless @engine.connection_pool.table_exists?(table)
raise "No table named #{table} exists"
end
@engine.connection_pool.columns_hash[table][name].type
end
private
def get_parent_and_attribute_name(str, parent = @base)
attr_name = nil
if get_ransacker(str, parent) || get_column(str, parent)
attr_name = str
elsif (segments = str.split(/_/)).size > 1
remainder = []
found_assoc = nil
@ -71,12 +76,17 @@ module Ransack
assoc, klass = unpolymorphize_association(segments.join('_'))
if found_assoc = get_association(assoc, parent)
join = build_or_find_association(found_assoc.name, parent, klass)
attribute = get_attribute(remainder.join('_'), join)
parent, attr_name = get_parent_and_attribute_name(remainder.join('_'), join)
end
end
end
attribute
[parent, attr_name]
end
def get_ransacker(str, parent = @base)
klass = klassify(parent)
klass._ransackers[str] if klass.respond_to?(:_ransackers)
end
def get_column(str, parent = @base)

View File

@ -36,9 +36,10 @@ module Ransack
@engine = @base.arel_engine
@arel_visitor = Arel::Visitors.visitor_for @engine
@default_table = Arel::Table.new(@base.table_name, :as => @base.aliased_table_name, :engine => @engine)
@attributes = Hash.new do |hash, key|
if attribute = get_attribute(key.to_s)
hash[key] = attribute
@bind_pairs = Hash.new do |hash, key|
parent, attr_name = get_parent_and_attribute_name(key.to_s)
if parent && attr_name
hash[key] = [parent, attr_name]
end
end
end
@ -46,7 +47,12 @@ module Ransack
# Convert a string representing a chain of associations and an attribute
# into the attribute itself
def contextualize(str)
@attributes[str]
parent, attr_name = @bind_pairs[str]
table_for(parent)[attr_name]
end
def bind(object, str)
object.parent, object.attr_name = @bind_pairs[str]
end
def traverse(str, base = @base)

View File

@ -1,3 +1,4 @@
require 'ransack/nodes/bindable'
require 'ransack/nodes/node'
require 'ransack/nodes/attribute'
require 'ransack/nodes/value'

View File

@ -1,8 +1,12 @@
module Ransack
module Nodes
class Attribute < Node
attr_reader :name, :attr
include Bindable
attr_reader :name
delegate :blank?, :==, :to => :name
delegate :engine, :to => :context
def initialize(context, name = nil)
super(context)
@ -11,11 +15,23 @@ module Ransack
def name=(name)
@name = name
@attr = contextualize(name) unless name.blank?
context.bind(self, name) unless name.blank?
end
def valid?
@attr
attr
end
def ransacker
klass._ransackers[attr_name] if klass.respond_to?(:_ransackers)
end
def type
if ransacker
return ransacker[:type]
else
context.type_for(attr)
end
end
def eql?(other)

View File

@ -0,0 +1,21 @@
module Ransack
module Nodes
module Bindable
attr_accessor :parent, :attr_name
def attr
@attr ||= context.table_for(parent)[attr_name]
end
def klass
@klass ||= context.klassify(parent)
end
def reset_binding!
@parent = @attr_name = @attr = @klass = nil
end
end
end
end

View File

@ -191,7 +191,7 @@ module Ransack
if predicate && predicate.type
predicate.type
elsif attributes.size > 0
@context.type_for(attributes.first.attr)
attributes.first.type
end
end

View File

@ -1,6 +1,8 @@
module Ransack
module Nodes
class Sort < Node
include Bindable
attr_reader :name, :attr, :dir
i18n_word :asc, :desc
@ -22,12 +24,12 @@ module Ransack
end
def valid?
@attr
attr
end
def name=(name)
@name = name
@attr = contextualize(name) unless name.blank?
context.bind(self, name) unless name.blank?
end
def dir=(dir)

View File

@ -24,6 +24,14 @@ module Ransack
end
end
describe '#ransacker' do
it 'creates ransack attributes' do
ancestors = Person.singleton_class.ancestors.size
Person.ransacker :backwards_name
s = Person.search(:backwards_name_eq => 'blah')
end
end
end
end
end