From 0ebe58ed686bd44273409b6c429c2d8c56f0f352 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@jeremyevans.net>
Date: Sat, 11 Jul 2009 11:09:06 -0700
Subject: [PATCH] Model associations now default to associating to classes in the same scope (Fixes #274)
This changes the default for the :class_name option to use a model in
the same scope as the current model. This is a slight breakage of
backwards compatibility, but should only affect users who are
associating a model inside a module to a model outside the module.
Defaulting to an object in the same scope leads to less surprising
behavior.
This patch also is a cleaner implementation, since the code no longer
varies per association type.
---
CHANGELOG | 2 ++
lib/sequel/model/associations.rb | 4 +---
lib/sequel/plugins/many_through_many.rb | 1 -
spec/extensions/many_through_many_spec.rb | 13 +++++++++++++
spec/model/associations_spec.rb | 20 ++++++++++++++++++++
5 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 07e2def..7b7750a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
=== HEAD
+* Model associations now default to associating to classes in the same scope (jeremyevans, nougad) (#274)
+
* Add Dataset#unlimited, similar to unfiltered and unordered (jeremyevans)
* Make Dataset#from_self take an options hash and respect an :alias option, giving the alias to use (Phrogz)
diff --git a/lib/sequel/model/associations.rb b/lib/sequel/model/associations.rb
index 24176af..275edbf 100644
--- a/lib/sequel/model/associations.rb
+++ b/lib/sequel/model/associations.rb
@@ -548,6 +548,7 @@ module Sequel
when Class
opts[:class_name] ||= opts[:class].name
end
+ opts[:class_name] ||= (opts[:model].name.split("::")[0..-2] + [camelize(opts.returns_array? ? singularize(name) : name)]).join('::')
send(:"def_#{type}", opts)
@@ -646,7 +647,6 @@ module Sequel
left = (opts[:left_key] ||= opts.default_left_key)
right = (opts[:right_key] ||= opts.default_right_key)
left_pk = (opts[:left_primary_key] ||= self.primary_key)
- opts[:class_name] ||= camelize(singularize(name))
opts[:cartesian_product_number] ||= 1
join_table = (opts[:join_table] ||= opts.default_join_table)
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
@@ -705,7 +705,6 @@ module Sequel
opts[:key] = opts.default_key unless opts.include?(:key)
key = opts[:key]
opts[:cartesian_product_number] ||= 0
- opts[:class_name] ||= camelize(name)
opts[:dataset] ||= proc do
klass = opts.associated_class
klass.filter(SQL::QualifiedIdentifier.new(klass.table_name, opts.primary_key)=>send(key))
@@ -750,7 +749,6 @@ module Sequel
model = self
key = (opts[:key] ||= opts.default_key)
primary_key = (opts[:primary_key] ||= self.primary_key)
- opts[:class_name] ||= camelize(singularize(name))
opts[:dataset] ||= proc do
klass = opts.associated_class
klass.filter(SQL::QualifiedIdentifier.new(klass.table_name, key) => send(primary_key))
diff --git a/lib/sequel/plugins/many_through_many.rb b/lib/sequel/plugins/many_through_many.rb
index bdb18fa..490c85d 100644
--- a/lib/sequel/plugins/many_through_many.rb
+++ b/lib/sequel/plugins/many_through_many.rb
@@ -125,7 +125,6 @@ module Sequel
name = opts[:name]
model = self
opts[:read_only] = true
- opts[:class_name] ||= camelize(singularize(name))
opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
opts[:cartesian_product_number] ||= 2
opts[:through] = opts[:through].map do |e|
diff --git a/spec/extensions/many_through_many_spec.rb b/spec/extensions/many_through_many_spec.rb
index 694430d..e547829 100644
--- a/spec/extensions/many_through_many_spec.rb
+++ b/spec/extensions/many_through_many_spec.rb
@@ -23,6 +23,19 @@ describe Sequel::Model, "many_through_many" do
Object.send(:remove_const, :Tag)
end
+ it "should default to associating to other models in the same scope" do
+ class ::AssociationModuleTest
+ class Artist < Sequel::Model
+ plugin :many_through_many
+ many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
+ end
+ class Tag < Sequel::Model
+ end
+ end
+
+ ::AssociationModuleTest::Artist.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
+ end
+
it "should raise an error if in invalid form of through is used" do
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id]]}.should raise_error(Sequel::Error)
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], {:table=>:album_tags, :left=>:album_id}]}.should raise_error(Sequel::Error)
diff --git a/spec/model/associations_spec.rb b/spec/model/associations_spec.rb
index d02725a..6a9885a 100644
--- a/spec/model/associations_spec.rb
+++ b/spec/model/associations_spec.rb
@@ -16,6 +16,26 @@ describe Sequel::Model, "associate" do
klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
end
+ it "should default to associating to other models in the same scope" do
+ class ::AssociationModuleTest
+ class Album < Sequel::Model
+ many_to_one :artist
+ many_to_many :tags
+ end
+ class Artist< Sequel::Model
+ one_to_many :albums
+ end
+ class Tag < Sequel::Model
+ many_to_many :albums
+ end
+ end
+
+ ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
+ ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
+ ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
+ ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
+ end
+
it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
MODEL_DB.reset
klass = Class.new(Sequel::Model(:nodes)) do
--
1.6.1.3