activerecord-hackery--ransack/lib/ransack/adapters/mongoid/context.rb

213 lines
6.8 KiB
Ruby

require 'ransack/context'
require 'polyamorous'
module Ransack
module Adapters
module Mongoid
class Context < ::Ransack::Context
def initialize(object, options = {})
super
# @arel_visitor = @engine.connection.visitor
end
def relation_for(object)
object.all
end
def type_for(attr)
return nil unless attr && attr.valid?
name = attr.arel_attribute.name.to_s.split('.').last
# table = attr.arel_attribute.relation.table_name
# schema_cache = @engine.connection.schema_cache
# raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
# schema_cache.columns_hash(table)[name].type
# when :date
# when :datetime, :timestamp, :time
# when :boolean
# when :integer
# when :float
# when :decimal
# else # :string
name = '_id' if name == 'id'
t = object.klass.fields[name].try(:type) || bind_pair_for(attr.name).first.fields[name].type
t.to_s.demodulize.underscore.to_sym
end
def evaluate(search, opts = {})
viz = Visitor.new
relation = @object.where(viz.accept(search.base))
if search.sorts.any?
ary_sorting = viz.accept(search.sorts)
sorting = {}
ary_sorting.each do |s|
sorting.merge! Hash[s.map { |k, d| [k.to_s == 'id' ? '_id' : k, d] }]
end
relation = relation.order_by(sorting)
# relation = relation.except(:order)
# .reorder(viz.accept(search.sorts))
end
# -- mongoid has different distinct method
# opts[:distinct] ? relation.distinct : relation
relation
end
def attribute_method?(str, klass = @klass)
exists = false
if ransackable_attribute?(str, klass)
exists = true
elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
remainder = []
found_assoc = nil
while !found_assoc && remainder.unshift(
segments.pop) && segments.size > 0 do
assoc, poly_class = unpolymorphize_association(
segments.join('_')
)
if found_assoc = get_association(assoc, klass)
exists = attribute_method?(remainder.join('_'),
poly_class || found_assoc.klass
)
end
end
end
exists
end
def table_for(parent)
# parent.table
Ransack::Adapters::Mongoid::Table.new(parent)
end
def klassify(obj)
if Class === obj && obj.ancestors.include?(::Mongoid::Document)
obj
elsif obj.respond_to? :klass
obj.klass
elsif obj.respond_to? :base_klass
obj.base_klass
else
raise ArgumentError, "Don't know how to klassify #{obj}"
end
end
def lock_association(association)
warn "lock_association is not implemented for Ransack mongoid adapter" if $DEBUG
end
def remove_association(association)
warn "remove_association is not implemented for Ransack mongoid adapter" if $DEBUG
end
private
def get_parent_and_attribute_name(str, parent = @base)
attr_name = nil
if ransackable_attribute?(str, klassify(parent))
attr_name = str
elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
remainder = []
found_assoc = nil
while remainder.unshift(
segments.pop) && segments.size > 0 && !found_assoc do
assoc, klass = unpolymorphize_association(segments.join('_'))
if found_assoc = get_association(assoc, parent)
parent, attr_name = get_parent_and_attribute_name(
remainder.join('_'), found_assoc.klass
)
attr_name = "#{segments.join('_')}.#{attr_name}"
end
end
end
[parent, attr_name]
end
def get_association(str, parent = @base)
klass = klassify parent
ransackable_association?(str, klass) &&
klass.reflect_on_all_associations_all.detect { |a| a.name.to_s == str }
end
def join_dependency(relation)
if relation.respond_to?(:join_dependency) # Polyamorous enables this
relation.join_dependency
else
build_join_dependency(relation)
end
end
# Checkout active_record/relation/query_methods.rb +build_joins+ for
# reference. Lots of duplicated code maybe we can avoid it
def build_join_dependency(relation)
buckets = relation.joins_values.group_by do |join|
case join
when String
Constants::STRING_JOIN
when Hash, Symbol, Array
Constants::ASSOCIATION_JOIN
when JoinDependency, JoinDependency::JoinAssociation
Constants::STASHED_JOIN
when Arel::Nodes::Join
Constants::JOIN_NODE
else
raise 'unknown class: %s' % join.class.name
end
end
association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
join_nodes = buckets[Constants::JOIN_NODE] || []
string_joins = (buckets[Constants::STRING_JOIN] || [])
.map { |x| x.strip }
.uniq
join_list = relation.send :custom_join_ast,
relation.table.from(relation.table), string_joins
join_dependency = JoinDependency.new(
relation.klass, association_joins, join_list
)
join_nodes.each do |join|
join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
end
join_dependency # ActiveRecord::Associations::JoinDependency
end
# ActiveRecord method
def build_or_find_association(name, parent = @base, klass = nil)
found_association = @join_dependency.join_associations
.detect do |assoc|
assoc.reflection.name == name &&
assoc.parent == parent &&
(!klass || assoc.reflection.klass == klass)
end
unless found_association
@join_dependency.send(
:build,
Polyamorous::Join.new(name, @join_type, klass),
parent
)
found_association = @join_dependency.join_associations.last
# Leverage the stashed association functionality in AR
@object = @object.joins(found_association)
end
found_association
end
end
end
end
end