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

209 lines
6.6 KiB
Ruby
Raw Normal View History

2014-08-01 06:42:22 +00:00
require 'ransack/context'
require 'polyamorous'
module Ransack
module Adapters
module Mongoid
class Context < ::Ransack::Context
# Because the AR::Associations namespace is insane
2014-08-01 09:05:25 +00:00
# JoinDependency = ::Mongoid::Associations::JoinDependency
# JoinPart = JoinDependency::JoinPart
2014-08-01 06:42:22 +00:00
def initialize(object, options = {})
super
# @arel_visitor = @engine.connection.visitor
2014-08-01 06:42:22 +00:00
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
# table = attr.arel_attribute.relation.table_name
2014-08-01 06:42:22 +00:00
# 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
2014-11-02 19:14:37 +00:00
name = '_id' if name == 'id'
t = object.klass.fields[name].type
t.to_s.demodulize.underscore.to_sym
2014-08-01 06:42:22 +00:00
end
def evaluate(search, opts = {})
viz = Visitor.new
relation = @object.where(viz.accept(search.base))
if search.sorts.any?
2014-08-02 17:36:23 +00:00
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))
2014-08-01 06:42:22 +00:00
end
2014-08-02 17:36:23 +00:00
# -- mongoid has different distinct method
# opts[:distinct] ? relation.distinct : relation
relation
2014-08-01 06:42:22 +00:00
end
def attribute_method?(str, klass = @klass)
exists = false
if ransackable_attribute?(str, klass)
exists = true
elsif (segments = str.split(/_/)).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)
2014-08-01 06:42:22 +00:00
end
def klassify(obj)
if Class === obj && obj.ancestors.include?(::Mongoid::Document)
2014-08-01 06:42:22 +00:00
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
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(/_/)).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)
join = build_or_find_association(found_assoc.name, parent, klass)
parent, attr_name = get_parent_and_attribute_name(
remainder.join('_'), join
)
end
end
end
[parent, attr_name]
end
def get_association(str, parent = @base)
klass = klassify parent
ransackable_association?(str, klass) &&
2014-08-02 17:36:23 +00:00
klass.reflect_on_all_associations_all.detect { |a| a.name.to_s == str }
2014-08-01 06:42:22 +00:00
end
def join_dependency(relation)
if relation.respond_to?(:join_dependency) # Squeel will enable this
relation.join_dependency
else
build_join_dependency(relation)
end
end
# Checkout active_record/relation/query_methods.rb +build_joins+ for
2014-08-01 06:42:22 +00:00
# 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
2014-11-21 19:03:20 +00:00
Constants::STRING_JOIN
2014-08-01 06:42:22 +00:00
when Hash, Symbol, Array
2014-11-21 19:03:20 +00:00
Constants::ASSOCIATION_JOIN
2014-08-01 06:42:22 +00:00
when JoinDependency, JoinDependency::JoinAssociation
2014-11-21 19:03:20 +00:00
Constants::STASHED_JOIN
2014-08-01 06:42:22 +00:00
when Arel::Nodes::Join
2014-11-21 19:03:20 +00:00
Constants::JOIN_NODE
2014-08-01 06:42:22 +00:00
else
raise 'unknown class: %s' % join.class.name
end
end
2014-11-21 19:03:20 +00:00
association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
2014-08-01 06:42:22 +00:00
2014-11-21 19:03:20 +00:00
stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
2014-08-01 06:42:22 +00:00
2014-11-21 19:03:20 +00:00
join_nodes = buckets[Constants::JOIN_NODE] || []
2014-08-01 06:42:22 +00:00
2014-11-21 19:03:20 +00:00
string_joins = (buckets[Constants::STRING_JOIN] || [])
.map { |x| x.strip }
.uniq
2014-08-01 06:42:22 +00:00
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
2014-08-02 17:36:23 +00:00
join_dependency # ActiveRecord::Associations::JoinDependency
2014-08-01 06:42:22 +00:00
end
2014-08-02 17:36:23 +00:00
# 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)
2014-08-01 06:42:22 +00:00
end
2014-08-02 17:36:23 +00:00
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)
2014-08-01 06:42:22 +00:00
end
2014-08-02 17:36:23 +00:00
found_association
2014-08-01 06:42:22 +00:00
end
end
end
end
end