Skip to content
This repository was archived by the owner on Mar 30, 2022. It is now read-only.

Commit 73d47d3

Browse files
author
Ernie Miller
committed
tests passing against edge rails apart from the ones dependent on rails/rails#399
1 parent 32bfc9f commit 73d47d3

File tree

8 files changed

+139
-121
lines changed

8 files changed

+139
-121
lines changed

Rakefile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ begin
1515
gem.homepage = "http://metautonomo.us/projects/metasearch/"
1616
gem.authors = ["Ernie Miller"]
1717
gem.add_development_dependency "shoulda"
18-
gem.add_dependency "activerecord", "~> 3.0.2"
19-
gem.add_dependency "activesupport", "~> 3.0.2"
20-
gem.add_dependency "actionpack", "~> 3.0.2"
21-
gem.add_dependency "arel", "~> 2.0.2"
18+
gem.add_dependency "activerecord", "~> 3.1.0"
19+
gem.add_dependency "activesupport", "~> 3.1.0"
20+
gem.add_dependency "actionpack", "~> 3.1.0"
2221
gem.post_install_message = <<END
2322
2423
*** Thanks for installing MetaSearch! ***

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.5
1+
1.1.0.pre

lib/meta_search.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ module MetaSearch
5353

5454
I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'meta_search', 'locale', '*.yml')]
5555

56-
ActiveRecord::Associations::ClassMethods::JoinDependency.send(:include, MetaSearch::JoinDependency)
56+
ActiveRecord::Associations::JoinDependency.send(:include, MetaSearch::JoinDependency)
5757
ActiveRecord::Base.send(:include, MetaSearch::Searches::ActiveRecord)
5858
ActionView::Helpers::FormBuilder.send(:include, MetaSearch::Helpers::FormBuilder)
5959
ActionController::Base.helper(MetaSearch::Helpers::UrlHelper)

lib/meta_search/builder.rb

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def initialize(base_or_relation, opts = {})
3535
@options = opts # Let's just hang on to other options for use in authorization blocks
3636
@join_type = opts[:join_type] || Arel::Nodes::OuterJoin
3737
@join_type = get_join_type(@join_type)
38-
@join_dependency = build_join_dependency
38+
@join_dependency = build_join_dependency(@relation)
3939
@search_attributes = {}
4040
@errors = ActiveModel::Errors.new(self)
4141
end
@@ -51,12 +51,7 @@ def get_association(assoc, base = @base)
5151
def get_attribute(name, parent = @join_dependency.join_base)
5252
attribute = nil
5353
if get_column(name, parent.active_record)
54-
if parent.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
55-
relation = parent.relation.is_a?(Array) ? parent.relation.last : parent.relation
56-
attribute = relation[name]
57-
else
58-
attribute = @relation.arel_table[name]
59-
end
54+
attribute = parent.table[name]
6055
elsif (segments = name.to_s.split(/_/)).size > 1
6156
remainder = []
6257
found_assoc = nil
@@ -252,53 +247,58 @@ def type_from_association_segments(segments, base, depth)
252247
type
253248
end
254249

255-
def build_or_find_association(association, parent = @join_dependency.join_base, klass = nil)
250+
def build_or_find_association(name, parent = @join_dependency.join_base, klass = nil)
256251
found_association = @join_dependency.join_associations.detect do |assoc|
257-
assoc.reflection.name == association.to_sym &&
258-
assoc.reflection.klass == klass &&
259-
assoc.parent == parent
252+
assoc.reflection.name == name &&
253+
assoc.parent == parent &&
254+
(!klass || assoc.reflection.klass == klass)
260255
end
261256
unless found_association
262-
@join_dependency.send(:build_with_metasearch, association, parent, @join_type, klass)
257+
@join_dependency.send(:build_polymorphic, name.to_sym, parent, @join_type, klass)
263258
found_association = @join_dependency.join_associations.last
259+
# Leverage the stashed association functionality in AR
264260
@relation = @relation.joins(found_association)
265261
end
262+
266263
found_association
267264
end
268265

269-
def build_join_dependency
270-
joins = @relation.joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
271-
272-
association_joins = joins.select do |j|
273-
[Hash, Array, Symbol].include?(j.class) && !array_of_strings?(j)
274-
end
275-
276-
stashed_association_joins = joins.select do |j|
277-
j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
266+
def build_join_dependency(relation)
267+
buckets = relation.joins_values.group_by do |join|
268+
case join
269+
when String
270+
'string_join'
271+
when Hash, Symbol, Array
272+
'association_join'
273+
when ::ActiveRecord::Associations::JoinDependency::JoinAssociation
274+
'stashed_join'
275+
when Arel::Nodes::Join
276+
'join_node'
277+
else
278+
raise 'unknown class: %s' % join.class.name
279+
end
278280
end
279281

280-
non_association_joins = (joins - association_joins - stashed_association_joins)
281-
custom_joins = custom_join_sql(*non_association_joins)
282+
association_joins = buckets['association_join'] || []
283+
stashed_association_joins = buckets['stashed_join'] || []
284+
join_nodes = buckets['join_node'] || []
285+
string_joins = (buckets['string_join'] || []).map { |x|
286+
x.strip
287+
}.uniq
282288

283-
ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, association_joins, custom_joins)
284-
end
289+
join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins
285290

286-
def custom_join_sql(*joins)
287-
arel = @relation.table
288-
joins.each do |join|
289-
next if join.blank?
291+
join_dependency = ::ActiveRecord::Associations::JoinDependency.new(
292+
relation.klass,
293+
association_joins,
294+
join_list
295+
)
290296

291-
case join
292-
when Hash, Array, Symbol
293-
if array_of_strings?(join)
294-
join_string = join.join(' ')
295-
arel = arel.join(join_string)
296-
end
297-
else
298-
arel = arel.join(join)
299-
end
297+
join_nodes.each do |join|
298+
join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase)
300299
end
301-
arel.joins(arel)
300+
301+
join_dependency.graft(*stashed_association_joins)
302302
end
303303

304304
def get_join_type(opt_join)

lib/meta_search/join_dependency.rb

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,90 +2,93 @@ module MetaSearch
22

33
module JoinDependency
44

5+
class JoinAssociation < ::ActiveRecord::Associations::JoinDependency::JoinAssociation
6+
7+
def initialize(reflection, join_dependency, parent = nil, polymorphic_class = nil)
8+
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
9+
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
10+
super(reflection, join_dependency, parent)
11+
end
12+
else
13+
super(reflection, join_dependency, parent)
14+
end
15+
end
16+
17+
def swapping_reflection_klass(reflection, klass)
18+
reflection = reflection.clone
19+
original_polymorphic = reflection.options.delete(:polymorphic)
20+
reflection.instance_variable_set(:@klass, klass)
21+
yield reflection
22+
ensure
23+
reflection.options[:polymorphic] = original_polymorphic
24+
end
25+
26+
def ==(other)
27+
super && active_record == other.active_record
28+
end
29+
30+
def build_constraint(reflection, table, key, foreign_table, foreign_key)
31+
if reflection.options[:polymorphic]
32+
super.and(
33+
foreign_table[reflection.foreign_type].eq(reflection.klass.name)
34+
)
35+
else
36+
super
37+
end
38+
end
39+
40+
end
41+
42+
# Yes, I'm using alias_method_chain here. No, I don't feel too
43+
# bad about it. JoinDependency, or, to call it by its full proper
44+
# name, ::ActiveRecord::Associations::JoinDependency, is one of the
45+
# most "for internal use only" chunks of ActiveRecord.
546
def self.included(base)
647
base.class_eval do
7-
alias_method_chain :graft, :metasearch
48+
alias_method_chain :graft, :meta_search
849
end
950
end
1051

11-
def graft_with_metasearch(*associations)
52+
def graft_with_meta_search(*associations)
1253
associations.each do |association|
1354
join_associations.detect {|a| association == a} ||
14-
(
15-
association.class == MetaSearch::PolymorphicJoinAssociation ?
16-
build_with_metasearch(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass) :
17-
build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
18-
)
55+
build_polymorphic(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass)
1956
end
2057
self
2158
end
2259

23-
protected
60+
# Should only be called by MetaSearch, and only with a single association name
61+
def build_polymorphic(association, parent = nil, join_type = Arel::OuterJoin, klass = nil)
62+
parent ||= join_parts.last
63+
reflection = parent.reflections[association] or
64+
raise ::ActiveRecord::ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?"
65+
unless join_association = find_join_association_respecting_polymorphism(reflection, parent, klass)
66+
@reflections << reflection
67+
join_association = build_join_association_respecting_polymorphism(reflection, parent, klass)
68+
join_association.join_type = join_type
69+
@join_parts << join_association
70+
cache_joined_association(join_association)
71+
end
2472

25-
def build_with_metasearch(associations, parent = nil, join_type = Arel::Nodes::InnerJoin, polymorphic_class = nil)
26-
parent ||= @joins.last
27-
case associations
28-
when Symbol, String
29-
reflection = parent.reflections[associations.to_s.intern] or
30-
raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
31-
unless (association = find_join_association(reflection, parent)) && (!polymorphic_class || association.active_record == polymorphic_class)
32-
@reflections << reflection
33-
if reflection.options[:polymorphic]
34-
raise ArgumentError, "You can't create a polymorphic belongs_to join without specifying the polymorphic class!" unless polymorphic_class
35-
association = PolymorphicJoinAssociation.new(reflection, self, polymorphic_class, parent)
36-
else
37-
association = build_join_association(reflection, parent)
38-
end
39-
association.join_type = join_type
40-
@joins << association
41-
end
73+
join_association
74+
end
75+
76+
def find_join_association_respecting_polymorphism(reflection, parent, klass)
77+
if association = find_join_association(reflection, parent)
78+
unless reflection.options[:polymorphic]
4279
association
4380
else
44-
build(associations, parent, join_type)
81+
association if association.active_record == klass
4582
end
4683
end
47-
end
48-
49-
class PolymorphicJoinAssociation < ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
50-
51-
def initialize(reflection, join_dependency, polymorphic_class, parent = nil)
52-
reflection.check_validity!
53-
@active_record = polymorphic_class
54-
@cached_record = {}
55-
@join_dependency = join_dependency
56-
@parent = parent || join_dependency.join_base
57-
@reflection = reflection.clone
58-
@reflection.instance_variable_set(:"@klass", polymorphic_class)
59-
@aliased_prefix = "t#{ join_dependency.joins.size }"
60-
@parent_table_name = @parent.active_record.table_name
61-
@aliased_table_name = aliased_table_name_for(table_name)
62-
@join = nil
63-
@join_type = Arel::Nodes::InnerJoin
64-
end
65-
66-
def ==(other)
67-
other.class == self.class &&
68-
other.reflection == reflection &&
69-
other.active_record == active_record &&
70-
other.parent == parent
7184
end
7285

73-
def association_join
74-
return @join if @join
75-
76-
aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
77-
parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
78-
79-
@join = [
80-
aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name]),
81-
parent_table[options[:foreign_type]].eq(active_record.name)
82-
]
83-
84-
if options[:conditions]
85-
@join << interpolate_sql(sanitize_sql(options[:conditions], aliased_table_name))
86+
def build_join_association_respecting_polymorphism(reflection, parent, klass = nil)
87+
if reflection.options[:polymorphic] && klass
88+
JoinAssociation.new(reflection, self, parent, klass)
89+
else
90+
JoinAssociation.new(reflection, self, parent)
8691
end
87-
88-
@join
8992
end
9093
end
9194
end

test/test_view_helpers.rb

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,34 @@ class TestViewHelpers < ActionView::TestCase
66
tests MetaSearch::Helpers::FormHelper
77
include MetaSearch::Helpers::UrlHelper
88

9-
router = ActionDispatch::Routing::RouteSet.new
10-
router.draw do
11-
resources :developers
12-
resources :companies
13-
resources :projects
14-
resources :notes
15-
match ':controller(/:action(/:id(.:format)))'
9+
def self.router
10+
@router ||= begin
11+
router = ActionDispatch::Routing::RouteSet.new
12+
router.draw do
13+
resources :developers
14+
resources :companies
15+
resources :projects
16+
resources :notes
17+
match ':controller(/:action(/:id(.:format)))'
18+
end
19+
router
20+
end
1621
end
22+
1723
include router.url_helpers
1824

25+
# FIXME: figure out a cleaner way to get this behavior
1926
def setup
27+
router = self.class.router
2028
@controller = ActionView::TestCase::TestController.new
29+
@controller.instance_variable_set(:@_routes, router)
30+
@controller.class_eval do
31+
include router.url_helpers
32+
end
33+
34+
@controller.view_context_class.class_eval do
35+
include router.url_helpers
36+
end
2137
end
2238

2339
context "A search against Company and a search against Developer" do
@@ -326,7 +342,7 @@ def setup
326342
end
327343

328344
should "maintain previous search options in its sort links" do
329-
assert_match /search\[name_contains\]=a/,
345+
assert_match /search%5Bname_contains%5D=a/,
330346
sort_link(@s, :name, :controller => 'companies')
331347
end
332348
end
@@ -359,7 +375,7 @@ def setup
359375
end
360376

361377
should "maintain previous search options in its sort links" do
362-
assert_match /search\[name_contains\]=a/,
378+
assert_match /search%5Bname_contains%5D=a/,
363379
sort_link(@s, :company_name, :controller => 'companies')
364380
end
365381
end

vendor/arel

Submodule arel updated 104 files

vendor/rails

Submodule rails updated 1171 files

0 commit comments

Comments
 (0)