diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a3d1bbb..6eb7542 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -124,6 +124,7 @@ module ActiveRecord # #create_other(attributes={}) | X | | X # #other.create!(attributes={}) | | | X # #other.nil? | X | X | + # #other? | X | X | X # # ===Collection associations (one-to-many / many-to-many) # | | | has_many @@ -144,6 +145,7 @@ module ActiveRecord # #others.count | X | X | X # #others.sum(args*,&block) | X | X | X # #others.empty? | X | X | X + # #others? | X | X | X # #others.clear | X | X | X # #others.delete(other,other,...) | X | X | X # #others.delete_all | X | X | @@ -626,6 +628,7 @@ module ActiveRecord # are associated with :dependent => :destroy, deletes them directly from the database if :dependent => :delete_all, # otherwise sets their foreign keys to +NULL+. # * collection.empty? - Returns +true+ if there are no associated objects. + # * collection? - Returns +false+ if there are no associated objects, true if there are associated objects # * collection.size - Returns the number of associated objects. # * collection.find - Finds an associated object according to the same rules as Base.find. # * collection.build(attributes = {}, ...) - Returns one or more new objects of the collection type that have been instantiated @@ -644,6 +647,7 @@ module ActiveRecord # * Firm#client_ids= # * Firm#clients.clear # * Firm#clients.empty? (similar to firm.clients.size == 0) + # * Firm#clients? (similar to !firm.clients.empty?) # * Firm#clients.size (similar to Client.count "firm_id = #{id}") # * Firm#clients.find (similar to Client.find(id, :conditions => "firm_id = #{id}")) # * Firm#clients.build (similar to Client.new("firm_id" => id)) @@ -727,6 +731,7 @@ module ActiveRecord # * association=(associate) - Assigns the associate object, extracts the primary key, sets it as the foreign key, # and saves the associate object. # * association.nil? - Returns +true+ if there is no associated object. + # * association? - Returns +false+ if there is no associated object. # * build_association(attributes = {}) - Returns a new object of the associated type that has been instantiated # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if # an association already exists. It will NOT work if the association is +nil+. @@ -737,6 +742,7 @@ module ActiveRecord # * Account#beneficiary (similar to Beneficiary.find(:first, :conditions => "account_id = #{id}")) # * Account#beneficiary=(beneficiary) (similar to beneficiary.account_id = account.id; beneficiary.save) # * Account#beneficiary.nil? + # * Account#beneficiary? # * Account#build_beneficiary (similar to Beneficiary.new("account_id" => id)) # * Account#create_beneficiary (similar to b = Beneficiary.new("account_id" => id); b.save; b) # @@ -814,6 +820,7 @@ module ActiveRecord # * association(force_reload = false) - Returns the associated object. +nil+ is returned if none is found. # * association=(associate) - Assigns the associate object, extracts the primary key, and sets it as the foreign key. # * association.nil? - Returns +true+ if there is no associated object. + # * association? - Returns +false+ if there is no associated object. # * build_association(attributes = {}) - Returns a new object of the associated type that has been instantiated # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. # * create_association(attributes = {}) - Returns a new object of the associated type that has been instantiated @@ -824,6 +831,7 @@ module ActiveRecord # * Post#author=(author) (similar to post.author_id = author.id) # * Post#author? (similar to post.author == some_author) # * Post#author.nil? + # * Post#author? # * Post#build_author (similar to post.author = Author.new) # * Post#create_author (similar to post.author = Author.new; post.author.save; post.author) # The declaration can also include an options hash to specialize the behavior of the association. @@ -969,6 +977,7 @@ module ActiveRecord # * collection_singular_ids=ids - Replace the collection by the objects identified by the primary keys in +ids+. # * collection.clear - Removes every object from the collection. This does not destroy the objects. # * collection.empty? - Returns +true+ if there are no associated objects. + # * collection? - Returns +false+ if there are no associated objects. # * collection.size - Returns the number of associated objects. # * collection.find(id) - Finds an associated object responding to the +id+ and that # meets the condition that it has to be associated with this object. @@ -986,6 +995,7 @@ module ActiveRecord # * Developer#project_ids= # * Developer#projects.clear # * Developer#projects.empty? + # * Developer#projects? # * Developer#projects.size # * Developer#projects.find(id) # * Developer#projects.build (similar to Project.new("project_id" => id)) @@ -1115,6 +1125,15 @@ module ActiveRecord association.target = target instance_variable_set(ivar, association) end + + define_method("#{reflection.name.to_s}?") do + if association_proxy_class == HasOneThroughAssociation + !send(reflection.name).empty? + else + !send(reflection.name).nil? + end + end + end def collection_reader_method(reflection, association_proxy_class) @@ -1150,6 +1169,10 @@ module ActiveRecord association end + define_method("#{reflection.name.to_s}?") do + !send(reflection.name).empty? + end + define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| ids = (new_value || []).reject { |nid| nid.blank? } send("#{reflection.name}=", reflection.class_name.constantize.find(ids)) diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index e0da8bf..1b2e3d1 100755 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -409,4 +409,16 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase sponsor.sponsorable = new_member assert_equal nil, sponsor.sponsorable_id end + + def test_method_name_with_question_mark_should_return_false_when_nil + client = Client.new + assert_equal false, client.firm? + end + + def test_method_name_with_question_mark_should_return_true_when_not_nil + client = Client.new + client.firm = Firm.find(:first) + assert_equal true, client.firm? + end + end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 294b993..057f516 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -681,4 +681,23 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal developer, project.developers.find(:first) assert_equal project, developer.projects.find(:first) end + + def test_method_name_with_question_mark_should_return_false_when_empty + developer = DeveloperWithSymbolsForKeys.new(:name => 'David') + project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing') + + assert_equal 0, project.developers.size + assert_equal false, project.developers? + end + + def test_method_name_with_question_mark_should_return_true_when_not_emtpy + developer = DeveloperWithSymbolsForKeys.new(:name => 'David') + project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing') + project.developers << developer + project.save! + + assert_equal 1, project.developers.size + assert_equal true, project.developers? + end + end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index dbfa025..c1cde59 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -929,4 +929,19 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert firm.clients.loaded? end + def test_method_name_with_question_mark_should_return_false_when_empty + firm = companies(:first_firm) + firm.clients.clear + + assert_equal 0, firm.clients.length + assert_equal false, firm.clients? + end + + def test_method_name_with_question_mark_should_return_true_when_not_emtpy + firm = companies(:first_firm) + + assert_equal true, firm.clients.length > 0 + assert_equal true, firm.clients? + end + end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 05155f6..878bdeb 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -187,4 +187,31 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase post.people_with_callbacks.clear assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort end + + def test_method_name_on_join_model_with_question_mark_should_return_false_when_empty + post = Post.new + assert_equal 0, post.readers.length + assert_equal false, post.readers? + end + + def test_method_name_on_join_model_with_question_mark_should_return_true_when_not_empty + post = Post.new + post.readers << Reader.find(:first) + assert_equal true, post.readers.length > 0 + assert_equal true, post.readers? + end + + def test_method_name_with_question_mark_should_return_false_when_empty + post = Post.new + assert_equal 0, post.people.length + assert_equal false, post.people? + end + + def test_method_name_with_question_mark_should_return_true_when_not_empty + post = posts(:thinking) + post.readers << Reader.find(:first) + assert_equal true, post.people.length > 0 + assert_equal true, post.people? + end + end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index abc7ee7..b23f8e5 100755 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -320,4 +320,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert companies(:first_firm).readonly_account.readonly? end + def test_method_name_with_question_mark_should_return_false_when_nil + firm = Firm.find(:first) + firm.account = nil + assert_equal false, firm.account? + end + + def test_method_name_with_question_mark_should_return_true_when_not_nil + firm = Firm.find(:first) + firm.account = Account.find(:first) + assert_equal true, firm.account? + end + end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 3eb66bc..7311d75 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -71,4 +71,27 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase assert_not_nil assert_no_queries {clubs[0].sponsored_member} end + def test_method_name_on_join_model_with_question_mark_should_return_false_when_nil + new_member = Member.new(:name => "Chris") + assert_equal false, new_member.memberships? + end + + def test_method_name_on_join_model_with_question_mark_should_return_true_when_not_nil + new_member = Member.create(:name => "Chris") + new_member.club = Club.create(:name => "LRUG") + new_member.reload + assert_equal true, new_member.memberships? + end + + def test_method_name_with_question_mark_should_return_false_when_nil + new_member = Member.new(:name => "Chris") + assert_equal false, new_member.club? + end + + def test_method_name_with_question_mark_should_return_true_when_not_nil + new_member = Member.create(:name => "Chris") + new_member.club = Club.create(:name => "LRUG") + assert_equal true, new_member.club? + end + end