Skip to content

Commit

Permalink
Avoid overwriting association conditions with default scope
Browse files Browse the repository at this point in the history
When joining ransackable associations in Rails 3, preserve both:

* association default_scope
* association conditions

Combining the stashed JoinAssociation with a separate string join for
the "AND" conditions looks like a hack, but there is no cleaner way to
extract the default_scope conditions with correct table aliases and
merge them back into the association since ActiveRecord rebuilds its own
join parts when generating the query.
  • Loading branch information
avit committed Jul 9, 2015
1 parent d5d1cfa commit 9f778e8
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 27 deletions.
18 changes: 7 additions & 11 deletions lib/ransack/adapters/active_record/3.0/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,23 +182,19 @@ def build_or_find_association(name, parent = @base, klass = nil)
:build, Polyamorous::Join.new(name, @join_type, klass), parent
)
found_association = @join_dependency.join_associations.last
apply_default_conditions(found_association)

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)
@object = @object.joins(found_association).joins(and_default_conditions)
end

found_association
end

def apply_default_conditions(join_association)
reflection = join_association.reflection
default_scope = join_association.active_record.scoped
default_conditions = default_scope.arel.where_clauses
if default_conditions.any?
reflection.options[:conditions] = default_conditions
end
end

end
end
end
Expand Down
24 changes: 10 additions & 14 deletions lib/ransack/adapters/active_record/3.1/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def join_associations
#
def join_sources
base = Arel::SelectManager.new(@object.engine, @object.table)
joins = @object.joins_values
joins.each do |assoc|
@object.joins_values.each do |assoc|
next unless assoc.is_a?(JoinDependency::JoinAssociation)
assoc.join_to(base)
end
base.join_sources
Expand Down Expand Up @@ -151,7 +151,7 @@ def build_join_dependency(relation)
Constants::STRING_JOIN
when Hash, Symbol, Array
Constants::ASSOCIATION_JOIN
when ::ActiveRecord::Associations::JoinDependency::JoinAssociation
when JoinDependency::JoinAssociation
Constants::STASHED_JOIN
when Arel::Nodes::Join
Constants::JOIN_NODE
Expand Down Expand Up @@ -196,23 +196,19 @@ def build_or_find_association(name, parent = @base, klass = nil)
:build, Polyamorous::Join.new(name, @join_type, klass), parent
)
found_association = @join_dependency.join_associations.last
apply_default_conditions(found_association)

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)
@object = @object.joins(found_association).joins(and_default_conditions)
end

found_association
end

def apply_default_conditions(join_association)
reflection = join_association.reflection
default_scope = join_association.active_record.scoped
default_conditions = default_scope.arel.where_clauses
if default_conditions.any?
reflection.options[:conditions] = default_conditions
end
end

end
end
end
Expand Down
5 changes: 3 additions & 2 deletions spec/ransack/search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ module Ransack
expect(condition.value).to eq 'Ernie'
end

it 'preserves default scope conditions for associations' do
search = Search.new(Person, articles_title_eq: 'Test')
it 'preserves default scope and conditions for associations' do
search = Search.new(Person, published_articles_title_eq: 'Test')
expect(search.result.to_sql).to include 'default_scope'
expect(search.result.to_sql).to include 'published'
end

it 'discards empty conditions' do
Expand Down
2 changes: 2 additions & 0 deletions spec/support/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Person < ActiveRecord::Base
belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id
has_many :children, :class_name => 'Person', :foreign_key => :parent_id
has_many :articles
has_many :published_articles, :class_name => 'Article', :conditions => {published: true}
has_many :comments
has_many :authored_article_comments, :through => :articles,
:source => :comments, :foreign_key => :person_id
Expand Down Expand Up @@ -171,6 +172,7 @@ def self.create
t.string :title
t.text :subject_header
t.text :body
t.boolean :published, default: true
end

create_table :comments, :force => true do |t|
Expand Down

0 comments on commit 9f778e8

Please sign in to comment.