views:

312

answers:

1

I'm using single table inheritance for my application. My polymorphic type is Maintenance with only one subtype, right now, named OilChange. I'm running into problems creating my records in my create method in the controller. Here's the code.

@log = Log.new(params[:log])
@log.maintenance = Maintenance.new(params[:maintenance])

The params[:maintenance] hash has keys {:name, :type}. I can verify their existence and values by printing them out as follows

print params[:maintenance][:name]
print params[:maintenance][:type]

If I pass in "OilChange" for the value of the :type key, the Maintenance record is of type Maintenance and not of OilChange. I can verify that by finding the record in the REPL console. The type field is nil. I can make it work like I want by adding the following line.

@log.maintenance.type = params[:maintenance][:type]

But that's ugly. What I'm wondering is why doesn't the create method set the type field as it does the name field just find?

The two types you see look like this in my schema.rb

create_table "logs", :force => true do |t|
  t.date     "date"
  t.text     "description"
  t.string   "title"
  t.string   "summary"
  t.integer  "car_id"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.integer  "maintenance_id"
  t.integer  "mileage"
end

create_table "maintenances", :force => true do |t|
  t.string   "name"
  t.string   "type"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "oil_brand"
  t.string   "oil_type"
  t.string   "oil_filter_type"

My models look like this.

class Log < ActiveRecord::Base
belongs_to :car
has_and_belongs_to_many :tags
  belongs_to :maintenance
end

class Maintenance < ActiveRecord::Base
  has_one :log
end

class OilChange < Maintenance
end

TIA!

+1  A: 

The specific answer is that the type attribute, like many of Rails's special attributes, is protected from mass assignment. (Look up :attr_protected in the documentation.)

The answer to the more general problem is that you're not trusting your models enough. If you want to create a record of type OilChange, you shouldn't call Maintenance.new or Maintenance.create. Call OilChange.new or OilChange.create instead, and Rails will automatically take care of setting the type and doing all the background work for you.

SFEley
I would love to do that, but the type of maintenance is based on user input. i just don't know what it's going to be at compile time
dharga
Doesn't matter. Ruby's dynamic. >8-> Let's say you have a "Maintenance Type" dropdown. Just pass back values from that dropdown like "OilChange", and you can call `params[:maintenance_type].constantize.new` to get your object. (_constantize_ is a method provided by ActiveSupport.)
SFEley