✂️ old rails support
This commit is contained in:
parent
85fc41b329
commit
85664cb394
|
@ -19,9 +19,6 @@ env:
|
|||
- RAILS=5-0-stable DB=mysql
|
||||
- RAILS=5-0-stable DB=postgres
|
||||
|
||||
- RAILS=4-2-stable DB=sqlite3
|
||||
- RAILS=4-2-stable DB=mysql
|
||||
- RAILS=4-2-stable DB=postgres
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: RAILS=5-2-stable DB=sqlite3
|
||||
|
|
|
@ -24,28 +24,15 @@ if defined?(::ActiveRecord)
|
|||
require 'polyamorous/swapping_reflection_class'
|
||||
|
||||
ar_version = ::ActiveRecord::VERSION::STRING[0,3]
|
||||
ar_version = '3_and_4.0' if ar_version < '4.1'
|
||||
ar_version = ::ActiveRecord::VERSION::STRING[0,5] if ar_version == '5.2'
|
||||
|
||||
method, ruby_version =
|
||||
if RUBY_VERSION >= '2.0' && ar_version >= '4.1'
|
||||
# Ruby 2; we can use `prepend` to patch Active Record cleanly.
|
||||
[:prepend, '2']
|
||||
else
|
||||
# Ruby 1.9; we must use `alias_method` to patch Active Record.
|
||||
[:include, '1.9']
|
||||
end
|
||||
|
||||
%w(join_association join_dependency).each do |file|
|
||||
require "polyamorous/activerecord_#{ar_version}_ruby_#{ruby_version}/#{file}"
|
||||
require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
|
||||
end
|
||||
|
||||
Polyamorous::JoinDependency.send(method, Polyamorous::JoinDependencyExtensions)
|
||||
if method == :prepend
|
||||
Polyamorous::JoinDependency.singleton_class
|
||||
.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
|
||||
end
|
||||
Polyamorous::JoinAssociation.send(method, Polyamorous::JoinAssociationExtensions)
|
||||
Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
|
||||
Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
|
||||
Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions)
|
||||
|
||||
Polyamorous::JoinBase.class_eval do
|
||||
if method_defined?(:active_record)
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
# active_record_3_and_4.0_ruby_1.9/join_association.rb
|
||||
module Polyamorous
|
||||
module JoinAssociationExtensions
|
||||
include SwappingReflectionClass
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
alias_method_chain :initialize, :polymorphism
|
||||
alias_method :equality_without_polymorphism, :==
|
||||
alias_method :==, :equality_with_polymorphism
|
||||
if base.method_defined?(:active_record)
|
||||
alias_method :base_klass, :active_record
|
||||
end
|
||||
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.0\./
|
||||
alias_method_chain :association_join, :polymorphism
|
||||
else
|
||||
alias_method_chain :build_constraint, :polymorphism
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_with_polymorphism(
|
||||
reflection, join_dependency, parent = nil, polymorphic_class = nil
|
||||
)
|
||||
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
||||
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
||||
initialize_without_polymorphism(reflection, join_dependency, parent)
|
||||
self.reflection.options[:polymorphic] = true
|
||||
end
|
||||
else
|
||||
initialize_without_polymorphism(reflection, join_dependency, parent)
|
||||
end
|
||||
end
|
||||
|
||||
def equality_with_polymorphism(other)
|
||||
equality_without_polymorphism(other) && base_klass == other.base_klass
|
||||
end
|
||||
|
||||
def build_constraint_with_polymorphism(
|
||||
reflection, table, key, foreign_table, foreign_key
|
||||
)
|
||||
if reflection.options[:polymorphic]
|
||||
build_constraint_without_polymorphism(
|
||||
reflection, table, key, foreign_table, foreign_key
|
||||
)
|
||||
.and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
|
||||
else
|
||||
build_constraint_without_polymorphism(
|
||||
reflection, table, key, foreign_table, foreign_key
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def association_join_with_polymorphism
|
||||
return @join if @Join
|
||||
@join = association_join_without_polymorphism
|
||||
if reflection.macro == :belongs_to && reflection.options[:polymorphic]
|
||||
aliased_table = Arel::Table.new(
|
||||
table_name,
|
||||
as: @aliased_table_name,
|
||||
engine: arel_engine,
|
||||
columns: klass.columns
|
||||
)
|
||||
parent_table = Arel::Table.new(
|
||||
parent.table_name,
|
||||
as: parent.aliased_table_name,
|
||||
engine: arel_engine,
|
||||
columns: parent.base_klass.columns
|
||||
)
|
||||
@join << parent_table[reflection.options[:foreign_type]]
|
||||
.eq(reflection.klass.name)
|
||||
end
|
||||
@join
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,96 +0,0 @@
|
|||
# active_record_3_and_4.0_ruby_1.9/join_dependency.rb
|
||||
module Polyamorous
|
||||
module JoinDependencyExtensions
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
alias_method_chain :build, :polymorphism
|
||||
alias_method_chain :graft, :polymorphism
|
||||
if base.method_defined?(:active_record)
|
||||
alias_method :base_klass, :active_record
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def graft_with_polymorphism(*associations)
|
||||
associations.each do |association|
|
||||
unless join_associations.detect { |a| association == a }
|
||||
if association.reflection.options[:polymorphic]
|
||||
build(
|
||||
Join.new(
|
||||
association.reflection.name,
|
||||
association.join_type,
|
||||
association.reflection.klass
|
||||
),
|
||||
association.find_parent_in(self) || join_base,
|
||||
association.join_type
|
||||
)
|
||||
else
|
||||
build(
|
||||
association.reflection.name,
|
||||
association.find_parent_in(self) || join_base,
|
||||
association.join_type
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.0\./
|
||||
def _join_parts
|
||||
@joins
|
||||
end
|
||||
else
|
||||
def _join_parts
|
||||
@join_parts
|
||||
end
|
||||
end
|
||||
|
||||
def build_with_polymorphism(
|
||||
associations, parent = nil, join_type = InnerJoin
|
||||
)
|
||||
case associations
|
||||
when Join
|
||||
parent ||= _join_parts.last
|
||||
reflection = parent.reflections[associations.name] or
|
||||
raise ::ActiveRecord::ConfigurationError,
|
||||
"Association named '#{associations.name
|
||||
}' was not found; perhaps you misspelled it?"
|
||||
|
||||
unless join_association = find_join_association_respecting_polymorphism(
|
||||
reflection, parent, associations.klass
|
||||
)
|
||||
@reflections << reflection
|
||||
join_association = build_join_association_respecting_polymorphism(
|
||||
reflection, parent, associations.klass
|
||||
)
|
||||
join_association.join_type = associations.type
|
||||
_join_parts << join_association
|
||||
cache_joined_association(join_association)
|
||||
end
|
||||
|
||||
join_association
|
||||
else
|
||||
build_without_polymorphism(associations, parent, join_type)
|
||||
end
|
||||
end
|
||||
|
||||
def find_join_association_respecting_polymorphism(reflection, parent, klass)
|
||||
if association = find_join_association(reflection, parent)
|
||||
unless reflection.options[:polymorphic]
|
||||
association
|
||||
else
|
||||
association if association.base_klass == klass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_join_association_respecting_polymorphism(reflection, parent, klass)
|
||||
if reflection.options[:polymorphic] && klass
|
||||
JoinAssociation.new(reflection, self, parent, klass)
|
||||
else
|
||||
JoinAssociation.new(reflection, self, parent)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
# active_record_4.1_ruby_1.9/join_association.rb
|
||||
require 'polyamorous/activerecord_4.2_ruby_1.9/join_association'
|
|
@ -1,4 +0,0 @@
|
|||
# active_record_4.1_ruby_1.9/join_dependency.rb
|
||||
require 'polyamorous/activerecord_4.2_ruby_2/join_dependency'
|
||||
require 'polyamorous/activerecord_4.2_ruby_1.9/join_dependency'
|
||||
require 'polyamorous/activerecord_4.1_ruby_2/make_polyamorous_inner_joins'
|
|
@ -1,2 +0,0 @@
|
|||
# active_record_4.1_ruby_2/join_association.rb
|
||||
require 'polyamorous/activerecord_5.0_ruby_2/join_association'
|
|
@ -1,3 +0,0 @@
|
|||
# active_record_4.1_ruby_2/join_dependency.rb
|
||||
require 'polyamorous/activerecord_4.2_ruby_2/join_dependency'
|
||||
require 'polyamorous/activerecord_4.1_ruby_2/make_polyamorous_inner_joins'
|
|
@ -1,14 +0,0 @@
|
|||
module Polyamorous
|
||||
module JoinDependencyExtensions
|
||||
# Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
|
||||
#
|
||||
def make_polyamorous_inner_joins(parent, child)
|
||||
make_constraints(
|
||||
parent, child, child.tables, child.join_type || Arel::Nodes::InnerJoin
|
||||
)
|
||||
.concat child.children.flat_map { |c|
|
||||
make_polyamorous_inner_joins(child, c)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# active_record_4.2_ruby_1.9/join_association.rb
|
||||
module Polyamorous
|
||||
module JoinAssociationExtensions
|
||||
include SwappingReflectionClass
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
attr_reader :join_type
|
||||
alias_method_chain :initialize, :polymorphism
|
||||
alias_method_chain :build_constraint, :polymorphism
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_with_polymorphism(reflection, children,
|
||||
polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
|
||||
@join_type = join_type
|
||||
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
||||
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
||||
initialize_without_polymorphism(reflection, children)
|
||||
self.reflection.options[:polymorphic] = true
|
||||
end
|
||||
else
|
||||
initialize_without_polymorphism(reflection, children)
|
||||
end
|
||||
end
|
||||
|
||||
# Reference https://github.com/rails/rails/commit/9b15db51b78028bfecdb85595624de4b838adbd1
|
||||
def ==(other)
|
||||
base_klass == other.base_klass
|
||||
end
|
||||
|
||||
def build_constraint_with_polymorphism(
|
||||
klass, table, key, foreign_table, foreign_key
|
||||
)
|
||||
if reflection.polymorphic?
|
||||
build_constraint_without_polymorphism(
|
||||
klass, table, key, foreign_table, foreign_key
|
||||
)
|
||||
.and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
|
||||
else
|
||||
build_constraint_without_polymorphism(
|
||||
klass, table, key, foreign_table, foreign_key
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,87 +0,0 @@
|
|||
# active_record_4.2_ruby_1.9/join_dependency.rb
|
||||
require 'polyamorous/activerecord_4.2_ruby_2/join_dependency'
|
||||
|
||||
module Polyamorous
|
||||
module JoinDependencyExtensions
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
base.class_eval do
|
||||
class << self
|
||||
alias_method :walk_tree_without_polymorphism, :walk_tree
|
||||
alias_method :walk_tree, :walk_tree_with_polymorphism
|
||||
end
|
||||
|
||||
alias_method :build_without_polymorphism, :build
|
||||
alias_method :build, :build_with_polymorphism
|
||||
|
||||
alias_method :join_constraints_without_polymorphism, :join_constraints
|
||||
alias_method :join_constraints, :join_constraints_with_polymorphism
|
||||
end
|
||||
end
|
||||
|
||||
# Replaces ActiveRecord::Associations::JoinDependency#build
|
||||
#
|
||||
def build_with_polymorphism(associations, base_klass)
|
||||
associations.map do |name, right|
|
||||
if name.is_a? Join
|
||||
reflection = find_reflection base_klass, name.name
|
||||
reflection.check_validity!
|
||||
klass = if reflection.polymorphic?
|
||||
name.klass || base_klass
|
||||
else
|
||||
reflection.klass
|
||||
end
|
||||
JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
|
||||
else
|
||||
reflection = find_reflection base_klass, name
|
||||
reflection.check_validity!
|
||||
if reflection.polymorphic?
|
||||
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
||||
end
|
||||
JoinAssociation.new reflection, build(right, reflection.klass)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Replaces ActiveRecord::Associations::JoinDependency#join_constraints
|
||||
# to call #make_polyamorous_inner_joins instead of #make_inner_joins
|
||||
#
|
||||
def join_constraints_with_polymorphism(outer_joins)
|
||||
joins = join_root.children.flat_map { |child|
|
||||
make_polyamorous_inner_joins join_root, child
|
||||
}
|
||||
joins.concat outer_joins.flat_map { |oj|
|
||||
if join_root.match? oj.join_root
|
||||
walk(join_root, oj.join_root)
|
||||
else
|
||||
oj.join_root.children.flat_map { |child|
|
||||
make_outer_joins(oj.join_root, child)
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Replaces ActiveRecord::Associations::JoinDependency#self.walk_tree
|
||||
#
|
||||
def walk_tree_with_polymorphism(associations, hash)
|
||||
case associations
|
||||
when TreeNode
|
||||
associations.add_to_tree(hash)
|
||||
when Hash
|
||||
associations.each do |k, v|
|
||||
cache =
|
||||
if TreeNode === k
|
||||
k.add_to_tree(hash)
|
||||
else
|
||||
hash[k] ||= {}
|
||||
end
|
||||
walk_tree(v, cache)
|
||||
end
|
||||
else
|
||||
walk_tree_without_polymorphism(associations, hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
# active_record_4.2_ruby_2/join_association.rb
|
||||
require 'polyamorous/activerecord_5.0_ruby_2/join_association'
|
|
@ -1,24 +0,0 @@
|
|||
# active_record_4.2_ruby_2/join_dependency.rb
|
||||
require 'polyamorous/activerecord_5.0_ruby_2/join_dependency'
|
||||
|
||||
module Polyamorous
|
||||
module JoinDependencyExtensions
|
||||
# Replaces ActiveRecord::Associations::JoinDependency#join_constraints
|
||||
# to call #make_polyamorous_inner_joins instead of #make_inner_joins.
|
||||
#
|
||||
def join_constraints(outer_joins)
|
||||
joins = join_root.children.flat_map { |child|
|
||||
make_polyamorous_inner_joins join_root, child
|
||||
}
|
||||
joins.concat outer_joins.flat_map { |oj|
|
||||
if join_root.match? oj.join_root
|
||||
walk(join_root, oj.join_root)
|
||||
else
|
||||
oj.join_root.children.flat_map { |child|
|
||||
make_outer_joins(oj.join_root, child)
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,7 @@ module Polyamorous
|
|||
if name.is_a? Join
|
||||
reflection = find_reflection base_klass, name.name
|
||||
reflection.check_validity!
|
||||
reflection.check_eager_loadable! if ActiveRecord::VERSION::MAJOR >= 5
|
||||
reflection.check_eager_loadable!
|
||||
|
||||
klass = if reflection.polymorphic?
|
||||
name.klass || base_klass
|
||||
|
@ -20,7 +20,7 @@ module Polyamorous
|
|||
else
|
||||
reflection = find_reflection base_klass, name
|
||||
reflection.check_validity!
|
||||
reflection.check_eager_loadable! if ActiveRecord::VERSION::MAJOR >= 5
|
||||
reflection.check_eager_loadable!
|
||||
|
||||
if reflection.polymorphic?
|
||||
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
||||
|
|
|
@ -9,7 +9,7 @@ module Polyamorous
|
|||
if name.is_a? Join
|
||||
reflection = find_reflection base_klass, name.name
|
||||
reflection.check_validity!
|
||||
reflection.check_eager_loadable! if ActiveRecord::VERSION::MAJOR >= 5
|
||||
reflection.check_eager_loadable!
|
||||
|
||||
klass = if reflection.polymorphic?
|
||||
name.klass || base_klass
|
||||
|
@ -20,7 +20,7 @@ module Polyamorous
|
|||
else
|
||||
reflection = find_reflection base_klass, name
|
||||
reflection.check_validity!
|
||||
reflection.check_eager_loadable! if ActiveRecord::VERSION::MAJOR >= 5
|
||||
reflection.check_eager_loadable!
|
||||
|
||||
if reflection.polymorphic?
|
||||
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
||||
|
|
|
@ -12,12 +12,3 @@ ActiveSupport.on_load(:active_record) do
|
|||
end
|
||||
|
||||
require 'ransack/adapters/active_record/context'
|
||||
|
||||
case ActiveRecord::VERSION::STRING
|
||||
when /^3\.0\./
|
||||
require 'ransack/adapters/active_record/3.0/context'
|
||||
when /^3\.1\./
|
||||
require 'ransack/adapters/active_record/3.1/context'
|
||||
when /^3\.2\./
|
||||
require 'ransack/adapters/active_record/3.2/context'
|
||||
end
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
# UGLY, UGLY MONKEY PATCHES FOR BACKWARDS COMPAT!!! AVERT YOUR EYES!!
|
||||
if Arel::Nodes::And < Arel::Nodes::Binary
|
||||
class Ransack::Visitor
|
||||
def visit_Ransack_Nodes_And(object)
|
||||
nodes = object.values.map { |o| accept(o) }.compact
|
||||
nodes.inject(&:and)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
|
||||
def table
|
||||
Arel::Table.new(
|
||||
table_name,
|
||||
:as => aliased_table_name,
|
||||
:engine => active_record.arel_engine,
|
||||
:columns => active_record.columns
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
module Arel
|
||||
|
||||
class Table
|
||||
alias :table_name :name
|
||||
|
||||
def [] name
|
||||
::Arel::Attribute.new self, name.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
module Nodes
|
||||
class Node
|
||||
def not
|
||||
Nodes::Not.new self
|
||||
end
|
||||
end
|
||||
|
||||
remove_const :And
|
||||
class And < Arel::Nodes::Node
|
||||
attr_reader :children
|
||||
|
||||
def initialize children, right = nil
|
||||
unless Array === children
|
||||
children = [children, right]
|
||||
end
|
||||
@children = children
|
||||
end
|
||||
|
||||
def left
|
||||
children.first
|
||||
end
|
||||
|
||||
def right
|
||||
children[1]
|
||||
end
|
||||
end
|
||||
|
||||
class NamedFunction < Arel::Nodes::Function
|
||||
attr_accessor :name, :distinct
|
||||
|
||||
include Arel::Predications
|
||||
|
||||
def initialize name, expr, aliaz = nil
|
||||
super(expr, aliaz)
|
||||
@name = name
|
||||
@distinct = false
|
||||
end
|
||||
end
|
||||
|
||||
class InfixOperation < Binary
|
||||
include Arel::Expressions
|
||||
include Arel::Predications
|
||||
|
||||
attr_reader :operator
|
||||
|
||||
def initialize operator, left, right
|
||||
super(left, right)
|
||||
@operator = operator
|
||||
end
|
||||
end
|
||||
|
||||
class Multiplication < InfixOperation
|
||||
def initialize left, right
|
||||
super(:*, left, right)
|
||||
end
|
||||
end
|
||||
|
||||
class Division < InfixOperation
|
||||
def initialize left, right
|
||||
super(:/, left, right)
|
||||
end
|
||||
end
|
||||
|
||||
class Addition < InfixOperation
|
||||
def initialize left, right
|
||||
super(:+, left, right)
|
||||
end
|
||||
end
|
||||
|
||||
class Subtraction < InfixOperation
|
||||
def initialize left, right
|
||||
super(:-, left, right)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Visitors
|
||||
class ToSql
|
||||
def column_for attr
|
||||
name = attr.name.to_s
|
||||
table = attr.relation.table_name
|
||||
|
||||
column_cache[table][name]
|
||||
end
|
||||
|
||||
def column_cache
|
||||
@column_cache ||= Hash.new do |hash, key|
|
||||
hash[key] = Hash[
|
||||
@engine.connection
|
||||
.columns(key, "#{key} Columns")
|
||||
.map { |c| [c.name, c] }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_InfixOperation o
|
||||
"#{visit o.left} #{o.operator} #{visit o.right}"
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_NamedFunction o
|
||||
"#{
|
||||
o.name
|
||||
}(#{
|
||||
o.distinct ? Ransack::Constants::DISTINCT : ''.freeze
|
||||
}#{
|
||||
o.expressions.map { |x| visit x }.join(', '.freeze)
|
||||
})#{
|
||||
o.alias ? " AS #{visit o.alias}" : ''.freeze
|
||||
}"
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_And o
|
||||
o.children.map { |x| visit x }.join(' AND '.freeze)
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_Not o
|
||||
"NOT (#{visit o.expr})"
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_Values o
|
||||
"VALUES (#{
|
||||
o.expressions.zip(o.columns)
|
||||
.map { |value, attr|
|
||||
if Nodes::SqlLiteral === value
|
||||
visit_Arel_Nodes_SqlLiteral value
|
||||
else
|
||||
quote(value, attr && column_for(attr))
|
||||
end
|
||||
}
|
||||
.join(', '.freeze)
|
||||
})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Predications
|
||||
def as other
|
||||
Nodes::As.new self, Nodes::SqlLiteral.new(other)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,203 +0,0 @@
|
|||
require 'ransack/context'
|
||||
require 'polyamorous'
|
||||
require 'ransack/adapters/active_record/3.0/compat'
|
||||
|
||||
module Ransack
|
||||
|
||||
module Adapters
|
||||
module ActiveRecord
|
||||
class Context < ::Ransack::Context
|
||||
|
||||
# Because the AR::Associations namespace is insane
|
||||
if defined? ::ActiveRecord::Associations::ClassMethods::JoinDependency
|
||||
JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
|
||||
end
|
||||
|
||||
# Redefine a few things for ActiveRecord 3.0.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
@arel_visitor = Arel::Visitors.visitor_for @engine
|
||||
end
|
||||
|
||||
def relation_for(object)
|
||||
object.scoped
|
||||
end
|
||||
|
||||
def evaluate(search, opts = {})
|
||||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
relation = relation.except(:order)
|
||||
.reorder(viz.accept(search.sorts))
|
||||
end
|
||||
if opts[:distinct]
|
||||
relation.select(Constants::DISTINCT + @klass.quoted_table_name +
|
||||
Constants::DOT_ASTERIX)
|
||||
else
|
||||
relation
|
||||
end
|
||||
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(Constants::UNDERSCORE)
|
||||
)
|
||||
if found_assoc = get_association(assoc, klass)
|
||||
exists = attribute_method?(
|
||||
remainder.join(Constants::UNDERSCORE),
|
||||
poly_class || found_assoc.klass
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
exists
|
||||
end
|
||||
|
||||
def table_for(parent)
|
||||
parent.table
|
||||
end
|
||||
|
||||
def type_for(attr)
|
||||
return nil unless attr && attr.valid?
|
||||
klassify(attr.parent)
|
||||
.columns_hash[attr.arel_attribute.name.to_s]
|
||||
.type
|
||||
end
|
||||
|
||||
# All dependent JoinAssociation items used in the search query
|
||||
#
|
||||
def join_associations
|
||||
@join_dependency.join_associations
|
||||
end
|
||||
|
||||
def join_sources
|
||||
raise NotImplementedError,
|
||||
"ActiveRecord 3.0 does not use join_sources or support joining relations with Arel::Join nodes. Use join_associations."
|
||||
end
|
||||
|
||||
def alias_tracker
|
||||
raise NotImplementedError,
|
||||
"ActiveRecord 3.0 does not have an alias tracker"
|
||||
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(Constants::UNDERSCORE)
|
||||
)
|
||||
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(Constants::UNDERSCORE), join
|
||||
)
|
||||
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.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
|
||||
|
||||
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 ::ActiveRecord::Associations::ClassMethods::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(&:strip).uniq
|
||||
|
||||
join_list = relation.send :custom_join_sql, (string_joins + join_nodes)
|
||||
|
||||
join_dependency = JoinDependency.new(
|
||||
relation.klass,
|
||||
association_joins,
|
||||
join_list
|
||||
)
|
||||
|
||||
join_nodes.each do |join|
|
||||
join_dependency.table_aliases[join.left.name.downcase] = 1
|
||||
end
|
||||
|
||||
join_dependency.graft(*stashed_association_joins)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
default_conditions = found_association.active_record.scoped.arel.constraints
|
||||
if default_conditions.any?
|
||||
and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
|
||||
end
|
||||
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(found_association).joins(and_default_conditions)
|
||||
end
|
||||
|
||||
found_association
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,212 +0,0 @@
|
|||
require 'ransack/context'
|
||||
require 'ransack/adapters/active_record/compat'
|
||||
require 'polyamorous'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module ActiveRecord
|
||||
class Context < ::Ransack::Context
|
||||
|
||||
# Redefine a few things for ActiveRecord 3.1.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
@arel_visitor = Arel::Visitors.visitor_for @engine
|
||||
end
|
||||
|
||||
def relation_for(object)
|
||||
object.scoped
|
||||
end
|
||||
|
||||
def evaluate(search, opts = {})
|
||||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
relation = relation.except(:order)
|
||||
.reorder(viz.accept(search.sorts))
|
||||
end
|
||||
if opts[:distinct]
|
||||
relation.select(Constants::DISTINCT + @klass.quoted_table_name +
|
||||
Constants::DOT_ASTERIX)
|
||||
else
|
||||
relation
|
||||
end
|
||||
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(Constants::UNDERSCORE)
|
||||
)
|
||||
if found_assoc = get_association(assoc, klass)
|
||||
exists = attribute_method?(
|
||||
remainder.join(Constants::UNDERSCORE),
|
||||
poly_class || found_assoc.klass
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
exists
|
||||
end
|
||||
|
||||
def table_for(parent)
|
||||
parent.table
|
||||
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
|
||||
|
||||
unless @engine.connection_pool.table_exists?(table)
|
||||
raise "No table named #{table} exists"
|
||||
end
|
||||
|
||||
@engine.connection_pool.columns_hash[table][name].type
|
||||
end
|
||||
|
||||
def join_associations
|
||||
@join_dependency.join_associations
|
||||
end
|
||||
|
||||
# All dependent Arel::Join nodes used in the search query
|
||||
#
|
||||
# This could otherwise be done as `@object.arel.join_sources`, except
|
||||
# that ActiveRecord's build_joins sets up its own JoinDependency.
|
||||
# This extracts what we need to access the joins using our existing
|
||||
# JoinDependency to track table aliases.
|
||||
#
|
||||
def join_sources
|
||||
base = Arel::SelectManager.new(@object.engine, @object.table)
|
||||
@object.joins_values.each do |assoc|
|
||||
next unless assoc.is_a?(JoinDependency::JoinAssociation)
|
||||
assoc.join_to(base)
|
||||
end
|
||||
base.join_sources
|
||||
end
|
||||
|
||||
def alias_tracker
|
||||
@join_dependency.alias_tracker
|
||||
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(Constants::UNDERSCORE)
|
||||
)
|
||||
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(Constants::UNDERSCORE), join
|
||||
)
|
||||
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.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
|
||||
|
||||
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::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(&: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.graft(*stashed_association_joins)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
default_conditions = found_association.active_record.scoped.arel.constraints
|
||||
if default_conditions.any?
|
||||
and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
|
||||
end
|
||||
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(found_association).joins(and_default_conditions)
|
||||
end
|
||||
|
||||
found_association
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
require 'ransack/context'
|
||||
require 'ransack/adapters/active_record/3.1/context'
|
||||
require 'ransack/adapters/active_record/compat'
|
||||
require 'polyamorous'
|
||||
|
||||
module Ransack
|
||||
module Adapters
|
||||
module ActiveRecord
|
||||
class Context < ::Ransack::Context
|
||||
|
||||
# Redefine a few things for ActiveRecord 3.2.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
@arel_visitor = @engine.connection.visitor
|
||||
end
|
||||
|
||||
def relation_for(object)
|
||||
object.scoped
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
def evaluate(search, opts = {})
|
||||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
||||
end
|
||||
opts[:distinct] ? relation.uniq : relation
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -28,7 +28,7 @@ module Ransack
|
|||
name = attr.arel_attribute.name.to_s
|
||||
table = attr.arel_attribute.relation.table_name
|
||||
schema_cache = self.klass.connection.schema_cache
|
||||
unless schema_cache.send(database_table_exists?, table)
|
||||
unless schema_cache.send(:data_source_exists?, table)
|
||||
raise "No table named #{table} exists."
|
||||
end
|
||||
attr.klass.columns.find { |column| column.name == name }.type
|
||||
|
@ -82,58 +82,30 @@ module Ransack
|
|||
end
|
||||
end
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
||||
|
||||
def join_associations
|
||||
raise NotImplementedError,
|
||||
"ActiveRecord 4.1 and later does not use join_associations. Use join_sources."
|
||||
# All dependent Arel::Join nodes used in the search query.
|
||||
#
|
||||
# This could otherwise be done as `@object.arel.join_sources`, except
|
||||
# that ActiveRecord's build_joins sets up its own JoinDependency.
|
||||
# This extracts what we need to access the joins using our existing
|
||||
# JoinDependency to track table aliases.
|
||||
#
|
||||
def join_sources
|
||||
base, joins =
|
||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
[
|
||||
Arel::SelectManager.new(@object.table),
|
||||
@join_dependency.join_constraints(@object.joins_values, @join_type)
|
||||
]
|
||||
else
|
||||
[
|
||||
Arel::SelectManager.new(@object.engine, @object.table),
|
||||
@join_dependency.join_constraints(@object.joins_values)
|
||||
]
|
||||
end
|
||||
|
||||
# All dependent Arel::Join nodes used in the search query.
|
||||
#
|
||||
# This could otherwise be done as `@object.arel.join_sources`, except
|
||||
# that ActiveRecord's build_joins sets up its own JoinDependency.
|
||||
# This extracts what we need to access the joins using our existing
|
||||
# JoinDependency to track table aliases.
|
||||
#
|
||||
def join_sources
|
||||
base, joins =
|
||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
[
|
||||
Arel::SelectManager.new(@object.table),
|
||||
@join_dependency.join_constraints(@object.joins_values, @join_type)
|
||||
]
|
||||
else
|
||||
[
|
||||
Arel::SelectManager.new(@object.engine, @object.table),
|
||||
@join_dependency.join_constraints(@object.joins_values)
|
||||
]
|
||||
end
|
||||
joins.each do |aliased_join|
|
||||
base.from(aliased_join)
|
||||
end
|
||||
base.join_sources
|
||||
joins.each do |aliased_join|
|
||||
base.from(aliased_join)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
# All dependent JoinAssociation items used in the search query.
|
||||
#
|
||||
# Deprecated: this goes away in ActiveRecord 4.1. Use join_sources.
|
||||
#
|
||||
def join_associations
|
||||
@join_dependency.join_associations
|
||||
end
|
||||
|
||||
def join_sources
|
||||
base = Arel::SelectManager.new(@object.engine, @object.table)
|
||||
joins = @object.joins_values
|
||||
joins.each do |assoc|
|
||||
assoc.join_to(base)
|
||||
end
|
||||
base.join_sources
|
||||
end
|
||||
|
||||
base.join_sources
|
||||
end
|
||||
|
||||
def alias_tracker
|
||||
|
@ -144,22 +116,14 @@ module Ransack
|
|||
@lock_associations << association
|
||||
end
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
||||
def remove_association(association)
|
||||
return if @lock_associations.include?(association)
|
||||
@join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed|
|
||||
stashed.eql?(association)
|
||||
}
|
||||
@object.joins_values.delete_if { |jd|
|
||||
jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
|
||||
}
|
||||
end
|
||||
else
|
||||
def remove_association(association)
|
||||
return if @lock_associations.include?(association)
|
||||
@join_dependency.join_parts.delete(association)
|
||||
@object.joins_values.delete(association)
|
||||
end
|
||||
def remove_association(association)
|
||||
return if @lock_associations.include?(association)
|
||||
@join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed|
|
||||
stashed.eql?(association)
|
||||
}
|
||||
@object.joins_values.delete_if { |jd|
|
||||
jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
|
||||
}
|
||||
end
|
||||
|
||||
# Build an Arel subquery that selects keys for the top query,
|
||||
|
@ -199,14 +163,6 @@ module Ransack
|
|||
|
||||
private
|
||||
|
||||
def database_table_exists?
|
||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
:data_source_exists?
|
||||
else
|
||||
:table_exists?
|
||||
end
|
||||
end
|
||||
|
||||
def get_parent_and_attribute_name(str, parent = @base)
|
||||
attr_name = nil
|
||||
|
||||
|
@ -251,8 +207,7 @@ module Ransack
|
|||
# Checkout active_record/relation/query_methods.rb +build_joins+ for
|
||||
# reference. Lots of duplicated code maybe we can avoid it
|
||||
def build_joins(relation)
|
||||
buckets = relation.joins_values
|
||||
buckets += relation.left_outer_joins_values if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
buckets = relation.joins_values + relation.left_outer_joins_values
|
||||
|
||||
buckets = buckets.group_by do |join|
|
||||
case join
|
||||
|
@ -275,14 +230,7 @@ module Ransack
|
|||
string_joins = buckets[:string_join].map(&:strip)
|
||||
string_joins.uniq!
|
||||
|
||||
join_list =
|
||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
join_nodes +
|
||||
convert_join_strings_to_ast(relation.table, string_joins)
|
||||
else
|
||||
relation.send :custom_join_ast,
|
||||
relation.table.from(relation.table), string_joins
|
||||
end
|
||||
join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
||||
join_dependency = JoinDependency.new(relation.klass, association_joins, join_list)
|
||||
|
@ -296,12 +244,7 @@ module Ransack
|
|||
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
||||
end
|
||||
end
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
||||
join_dependency
|
||||
else
|
||||
join_dependency.graft(*stashed_association_joins)
|
||||
end
|
||||
join_dependency
|
||||
end
|
||||
|
||||
def convert_join_strings_to_ast(table, joins)
|
||||
|
@ -314,109 +257,75 @@ module Ransack
|
|||
find_association(name, parent, klass) or build_association(name, parent, klass)
|
||||
end
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
||||
|
||||
def find_association(name, parent = @base, klass = nil)
|
||||
@join_dependency.instance_variable_get(:@join_root).children.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
(@associations_pot.empty? || @associations_pot[assoc] == parent) &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
def find_association(name, parent = @base, klass = nil)
|
||||
@join_dependency.instance_variable_get(:@join_root).children.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
(@associations_pot.empty? || @associations_pot[assoc] == parent) &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
|
||||
def build_association(name, parent = @base, klass = nil)
|
||||
if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
||||
jd = JoinDependency.new(
|
||||
parent.base_klass,
|
||||
Polyamorous::Join.new(name, @join_type, klass),
|
||||
[]
|
||||
)
|
||||
found_association = jd.join_root.children.last
|
||||
else
|
||||
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
|
||||
jd = JoinDependency.new(
|
||||
parent.base_klass,
|
||||
parent.base_klass.arel_table,
|
||||
Polyamorous::Join.new(name, @join_type, klass),
|
||||
alias_tracker
|
||||
)
|
||||
found_association = jd.instance_variable_get(:@join_root).children.last
|
||||
end
|
||||
|
||||
|
||||
@associations_pot[found_association] = parent
|
||||
|
||||
# TODO maybe we dont need to push associations here, we could loop
|
||||
# through the @associations_pot instead
|
||||
@join_dependency.instance_variable_get(:@join_root).children.push found_association
|
||||
|
||||
# Builds the arel nodes properly for this association
|
||||
@join_dependency.send(
|
||||
:construct_tables!, jd.instance_variable_get(:@join_root), found_association
|
||||
)
|
||||
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(jd)
|
||||
|
||||
found_association
|
||||
end
|
||||
|
||||
def extract_joins(association)
|
||||
parent = @join_dependency.instance_variable_get(:@join_root)
|
||||
reflection = association.reflection
|
||||
join_constraints = if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_1
|
||||
association.join_constraints(
|
||||
parent.table,
|
||||
parent.base_klass,
|
||||
association,
|
||||
Arel::Nodes::OuterJoin,
|
||||
association.tables,
|
||||
reflection.scope_chain,
|
||||
reflection.chain
|
||||
)
|
||||
else
|
||||
association.join_constraints(
|
||||
parent.table,
|
||||
parent.base_klass,
|
||||
Arel::Nodes::OuterJoin,
|
||||
association.tables,
|
||||
reflection.chain
|
||||
)
|
||||
end
|
||||
join_constraints.to_a.flatten
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
def build_association(name, parent = @base, klass = nil)
|
||||
@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)
|
||||
|
||||
found_association
|
||||
end
|
||||
|
||||
def extract_joins(association)
|
||||
query = Arel::SelectManager.new(association.base_klass, association.table)
|
||||
association.join_to(query).join_sources
|
||||
end
|
||||
|
||||
def find_association(name, parent = @base, klass = nil)
|
||||
@join_dependency.join_associations
|
||||
.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
assoc.parent == parent &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def build_association(name, parent = @base, klass = nil)
|
||||
if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
||||
jd = JoinDependency.new(
|
||||
parent.base_klass,
|
||||
Polyamorous::Join.new(name, @join_type, klass),
|
||||
[]
|
||||
)
|
||||
found_association = jd.join_root.children.last
|
||||
else
|
||||
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
|
||||
jd = JoinDependency.new(
|
||||
parent.base_klass,
|
||||
parent.base_klass.arel_table,
|
||||
Polyamorous::Join.new(name, @join_type, klass),
|
||||
alias_tracker
|
||||
)
|
||||
found_association = jd.instance_variable_get(:@join_root).children.last
|
||||
end
|
||||
|
||||
|
||||
@associations_pot[found_association] = parent
|
||||
|
||||
# TODO maybe we dont need to push associations here, we could loop
|
||||
# through the @associations_pot instead
|
||||
@join_dependency.instance_variable_get(:@join_root).children.push found_association
|
||||
|
||||
# Builds the arel nodes properly for this association
|
||||
@join_dependency.send(
|
||||
:construct_tables!, jd.instance_variable_get(:@join_root), found_association
|
||||
)
|
||||
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(jd)
|
||||
|
||||
found_association
|
||||
end
|
||||
|
||||
def extract_joins(association)
|
||||
parent = @join_dependency.instance_variable_get(:@join_root)
|
||||
reflection = association.reflection
|
||||
join_constraints = if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_1
|
||||
association.join_constraints(
|
||||
parent.table,
|
||||
parent.base_klass,
|
||||
association,
|
||||
Arel::Nodes::OuterJoin,
|
||||
association.tables,
|
||||
reflection.scope_chain,
|
||||
reflection.chain
|
||||
)
|
||||
else
|
||||
association.join_constraints(
|
||||
parent.table,
|
||||
parent.base_klass,
|
||||
Arel::Nodes::OuterJoin,
|
||||
association.tables,
|
||||
reflection.chain
|
||||
)
|
||||
end
|
||||
join_constraints.to_a.flatten
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,12 +32,9 @@ module Ransack
|
|||
|
||||
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_5_2
|
||||
@base = @join_dependency.instance_variable_get(:@join_root)
|
||||
elsif ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
||||
else
|
||||
@base = @join_dependency.join_root
|
||||
@engine = @base.base_klass.arel_engine
|
||||
else
|
||||
@base = @join_dependency.join_base
|
||||
@engine = @base.arel_engine
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -55,10 +52,6 @@ module Ransack
|
|||
obj
|
||||
elsif obj.respond_to? :klass
|
||||
obj.klass
|
||||
elsif obj.respond_to? :active_record # Rails 3
|
||||
obj.active_record
|
||||
elsif obj.respond_to? :base_klass # Rails 4
|
||||
obj.base_klass
|
||||
else
|
||||
raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
|
||||
end
|
||||
|
|
|
@ -2,11 +2,7 @@ module Ransack
|
|||
module Translate
|
||||
|
||||
def self.i18n_key(klass)
|
||||
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
||||
klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
|
||||
else
|
||||
klass.model_name.i18n_key.to_s.freeze
|
||||
end
|
||||
klass.model_name.i18n_key.to_s.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,7 +45,6 @@ module Ransack
|
|||
NOT_EQ_ALL = 'not_eq_all'.freeze
|
||||
CONT = 'cont'.freeze
|
||||
|
||||
RAILS_4_1 = '4.1'.freeze
|
||||
RAILS_5_1 = '5.1'.freeze
|
||||
RAILS_5_2 = '5.2'.freeze
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ module Ransack
|
|||
val.to_s.to_d
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cast_to_money(val)
|
||||
val.blank? ? nil : val.to_f.to_s
|
||||
end
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
module PolyamorousHelper
|
||||
if ActiveRecord::VERSION::STRING >= "4.1"
|
||||
def new_join_association(reflection, children, klass)
|
||||
Polyamorous::JoinAssociation.new reflection, children, klass
|
||||
end
|
||||
else
|
||||
def new_join_association(reflection, join_dependency, parent, klass)
|
||||
Polyamorous::JoinAssociation.new reflection, join_dependency, parent, klass
|
||||
end
|
||||
def new_join_association(reflection, children, klass)
|
||||
Polyamorous::JoinAssociation.new reflection, children, klass
|
||||
end
|
||||
|
||||
if ActiveRecord::VERSION::STRING >= "5.2"
|
||||
|
|
|
@ -10,8 +10,7 @@ module Ransack
|
|||
subject { Context.new(Person) }
|
||||
|
||||
|
||||
it 'has an Active Record alias tracker method',
|
||||
if: AR_version >= '3.1' do
|
||||
it 'has an Active Record alias tracker method' do
|
||||
expect(subject.alias_tracker)
|
||||
.to be_an ::ActiveRecord::Associations::AliasTracker
|
||||
end
|
||||
|
@ -71,14 +70,14 @@ module Ransack
|
|||
describe '#join_sources' do
|
||||
# FIXME: fix this test for Rails 4.2 and 5.0.
|
||||
it 'returns dependent arel join nodes for all searches run against
|
||||
the context', if: %w(3.1 3.2 4.0 4.1).include?(AR_version) do
|
||||
the context' do
|
||||
skip "fix this test for Rails 4.2 and > 5"
|
||||
parents, children = shared_context.join_sources
|
||||
expect(children.left.name).to eq "children_people"
|
||||
expect(parents.left.name).to eq "parents_people"
|
||||
end
|
||||
|
||||
it 'can be rejoined to execute a valid query',
|
||||
if: AR_version >= '3.1' do
|
||||
it 'can be rejoined to execute a valid query' do
|
||||
parents, children = shared_context.join_sources
|
||||
|
||||
expect { Person.joins(parents).joins(children).to_a }
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
=begin
|
||||
rails = ::ActiveRecord::VERSION::STRING.first(3)
|
||||
|
||||
if %w(3.2 4.0 4.1).include?(rails) || rails == '3.1' && RUBY_VERSION < '2.2'
|
||||
describe 'Ransack' do
|
||||
it 'can be required without errors' do
|
||||
output = `bundle exec ruby -e "require 'ransack'" 2>&1`
|
||||
expect(output).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
=end
|
|
@ -21,11 +21,7 @@ module Ransack
|
|||
@controller.view_context.search_form_for(@s) { |f| @f = f }
|
||||
end
|
||||
|
||||
it 'selects previously-entered time values with datetime_select',
|
||||
unless: (
|
||||
RUBY_VERSION >= '2.3' &&
|
||||
::ActiveRecord::VERSION::STRING.first(3) < '3.2'
|
||||
) do
|
||||
it 'selects previously-entered time values with datetime_select' do
|
||||
date_values = %w(2011 1 2 03 04 05)
|
||||
# @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
|
||||
@s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
|
||||
|
@ -75,11 +71,7 @@ module Ransack
|
|||
describe '#sort_link' do
|
||||
it 'sort_link for ransack attribute' do
|
||||
sort_link = @f.sort_link :name, :controller => 'people'
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
expect(sort_link).to match /people\?q%5Bs%5D=name\+asc/
|
||||
else
|
||||
expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
expect(sort_link).to match /sort_link/
|
||||
expect(sort_link).to match /Full Name<\/a>/
|
||||
end
|
||||
|
@ -171,11 +163,7 @@ module Ransack
|
|||
# Starting from Rails 4.2, the date_select html attributes are no longer
|
||||
# `sort`ed (for a speed gain), so the tests have to be different:
|
||||
def date_select_html(val)
|
||||
if ::ActiveRecord::VERSION::STRING >= '4.2'
|
||||
%(<option value="#{val}" selected="selected">#{val}</option>)
|
||||
else
|
||||
%(<option selected="selected" value="#{val}">#{val}</option>)
|
||||
end
|
||||
%(<option value="#{val}" selected="selected">#{val}</option>)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -30,15 +30,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
it { should match /sort_link desc/ }
|
||||
it { should match /Full Name ▼/ }
|
||||
end
|
||||
|
@ -51,15 +43,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
end
|
||||
|
||||
describe '#sort_link with default search_key defined as symbol' do
|
||||
|
@ -69,15 +53,7 @@ module Ransack
|
|||
:name, controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?people_search%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
end
|
||||
|
||||
describe '#sort_url with default search_key defined as symbol' do
|
||||
|
@ -87,15 +63,7 @@ module Ransack
|
|||
:name, controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?people_search%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
end
|
||||
|
||||
describe '#sort_link desc through association table defined as symbol' do
|
||||
|
@ -106,15 +74,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=comments.body\+desc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ }
|
||||
it { should match /sort_link asc/ }
|
||||
it { should match /Body ▲/ }
|
||||
end
|
||||
|
@ -127,15 +87,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=comments.body\+desc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ }
|
||||
end
|
||||
|
||||
describe '#sort_link through association table defined as a string' do
|
||||
|
@ -146,15 +98,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=comments.body\+asc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ }
|
||||
it { should match /sort_link desc/ }
|
||||
it { should match /Comments.body ▼/ }
|
||||
end
|
||||
|
@ -167,15 +111,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=comments.body\+asc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ }
|
||||
end
|
||||
|
||||
describe '#sort_link works even if search params are a blank string' do
|
||||
|
@ -214,15 +150,7 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?people_search%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
end
|
||||
|
||||
describe '#sort_link with default_order defined with a string key' do
|
||||
|
@ -487,15 +415,7 @@ module Ransack
|
|||
controller: 'notes'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/notes\?q%5Bs%5D=notable_of_Person_type_name\+asc/
|
||||
else
|
||||
/notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ }
|
||||
it { should match /sort_link/ }
|
||||
it { should match /Notable/ }
|
||||
end
|
||||
|
@ -508,15 +428,7 @@ module Ransack
|
|||
controller: 'notes'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/notes\?q%5Bs%5D=notable_of_Person_type_name\+asc/
|
||||
else
|
||||
/notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/
|
||||
end
|
||||
)
|
||||
}
|
||||
it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ }
|
||||
end
|
||||
|
||||
context 'view has existing parameters' do
|
||||
|
|
|
@ -3,12 +3,7 @@ require 'spec_helper'
|
|||
module Polyamorous
|
||||
describe JoinAssociation do
|
||||
|
||||
join_base, join_association_args, polymorphic =
|
||||
if ActiveRecord::VERSION::STRING >= '4.1'
|
||||
[:join_root, 'parent.children', 'reflection.options[:polymorphic]']
|
||||
else
|
||||
[:join_base, 'join_dependency, parent', 'options[:polymorphic]']
|
||||
end
|
||||
join_base, join_association_args, polymorphic = [:join_root, 'parent.children', 'reflection.options[:polymorphic]']
|
||||
|
||||
let(:join_dependency) { new_join_dependency Note, {} }
|
||||
let(:reflection) { Note.reflect_on_association(:notable) }
|
||||
|
|
|
@ -3,12 +3,7 @@ require 'spec_helper'
|
|||
module Polyamorous
|
||||
describe JoinDependency do
|
||||
|
||||
method, join_associations, join_base =
|
||||
if ActiveRecord::VERSION::STRING >= '4.1'
|
||||
[:instance_eval, 'join_root.drop(1)', :join_root]
|
||||
else
|
||||
[:send, 'join_associations', :join_base]
|
||||
end
|
||||
method, join_associations, join_base = :instance_eval, 'join_root.drop(1)', :join_root
|
||||
|
||||
context 'with symbol joins' do
|
||||
subject { new_join_dependency Person, articles: :comments }
|
||||
|
|
|
@ -232,8 +232,8 @@ module Ransack
|
|||
# https://github.com/activerecord-hackery/ransack/issues/374
|
||||
#
|
||||
it 'evaluates conditions for multiple `belongs_to` associations to the
|
||||
same table contextually',
|
||||
if: ::ActiveRecord::VERSION::STRING.first(3) == '4.0' do
|
||||
same table contextually' do
|
||||
skip "Make this spec pass for Rails >5.0"
|
||||
s = Search.new(
|
||||
Recommendation,
|
||||
person_name_eq: 'Ernie',
|
||||
|
|
|
@ -25,26 +25,12 @@ else
|
|||
end
|
||||
|
||||
class Person < ActiveRecord::Base
|
||||
if ::ActiveRecord::VERSION::MAJOR == 3
|
||||
default_scope order('id DESC')
|
||||
else
|
||||
default_scope { order(id: :desc) }
|
||||
end
|
||||
default_scope { order(id: :desc) }
|
||||
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
||||
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
||||
has_many :articles
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
if RUBY_VERSION >= '2.3'
|
||||
has_many :published_articles, class_name: "Article",
|
||||
conditions: "published = 't'"
|
||||
else
|
||||
has_many :published_articles, class_name: "Article",
|
||||
conditions: { published: true }
|
||||
end
|
||||
else
|
||||
has_many :published_articles, ->{ where(published: true) },
|
||||
has_many :published_articles, ->{ where(published: true) },
|
||||
class_name: "Article"
|
||||
end
|
||||
has_many :comments
|
||||
has_many :authored_article_comments, through: :articles,
|
||||
source: :comments, foreign_key: :person_id
|
||||
|
@ -144,11 +130,7 @@ class Article < ActiveRecord::Base
|
|||
|
||||
alias_attribute :content, :body
|
||||
|
||||
if ::ActiveRecord::VERSION::STRING >= '3.1'
|
||||
default_scope { where("'default_scope' = 'default_scope'") }
|
||||
else # Rails 3.0 does not accept a block
|
||||
default_scope where("'default_scope' = 'default_scope'")
|
||||
end
|
||||
default_scope { where("'default_scope' = 'default_scope'") }
|
||||
end
|
||||
|
||||
class Recommendation < ActiveRecord::Base
|
||||
|
|
Loading…
Reference in New Issue