Polymorphic :has_many, :through as module in Rails 3.1 plugin
- by JohnMetta
I've search everywhere for a pointer to this, but can't find one. Basically, I want to do what everyone else wants to do when they create a polymorphic relationship in a :has_many, :through way… but I want to do it in a module. I keep getting stuck and think I must be overlooking something simple.
To wit:
module ActsPermissive
module PermissiveUser
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_permissive
has_many :ownables
has_many :owned_circles, :through => :ownables
end
end
end
class PermissiveCircle < ActiveRecord::Base
belongs_to :ownable, :polymorphic => true
end
end
With a migration that looks like this:
create_table :permissive_circles do |t|
t.string :ownable_type
t.integer :ownable_id
t.timestamps
end
The idea, of course, is that whatever loads acts_permissive will be able to have a list of circles that it owns.
For simple tests, I have
it "should have a list of circles" do
user = Factory :user
user.owned_circles.should be_an_instance_of Array
end
which fails with:
Failure/Error: @user.circles.should be_an_instance_of Array
NameError: uninitialized constant User::Ownable
I've tried: using :class_name => 'ActsPermissive::PermissiveCircle' on the has_many :ownables line, which fails with:
Failure/Error: @user.circles.should be_an_instance_of Array
ActiveRecord::HasManyThroughSourceAssociationNotFoundError:
Could not find the source association(s) :owned_circle or
:owned_circles in model ActsPermissive::PermissiveCircle.
Try 'has_many :owned_circles, :through => :ownables,
:source => <name>'. Is it one of :ownable?
while following the suggestion and setting :source => :ownable fails with
Failure/Error: @user.circles.should be_an_instance_of Array
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError:
Cannot have a has_many :through association 'User#owned_circles'
on the polymorphic object 'Ownable#ownable'
Which seems to suggest that doing things with a non-polymorphic-through is necessary. So I added a circle_owner class similar to the setup here:
module ActsPermissive
class CircleOwner < ActiveRecord::Base
belongs_to :permissive_circle
belongs_to :ownable, :polymorphic => true
end
module PermissiveUser
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_permissive
has_many :circle_owners, :as => :ownable
has_many :circles, :through => :circle_owners,
:source => :ownable,
:class_name => 'ActsPermissive::PermissiveCircle'
end
end
class PermissiveCircle < ActiveRecord::Base
has_many :circle_owners
end
end
With a migration:
create_table :permissive_circles do |t|
t.string :name
t.string :guid
t.timestamps
end
create_table :circle_owner do |t|
t.string :ownable_type
t.string :ownable_id
t.integer :permissive_circle_id
end
which still fails with:
Failure/Error: @user.circles.should be_an_instance_of Array
NameError:
uninitialized constant User::CircleOwner
Which brings us back to the beginning.
How can I do what seems to be a rather common polymorphic :has_many, :through on a module?
Alternatively, is there a good way to allow an object to be collected by arbitrary objects in a similar way that will work with a module?