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