From bc4dd67530847035acc05291522eff934e426784 Mon Sep 17 00:00:00 2001 From: Nik Wakelin Date: Mon, 23 Jun 2008 10:14:33 +1200 Subject: [PATCH] Added syntactic sugar to belongs_to associations in ActiveRecord hash conditions --- activerecord/lib/active_record/base.rb | 14 ++++++++++++++ activerecord/test/cases/finder_test.rb | 10 ++++++++++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8fca34e..0fb9254 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -196,6 +196,10 @@ module ActiveRecord #:nodoc: # # Student.find(:all, :conditions => { :grade => [9,11,12] }) # + # You can also use assocation names for belongs_to assocations. For example, if Student belongs_to University + # + # Student.find(:all, :conditions => { :university => victoria_wellington }) + # # == Overwriting default accessors # # All column values are automatically available through basic accessors on the Active Record object, but sometimes you @@ -2002,6 +2006,9 @@ module ActiveRecord #:nodoc: # And for value objects on a composed_of relationship: # { :address => Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" + # There's also some sugar built in for belongs_to associations + # { :person => 4 } + # # => "person_id= 4" def sanitize_sql_hash_for_conditions(attrs) attrs = expand_hash_conditions_for_aggregates(attrs) @@ -2015,6 +2022,13 @@ module ActiveRecord #:nodoc: else table_name = quoted_table_name end + + unless column_names.include?(attr) + # Lookup the foreign key for belongs_to associations + if reflection = reflect_on_all_associations(:belongs_to).find { |a| a.name.to_s == attr } + attr = reflection.association_foreign_key + end + end "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" end.join(' AND ') diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 80936d5..7131173 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -207,6 +207,16 @@ class FinderTest < ActiveRecord::TestCase } end + def test_find_on_hash_conditions_with_belongs_to_sugar + david = authors(:david) + assert Post.find(:first, :conditions => { :author => david }) + + # test we still fall through + assert_raises(ActiveRecord::StatementInvalid) do + Post.find(:first, :conditions => { :this_is_not_a_column_name => "fubar"}) + end + end + def test_find_on_association_proxy_conditions assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort end -- 1.5.3.7 From f511d9f4f2646ab9ceb6a6f40778c7c58b7b9341 Mon Sep 17 00:00:00 2001 From: Nik Wakelin Date: Mon, 23 Jun 2008 11:09:10 +1200 Subject: [PATCH] Made the belongs_to sugar work for polymorphic associations too --- activerecord/lib/active_record/base.rb | 3 +++ activerecord/lib/active_record/reflection.rb | 4 ++++ activerecord/test/cases/finder_test.rb | 14 +++++++++++++- activerecord/test/cases/reflection_test.rb | 6 ++++++ activerecord/test/models/person.rb | 2 ++ 5 files changed, 28 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0fb9254..c8ac88c 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2026,6 +2026,9 @@ module ActiveRecord #:nodoc: unless column_names.include?(attr) # Lookup the foreign key for belongs_to associations if reflection = reflect_on_all_associations(:belongs_to).find { |a| a.name.to_s == attr } + if reflection.polymorphic? + attrs.merge!({ reflection.options[:foreign_type] => value.class.class_name }) + end attr = reflection.association_foreign_key end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8614ef8..1fb51ba 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -152,6 +152,10 @@ module ActiveRecord options[:counter_cache] end end + + def polymorphic? + @options[:polymorphic] || @options[:polymorphic] == :true + end # Returns the AssociationReflection object specified in the :through option # of a HasManyThrough or HasOneThrough association. Example: diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 7131173..f47587b 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -10,9 +10,12 @@ require 'models/post' require 'models/customer' require 'models/job' require 'models/categorization' +require 'models/member' +require 'models/sponsor' +require 'models/person' class FinderTest < ActiveRecord::TestCase - fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers + fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :members, :sponsors, :people def test_find assert_equal(topics(:first).title, Topic.find(1).title) @@ -216,6 +219,15 @@ class FinderTest < ActiveRecord::TestCase Post.find(:first, :conditions => { :this_is_not_a_column_name => "fubar"}) end end + + def test_find_on_hash_conditions_with_polymorphic_belongs_to_sugar + groucho = members(:groucho) + assert Sponsor.find(:first, :conditions => { :sponsorable => groucho }) + + koz = people(:michael) + assert !koz.sponsor # koz has no sponsor, lol + assert !Sponsor.find(:first, :conditions => { :sponsorable => koz }) + end def test_find_on_association_proxy_conditions assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 0c57b79..5c8b48b 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -4,6 +4,7 @@ require 'models/customer' require 'models/company' require 'models/company_in_module' require 'models/subscriber' +require 'models/sponsor' class ReflectionTest < ActiveRecord::TestCase fixtures :topics, :customers, :companies, :subscribers @@ -166,6 +167,11 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end + def test_reflection_is_polymorphic? + assert Sponsor.reflections[:sponsorable].polymorphic? + assert !Client.reflections[:firm].polymorphic? + end + private def assert_reflection(klass, association, options) assert reflection = klass.reflect_on_association(association) diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 430d0b3..ee9c7dd 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -7,4 +7,6 @@ class Person < ActiveRecord::Base has_many :jobs, :through => :references has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true] has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id' + has_one :sponsor, :as => :sponsorable + end -- 1.5.3.7