I have a model with "member", "group", "membership" and "user". The data is organized in a tree structure with groups that have members associated to them. Members without any group associations are considered orphaned and is of no use for the application.
When a user destroys a group there should not be left any orphaned members. In other words: a member should be destroyed if and only if the last group association is removed. Preferably this should happen in a single transaction, but the most important aspect is that orphaned object is not accumulated in the database over time.
Only members and groups that are associated with the user should be removed. Groups, members and memberships owned by other users should not be affected at all. (One might argue that a global cleanup method could be run anytime, but I want to isolate destructive operations to only affect the current users objects.)
My question: What is the most efficient and elegant way to implement this functionality in Rails 3? My current implementation, illustrated by the simplified model described here, does not remove a member from the database unless the user manually deletes it (or that the entire user and all his/her data is removed by cascade deletion.)
class User < ActiveRecord::Base
has_many :groups, :foreign_key => 'owner_id', :dependent => :delete_all
has_many :members, :foreign_key => 'owner_id', :dependent => :destroy
end
class Member < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :member
belongs_to :group
end
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
belongs_to :parent, :class_name => 'Group'
has_many :groups, :foreign_key => 'parent_id', :dependent => :destroy
has_many :memberships, :dependent => :destroy
has_many :members, :through => :memberships
end
I solved this by adding a callback in the Membership class, which notifies a Member when the membership is destroyed. The member object then destroys itself if it no longer has any group associations.
class Membership < ActiveRecord::Base
belongs_to :member
belongs_to :group
after_destroy :notify_member
def notify_member
member.destroy_if_empty_groups
end
end
class Member < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
def destroy_if_empty_groups
if groups.count == 0
self.destroy
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With