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

Commit

Permalink
tests passing against edge rails apart from the ones dependent on rai…
Browse files Browse the repository at this point in the history
  • Loading branch information
Ernie Miller committed May 5, 2011
1 parent 32bfc9f commit 73d47d3
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 121 deletions.
7 changes: 3 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ begin
gem.homepage = "http://metautonomo.us/projects/metasearch/"
gem.authors = ["Ernie Miller"]
gem.add_development_dependency "shoulda"
gem.add_dependency "activerecord", "~> 3.0.2"
gem.add_dependency "activesupport", "~> 3.0.2"
gem.add_dependency "actionpack", "~> 3.0.2"
gem.add_dependency "arel", "~> 2.0.2"
gem.add_dependency "activerecord", "~> 3.1.0"
gem.add_dependency "activesupport", "~> 3.1.0"
gem.add_dependency "actionpack", "~> 3.1.0"
gem.post_install_message = <<END
*** Thanks for installing MetaSearch! ***
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.5
1.1.0.pre
2 changes: 1 addition & 1 deletion lib/meta_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module MetaSearch

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

ActiveRecord::Associations::ClassMethods::JoinDependency.send(:include, MetaSearch::JoinDependency)
ActiveRecord::Associations::JoinDependency.send(:include, MetaSearch::JoinDependency)
ActiveRecord::Base.send(:include, MetaSearch::Searches::ActiveRecord)
ActionView::Helpers::FormBuilder.send(:include, MetaSearch::Helpers::FormBuilder)
ActionController::Base.helper(MetaSearch::Helpers::UrlHelper)
Expand Down
78 changes: 39 additions & 39 deletions lib/meta_search/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def initialize(base_or_relation, opts = {})
@options = opts # Let's just hang on to other options for use in authorization blocks
@join_type = opts[:join_type] || Arel::Nodes::OuterJoin
@join_type = get_join_type(@join_type)
@join_dependency = build_join_dependency
@join_dependency = build_join_dependency(@relation)
@search_attributes = {}
@errors = ActiveModel::Errors.new(self)
end
Expand All @@ -51,12 +51,7 @@ def get_association(assoc, base = @base)
def get_attribute(name, parent = @join_dependency.join_base)
attribute = nil
if get_column(name, parent.active_record)
if parent.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
relation = parent.relation.is_a?(Array) ? parent.relation.last : parent.relation
attribute = relation[name]
else
attribute = @relation.arel_table[name]
end
attribute = parent.table[name]
elsif (segments = name.to_s.split(/_/)).size > 1
remainder = []
found_assoc = nil
Expand Down Expand Up @@ -252,53 +247,58 @@ def type_from_association_segments(segments, base, depth)
type
end

def build_or_find_association(association, parent = @join_dependency.join_base, klass = nil)
def build_or_find_association(name, parent = @join_dependency.join_base, klass = nil)
found_association = @join_dependency.join_associations.detect do |assoc|
assoc.reflection.name == association.to_sym &&
assoc.reflection.klass == klass &&
assoc.parent == parent
assoc.reflection.name == name &&
assoc.parent == parent &&
(!klass || assoc.reflection.klass == klass)
end
unless found_association
@join_dependency.send(:build_with_metasearch, association, parent, @join_type, klass)
@join_dependency.send(:build_polymorphic, name.to_sym, parent, @join_type, klass)
found_association = @join_dependency.join_associations.last
# Leverage the stashed association functionality in AR
@relation = @relation.joins(found_association)
end

found_association
end

def build_join_dependency
joins = @relation.joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq

association_joins = joins.select do |j|
[Hash, Array, Symbol].include?(j.class) && !array_of_strings?(j)
end

stashed_association_joins = joins.select do |j|
j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
def build_join_dependency(relation)
buckets = relation.joins_values.group_by do |join|
case join
when String
'string_join'
when Hash, Symbol, Array
'association_join'
when ::ActiveRecord::Associations::JoinDependency::JoinAssociation
'stashed_join'
when Arel::Nodes::Join
'join_node'
else
raise 'unknown class: %s' % join.class.name
end
end

non_association_joins = (joins - association_joins - stashed_association_joins)
custom_joins = custom_join_sql(*non_association_joins)
association_joins = buckets['association_join'] || []
stashed_association_joins = buckets['stashed_join'] || []
join_nodes = buckets['join_node'] || []
string_joins = (buckets['string_join'] || []).map { |x|
x.strip
}.uniq

ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, association_joins, custom_joins)
end
join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins

def custom_join_sql(*joins)
arel = @relation.table
joins.each do |join|
next if join.blank?
join_dependency = ::ActiveRecord::Associations::JoinDependency.new(
relation.klass,
association_joins,
join_list
)

case join
when Hash, Array, Symbol
if array_of_strings?(join)
join_string = join.join(' ')
arel = arel.join(join_string)
end
else
arel = arel.join(join)
end
join_nodes.each do |join|
join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase)
end
arel.joins(arel)

join_dependency.graft(*stashed_association_joins)
end

def get_join_type(opt_join)
Expand Down
133 changes: 68 additions & 65 deletions lib/meta_search/join_dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,93 @@ module MetaSearch

module JoinDependency

class JoinAssociation < ::ActiveRecord::Associations::JoinDependency::JoinAssociation

def initialize(reflection, join_dependency, parent = nil, polymorphic_class = nil)
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
super(reflection, join_dependency, parent)
end
else
super(reflection, join_dependency, parent)
end
end

def swapping_reflection_klass(reflection, klass)
reflection = reflection.clone
original_polymorphic = reflection.options.delete(:polymorphic)
reflection.instance_variable_set(:@klass, klass)
yield reflection
ensure
reflection.options[:polymorphic] = original_polymorphic
end

def ==(other)
super && active_record == other.active_record
end

def build_constraint(reflection, table, key, foreign_table, foreign_key)
if reflection.options[:polymorphic]
super.and(
foreign_table[reflection.foreign_type].eq(reflection.klass.name)
)
else
super
end
end

end

# Yes, I'm using alias_method_chain here. No, I don't feel too
# bad about it. JoinDependency, or, to call it by its full proper
# name, ::ActiveRecord::Associations::JoinDependency, is one of the
# most "for internal use only" chunks of ActiveRecord.
def self.included(base)
base.class_eval do
alias_method_chain :graft, :metasearch
alias_method_chain :graft, :meta_search
end
end

def graft_with_metasearch(*associations)
def graft_with_meta_search(*associations)
associations.each do |association|
join_associations.detect {|a| association == a} ||
(
association.class == MetaSearch::PolymorphicJoinAssociation ?
build_with_metasearch(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass) :
build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
)
build_polymorphic(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass)
end
self
end

protected
# Should only be called by MetaSearch, and only with a single association name
def build_polymorphic(association, parent = nil, join_type = Arel::OuterJoin, klass = nil)
parent ||= join_parts.last
reflection = parent.reflections[association] or
raise ::ActiveRecord::ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?"
unless join_association = find_join_association_respecting_polymorphism(reflection, parent, klass)
@reflections << reflection
join_association = build_join_association_respecting_polymorphism(reflection, parent, klass)
join_association.join_type = join_type
@join_parts << join_association
cache_joined_association(join_association)
end

def build_with_metasearch(associations, parent = nil, join_type = Arel::Nodes::InnerJoin, polymorphic_class = nil)
parent ||= @joins.last
case associations
when Symbol, String
reflection = parent.reflections[associations.to_s.intern] or
raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
unless (association = find_join_association(reflection, parent)) && (!polymorphic_class || association.active_record == polymorphic_class)
@reflections << reflection
if reflection.options[:polymorphic]
raise ArgumentError, "You can't create a polymorphic belongs_to join without specifying the polymorphic class!" unless polymorphic_class
association = PolymorphicJoinAssociation.new(reflection, self, polymorphic_class, parent)
else
association = build_join_association(reflection, parent)
end
association.join_type = join_type
@joins << association
end
join_association
end

def find_join_association_respecting_polymorphism(reflection, parent, klass)
if association = find_join_association(reflection, parent)
unless reflection.options[:polymorphic]
association
else
build(associations, parent, join_type)
association if association.active_record == klass
end
end
end

class PolymorphicJoinAssociation < ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation

def initialize(reflection, join_dependency, polymorphic_class, parent = nil)
reflection.check_validity!
@active_record = polymorphic_class
@cached_record = {}
@join_dependency = join_dependency
@parent = parent || join_dependency.join_base
@reflection = reflection.clone
@reflection.instance_variable_set(:"@klass", polymorphic_class)
@aliased_prefix = "t#{ join_dependency.joins.size }"
@parent_table_name = @parent.active_record.table_name
@aliased_table_name = aliased_table_name_for(table_name)
@join = nil
@join_type = Arel::Nodes::InnerJoin
end

def ==(other)
other.class == self.class &&
other.reflection == reflection &&
other.active_record == active_record &&
other.parent == parent
end

def association_join
return @join if @join

aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)

@join = [
aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name]),
parent_table[options[:foreign_type]].eq(active_record.name)
]

if options[:conditions]
@join << interpolate_sql(sanitize_sql(options[:conditions], aliased_table_name))
def build_join_association_respecting_polymorphism(reflection, parent, klass = nil)
if reflection.options[:polymorphic] && klass
JoinAssociation.new(reflection, self, parent, klass)
else
JoinAssociation.new(reflection, self, parent)
end

@join
end
end
end
34 changes: 25 additions & 9 deletions test/test_view_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,34 @@ class TestViewHelpers < ActionView::TestCase
tests MetaSearch::Helpers::FormHelper
include MetaSearch::Helpers::UrlHelper

router = ActionDispatch::Routing::RouteSet.new
router.draw do
resources :developers
resources :companies
resources :projects
resources :notes
match ':controller(/:action(/:id(.:format)))'
def self.router
@router ||= begin
router = ActionDispatch::Routing::RouteSet.new
router.draw do
resources :developers
resources :companies
resources :projects
resources :notes
match ':controller(/:action(/:id(.:format)))'
end
router
end
end

include router.url_helpers

# FIXME: figure out a cleaner way to get this behavior
def setup
router = self.class.router
@controller = ActionView::TestCase::TestController.new
@controller.instance_variable_set(:@_routes, router)
@controller.class_eval do
include router.url_helpers
end

@controller.view_context_class.class_eval do
include router.url_helpers
end
end

context "A search against Company and a search against Developer" do
Expand Down Expand Up @@ -326,7 +342,7 @@ def setup
end

should "maintain previous search options in its sort links" do
assert_match /search\[name_contains\]=a/,
assert_match /search%5Bname_contains%5D=a/,
sort_link(@s, :name, :controller => 'companies')
end
end
Expand Down Expand Up @@ -359,7 +375,7 @@ def setup
end

should "maintain previous search options in its sort links" do
assert_match /search\[name_contains\]=a/,
assert_match /search%5Bname_contains%5D=a/,
sort_link(@s, :company_name, :controller => 'companies')
end
end
Expand Down
2 changes: 1 addition & 1 deletion vendor/arel
Submodule arel updated 104 files
2 changes: 1 addition & 1 deletion vendor/rails
Submodule rails updated 1171 files

0 comments on commit 73d47d3

Please sign in to comment.