Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternatives to use polymorphism in Ruby on Rails

I'm currently writing some intranet web application where people could submit to admins requests for adding different resources. The example requests would be:

  • installing programs, in this case user will select which program he wants installed
  • increasing quota, in this case user will just enter the amount of disk space he needs or maybe he will select the predefined quantities - 1GB, 10GB etc...
  • create new email alias, in this case user will just type the alias.
  • ...

I was thinking about having just one model UserRequests with the reference to the sender and two optional attributes one would be reference_id that would refefrence to other tables (for example the Program that he wants installed) and another would be used for free type fields like email alias or quota.

So my problem is that based on the type of the request the model should contain either:

  • reference to other table
  • integer data
  • string data

Based on the type of the request the given action should be taken - probably email alias could be added from rails but the application on users computer will be installed by hand.

Does anyone had similar problem? Do you think using polymorphism for this kind of stuff is a good idea? Do you have any suggestions on how to organize data in the tables?

like image 553
Jakub Troszok Avatar asked May 29 '09 14:05

Jakub Troszok


2 Answers

Single Table Inheritance! This way you can have each type of request have custom validations, while still having every request live in the same table.

class CreateUserRequests < ActiveRecord::Migration
  def self.up
    create_table :user_requests do |t|
      t.string :string_data, :type
      t.integer :user_id, :integer_data
      t.timestamps
    end
  end
  def self.down
    drop_table :user_requests
  end
end


class UserRequest < ActiveRecord::Base
  belongs_to :user
end

class EmailAliasRequest < UserRequest
  validates_presence_of :string_data
  validates_format_of :string_data, :with => EMAIL_REGEX
end

class ProgramInstallRequest < UserRequest
  belongs_to :program, :class_name => "Program", :foreign_key => "integer_data"
  validates_presence_of :integer_data
end

class QuotaIncreaseRequest < UserRequest
  validates_presence_of :string_data
  validates_inclusion_of :string_data, :in => %w( 1GB 5GB 10GB 15GB )
end

And of course, alias your string_data and integer_data to email or whatnot to make your other code have a little more meaning. Let the model be the little black box that hides it all away.

like image 180
Ian Terrell Avatar answered Sep 28 '22 22:09

Ian Terrell


I would use polymorphic associations, which let a model belong to more than one other model using a single association. Something like this:

class AdminRequest < ActiveRecord::Base
  belongs_to :user
  belongs_to :requestable, :polymorphic => true
end

class EmailAlias < ActiveRecord::Base
  has_many :admin_requests, :as => :requestable
end

class ProgramInstall < ActiveRecord::Base
  has_many :admin_requests, :as => :requestable
end

class QuotaIncrease < ActiveRecord::Base
  has_many :admin_requests, :as => :requestable
end
  • As ever, Ryan Bates has an excellent Railscast on the subject.
like image 38
John Topley Avatar answered Sep 28 '22 20:09

John Topley