Skip to content

Commit cd581f8

Browse files
committed
Merge pull request #412 from avit/shared-context
Support merging searches using shared context
2 parents c92fa4d + 7e2cf2d commit cd581f8

File tree

6 files changed

+140
-2
lines changed

6 files changed

+140
-2
lines changed

lib/ransack/adapters/active_record/3.0/context.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@ def type_for(attr)
6868
.type
6969
end
7070

71+
# All dependent JoinAssociation items used in the search query
72+
#
73+
def join_associations
74+
@join_dependency.join_associations
75+
end
76+
77+
def join_sources
78+
raise NotImplementedError,
79+
"ActiveRecord 3.0 does not use join_sources or support joining relations with Arel::Join nodes. Use join_associations."
80+
end
81+
82+
def alias_tracker
83+
raise NotImplementedError,
84+
"ActiveRecord 3.0 does not have an alias tracker"
85+
end
86+
7187
private
7288

7389
def get_parent_and_attribute_name(str, parent = @base)

lib/ransack/adapters/active_record/3.1/context.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,30 @@ def type_for(attr)
7373
@engine.connection_pool.columns_hash[table][name].type
7474
end
7575

76+
def join_associations
77+
@join_dependency.join_associations
78+
end
79+
80+
# All dependent Arel::Join nodes used in the search query
81+
#
82+
# This could otherwise be done as `@object.arel.join_sources`, except
83+
# that ActiveRecord's build_joins sets up its own JoinDependency.
84+
# This extracts what we need to access the joins using our existing
85+
# JoinDependency to track table aliases.
86+
#
87+
def join_sources
88+
base = Arel::SelectManager.new(@object.engine, @object.table)
89+
joins = @object.joins_values
90+
joins.each do |assoc|
91+
assoc.join_to(base)
92+
end
93+
base.join_sources
94+
end
95+
96+
def alias_tracker
97+
@join_dependency.alias_tracker
98+
end
99+
76100
private
77101

78102
def get_parent_and_attribute_name(str, parent = @base)

lib/ransack/adapters/active_record/context.rb

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,55 @@ def klassify(obj)
7878
end
7979
end
8080

81-
private
81+
if ::ActiveRecord::VERSION::STRING >= '4.1'
82+
83+
def join_associations
84+
raise NotImplementedError,
85+
"ActiveRecord 4.1 and later does not use join_associations. Use join_sources."
86+
end
87+
88+
# All dependent Arel::Join nodes used in the search query
89+
#
90+
# This could otherwise be done as `@object.arel.join_sources`, except
91+
# that ActiveRecord's build_joins sets up its own JoinDependency.
92+
# This extracts what we need to access the joins using our existing
93+
# JoinDependency to track table aliases.
94+
#
95+
def join_sources
96+
base = Arel::SelectManager.new(@object.engine, @object.table)
97+
joins = @join_dependency.join_constraints(@object.joins_values)
98+
joins.each do |aliased_join|
99+
base.from(aliased_join)
100+
end
101+
base.join_sources
102+
end
103+
104+
else
105+
106+
# All dependent JoinAssociation items used in the search query
107+
#
108+
# Deprecated: this goes away in ActiveRecord 4.1. Use join_sources.
109+
#
110+
def join_associations
111+
@join_dependency.join_associations
112+
end
113+
114+
def join_sources
115+
base = Arel::SelectManager.new(@object.engine, @object.table)
116+
joins = @object.joins_values
117+
joins.each do |assoc|
118+
assoc.join_to(base)
119+
end
120+
base.join_sources
121+
end
122+
123+
end
124+
125+
def alias_tracker
126+
@join_dependency.alias_tracker
127+
end
128+
129+
private
82130

83131
def get_parent_and_attribute_name(str, parent = @base)
84132
attr_name = nil

lib/ransack/search.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def initialize(object, params = {}, options = {})
1717
params = {} unless params.is_a?(Hash)
1818
(params ||= {})
1919
.delete_if { |k, v| [*v].all? { |i| i.blank? && i != false } }
20-
@context = Context.for(object, options)
20+
@context = options[:context] || Context.for(object, options)
2121
@context.auth_object = options[:auth_object]
2222
@base = Nodes::Grouping.new(@context, options[:grouping] || 'and')
2323
@scope_args = {}

spec/ransack/adapters/active_record/context_spec.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ module ActiveRecord
66
describe Context do
77
subject { Context.new(Person) }
88

9+
if ::ActiveRecord::VERSION::STRING >= "3.1"
10+
its(:alias_tracker) { should be_a ::ActiveRecord::Associations::AliasTracker }
11+
end
12+
913
describe '#relation_for' do
1014
it 'returns relation for given object' do
1115
expect(subject.object).to be_an ::ActiveRecord::Relation
@@ -30,6 +34,45 @@ module ActiveRecord
3034
end
3135
end
3236

37+
describe "sharing context across searches" do
38+
let(:shared_context) { Context.for(Person) }
39+
40+
before do
41+
Search.new(Person, {:parent_name_eq => 'A'}, context: shared_context)
42+
Search.new(Person, {:children_name_eq => 'B'}, context: shared_context)
43+
end
44+
45+
describe '#join_associations', :if => ::ActiveRecord::VERSION::STRING <= '4.0' do
46+
it 'returns dependent join associations for all searches run against the context' do
47+
parents, children = shared_context.join_associations
48+
49+
expect(children.aliased_table_name).to eq "children_people"
50+
expect(parents.aliased_table_name).to eq "parents_people"
51+
end
52+
53+
it 'can be rejoined to execute a valid query' do
54+
parents, children = shared_context.join_associations
55+
56+
expect { Person.joins(parents).joins(children).to_a }.to_not raise_error
57+
end
58+
end
59+
60+
describe '#join_sources', :if => ::ActiveRecord::VERSION::STRING >= '3.1' do
61+
it 'returns dependent arel join nodes for all searches run against the context' do
62+
parents, children = shared_context.join_sources
63+
64+
expect(children.left.name).to eq "children_people"
65+
expect(parents.left.name).to eq "parents_people"
66+
end
67+
68+
it 'can be rejoined to execute a valid query' do
69+
parents, children = shared_context.join_sources
70+
71+
expect { Person.joins(parents).joins(children).to_a }.to_not raise_error
72+
end
73+
end
74+
end
75+
3376
it 'contextualizes strings to attributes' do
3477
attribute = subject.contextualize 'children_children_parent_name'
3578
expect(attribute).to be_a Arel::Attributes::Attribute

spec/ransack/search_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ module Ransack
4040
it 'does not raise exception for string :params argument' do
4141
expect { Search.new(Person, '') }.not_to raise_error
4242
end
43+
44+
it 'accepts a context option' do
45+
shared_context = Context.for(Person)
46+
search1 = Search.new(Person, {"name_eq" => "A"}, context: shared_context)
47+
search2 = Search.new(Person, {"name_eq" => "B"}, context: shared_context)
48+
expect(search1.context).to be search2.context
49+
end
4350
end
4451

4552
describe '#build' do

0 commit comments

Comments
 (0)