Pages

Monday, December 30, 2013

Single Table Inheritance(STI) in Rails 3

What is Single Table Inheritance?
In a nutshell, STI allows you to create sub-classes of a particular database table. Using a single table, you can cast rows to specific objects that extend the base model.
How to create STI relationships in Rails
Lets say we have a model Computer
 class Computer < ActiveRecord:Base
  # in app/models
  # Fields:
  #   String name
  #   String owner
  #   String manafacturer
  #   String color

  def default_browser
    "unknown!"
  end 
end

Now, we want to differentiate between Macs and PCs. It doesn’t really make sense to make a different table for each, since they both have pretty much the same columns. Instead we can create a new column, type, which tells Rails to use STI on Computer. Lets look at what the models might look like.
class Computer < ActiveRecord:Base
  # in app/models
  # Fields:
  #   String name
  #   String owner
  #   String manafacturer
  #   String color
  #   String type

  def default_browser
    "unknown!"
  end 
end

class Mac < Computer
  # in app/models
  # this is for Computers with type="Mac"
  before_save :set_color

  # Lets say all macs are silver, no point setting these ourselves
  def set_color
    self.color = "silver"
    self.manafacturer = "apple"
  end

  # Lets overwrite the default_browser method
  def default_browser
    "safari"
  end
end

class PC < Computer
  # in app/models  

  # Lets overwrite the default_browser method
  def default_browser
    "ie =("
  end
end
Anytime Rails opens up the computer object, it looks for the subclass corresponding to type. For instance,type="CoolComputer" corresponds to model CoolComputer < Computer.
How to use STI Models
To create a new mac, you can do:
m = Mac.new
m.name = "kunal's mac"
m.owner = "kunal"
m.save
m # => #<Mac id: 1, name: "kunal's mac", owner: "kunal", manafacturer: "apple", color: "silver", type: "Mac", ...>
Whats even cooler is ActiveRecord queries. Lets say we want all the computers
Computer.all # => [#<Mac id: 1, name: "kunal's mac", owner: "kunal", manafacturer: "apple", color: "silver", type: "Mac", ...>, #<Mac id: 2, name: "anuj's mac", owner: "anuj", manafacturer: "apple", color: "silver", type: "Mac", ...>, #<PC id: 3, name: "bob's pc", owner: "bob", manafacturer: "toshiba", color: "blue", type: "PC", ...>]
Yup, it automatically gives you the correct objects! You can find out the type of a particular object by calling.typeis_a? or .class
Computer.first.type == Mac # true
Computer.first.is_a? Mac # true
Computer.first.class == Mac # true
If we only want Macs, we can do
Mac.all

1 comment: