#394 STI and Polymorphic Associations pro
Nov 25, 2012 | 14 minutes | Active Record, Refactoring
Single Table Inheritance (STI) can help organize branching logic into separate classes, but a polymorphic association may be a better fit if there are unique database columns.
- Download:
- source codeProject Files in Zip (112 KB)
- mp4Full Size H.264 Video (29.5 MB)
- m4vSmaller H.264 Video (15.8 MB)
- webmFull Size VP8 Video (19.7 MB)
- ogvFull Size Theora Video (33.8 MB)
Resources
- Single Table Inheritance
- Polymorphic Associations
- Episode 154: Polymorphic Association (revised)
- Episode 392: A Tour of State Machines
Single Table Inheritance Solution
terminal
rails g migration add_type_to_users type rails g migration remove_guest_from_users guest:boolean rake db:migrate
models/user.rb
class User < ActiveRecord::Base has_many :tasks, dependent: :destroy end
models/guest.rb
class Guest < User def guest? true end def move_to(user) tasks.update_all(user_id: user.id) end def name "Guest" end def task_limit 10 end def can_share_task?(task) false end def send_password_reset end end
models/member.rb
class Member < User attr_accessible :username, :email, :password, :password_confirmation validates_presence_of :username, :email validates_uniqueness_of :username, allow_blank: true has_secure_password def guest? false end def name username end def task_limit 1000 end def can_share_task?(task) task.user_id == id end def send_password_reset UserMailer.password_reset(self).deliver end end
Polymorphic Association Solution
terminal
rails g migration remove_member_from_users username email password_digest type rails g model MemberProfile username email password_digest rails g model GuestProfile rails g migration add_profile_to_users profile_id:integer profile_type
models/user.rb
class User < ActiveRecord::Base has_many :tasks, dependent: :destroy belongs_to :profile, polymorphic: true delegate :guest?, :name, :can_share_task?, :send_password_reset, :become_member, to: :profile end
models/guest_profile.rb
class GuestProfile < ActiveRecord::Base has_one :user, as: :profile, dependent: :destroy def guest? true end def become_member(member_profile) user.profile = member_profile user.save! end def name "Guest" end def task_limit 10 end def can_share_task?(task) false end def send_password_reset end end
models/member_profile.rb
class MemberProfile < ActiveRecord::Base has_one :user, as: :profile, dependent: :destroy attr_accessible :username, :email, :password, :password_confirmation validates_presence_of :username, :email validates_uniqueness_of :username, allow_blank: true has_secure_password def guest? false end def name username end def task_limit 1000 end def can_share_task?(task) task.user_id == user.id end def send_password_reset UserMailer.password_reset(self).deliver end end