From b2131eefe502a9121595f727f46fd38b4f221b4e Mon Sep 17 00:00:00 2001
From: John Firebaugh <john_firebaugh@bigfix.com>
Date: Wed, 10 Mar 2010 11:33:05 -0800
Subject: [PATCH] Support singular association names when using :one_to_one.

---
 lib/sequel/model/associations.rb |   14 +++++---------
 spec/model/associations_spec.rb  |   25 ++++++++++---------------
 2 files changed, 15 insertions(+), 24 deletions(-)

diff --git a/lib/sequel/model/associations.rb b/lib/sequel/model/associations.rb
index f6b98b6..4f4a037 100644
--- a/lib/sequel/model/associations.rb
+++ b/lib/sequel/model/associations.rb
@@ -443,9 +443,7 @@ module Sequel
         # :one_to_one option specified on the table without the foreign key.  The
         # two associations will operate similarly, except that the many_to_one
         # association setter doesn't update the database until you call save manually.
-        # Also, in most cases you need to specify the plural association name when using
-        # one_to_many with the :one_to_one option.
-        # 
+        #
         # The following options can be supplied:
         # * *ALL types*:
         #   - :after_add - Symbol, Proc, or array of both/either specifying a callback to call
@@ -547,8 +545,7 @@ module Sequel
         #     association methods usually added are either removed or made private,
         #     so using this is similar to using many_to_one, in terms of the methods
         #     it adds, the main difference is that the foreign key is in the associated
-        #     table instead of the current table.  Note that using this option still requires
-        #     you to use a plural name when creating and using the association (e.g. for reflections, eager loading, etc.).
+        #     table instead of the current table.
         #   - :primary_key - column in the current table that :key option references, as a symbol.
         #     Defaults to primary key of the current table. Can use an
         #     array of symbols for a composite key association.
@@ -899,11 +896,10 @@ module Sequel
             end
           end
           if opts[:one_to_one]
-            overridable_methods_module.send(:private, opts.association_method, opts.dataset_method)
+            overridable_methods_module.send(:private, opts.dataset_method)
             n = singularize(name).to_sym
-            raise(Sequel::Error, "one_to_many association names should still be plural even when using the :one_to_one option") if n == name
-            association_module_def(n) do |*o|
-              objs = send(name, *o)
+            association_module_def(n) do |*reload|
+              objs = load_associated_objects(opts, reload[0])
               raise(Sequel::Error, "multiple values found for a one-to-one relationship") if objs.length > 1
               objs.first
             end
diff --git a/spec/model/associations_spec.rb b/spec/model/associations_spec.rb
index cc9c462..c1aff81 100644
--- a/spec/model/associations_spec.rb
+++ b/spec/model/associations_spec.rb
@@ -1134,7 +1134,7 @@ describe Sequel::Model, "one_to_many" do
   end
 
   it "should add a getter method if the :one_to_one option is true" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true
     att = @c2.new(:id => 1234).attribute
     MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
     att.should be_a_kind_of(@c1)
@@ -1142,21 +1142,21 @@ describe Sequel::Model, "one_to_many" do
   end
 
   it "should not add a setter method if the :one_to_one option is true and :read_only option is true" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :read_only=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true, :read_only=>true
     im = @c2.instance_methods.collect{|x| x.to_s}
     im.should(include('attribute'))
     im.should_not(include('attribute='))
   end
 
   it "should have the getter method raise an error if more than one record is found" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true
     d = @c1.dataset
     def d.fetch_rows(s); 2.times{yield Hash.new} end
     proc{@c2.new(:id => 1234).attribute}.should raise_error(Sequel::Error)
   end
 
   it "should add a setter method if the :one_to_one option is true" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true
     attrib = @c1.new(:id=>3)
     d = @c1.dataset
     @c1.class_eval{remove_method :_refresh}
@@ -1175,7 +1175,7 @@ describe Sequel::Model, "one_to_many" do
   end
 
   it "should use a transaction in the setter method if the :one_to_one option is true" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true
     @c2.use_transactions = true
     MODEL_DB.sqls.clear
     attrib = @c1.load(:id=>3)
@@ -1187,7 +1187,7 @@ describe Sequel::Model, "one_to_many" do
   end
 
   it "should have the setter method for the :one_to_one option respect the :primary_key option" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :primary_key=>:xxx
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true, :primary_key=>:xxx
     attrib = @c1.new(:id=>3)
     d = @c1.dataset
     @c1.class_eval{remove_method :_refresh}
@@ -1206,7 +1206,7 @@ describe Sequel::Model, "one_to_many" do
     end
     
   it "should have the setter method for the :one_to_one option respect composite keys" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :key=>[:node_id, :y], :primary_key=>[:id, :x]
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true, :key=>[:node_id, :y], :primary_key=>[:id, :x]
     attrib = @c1.load(:id=>3, :y=>6)
     d = @c1.dataset
     def d.fetch_rows(s); yield({:id=>3, :y=>6}) end
@@ -1215,22 +1215,17 @@ describe Sequel::Model, "one_to_many" do
     MODEL_DB.sqls.last.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
   end
 
-  it "should raise an error if the one_to_one getter would be the same as the association name" do
-    proc{@c2.one_to_many :song, :class => @c1, :one_to_one=>true}.should raise_error(Sequel::Error)
-  end
-
   it "should not create remove_ and remove_all methods if :one_to_one option is used" do
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true
     @c2.new.should_not(respond_to(:remove_attribute))
     @c2.new.should_not(respond_to(:remove_all_attributes))
   end
 
   it "should make non getter and setter methods private if :one_to_one option is used" do 
-    @c2.one_to_many :attributes, :class => @c1, :one_to_one=>true do |ds| end
+    @c2.one_to_many :attribute, :class => @c1, :one_to_one=>true do |ds| end
     meths = @c2.private_instance_methods.collect{|x| x.to_s}
-    meths.should(include("attributes"))
     meths.should(include("add_attribute"))
-    meths.should(include("attributes_dataset"))
+    meths.should(include("attribute_dataset"))
   end
 
   it "should call an _add_ method internally to add attributes" do
-- 
1.6.5.3

