Creating inheritance records for individual tables in Rails

I am using unidirectional inheritance for my application. My polymorphic type is a service with only one subtype called OilChange. I am having problems creating my entries in my create method in a controller. Here is the code.

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

      

The params [: maintenance] hash has keys {: name ,: type}. I can check their existence and values ​​by printing them like this:

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

      

If I go to "OilChange" for a key value of type:, the maintenance record is of type Maintenance, not OilChange. I can check by finding the entry in the REPL console. Field of type nil. I can get it to work as I want by adding the following line.

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

      

But this is ugly. I am wondering why the create method does not set the type field as it just finds the name?

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

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!

+2


source to share


2 answers


The concrete answer is that the attribute type

, like many Rails special attributes, is protected from mass assignment. (Look :attr_protected

in the documentation.)



The answer to a more general problem is that you don't trust your models enough. If you want to create a record of type OilChange, you do not need to name Maintenance.new

or Maintenance.create

. Call OilChange.new

or OilChange.create

instead and Rails will automatically take care of setting up the type and doing all the background work for you.

+3


source


Try using the following code:

begin
  klass = Module.const_get(params[:maintenance][:type])
  @log.maintenance = klass.new(params[:maintenance])
  if ! @log.maintenance.is_a?(Maintenance)
    @log.maintenance = Maintenance.new(params[:maintenance])
  end
rescue NameError
  @log.maintenance = Maintenance.new(params[:maintenance])
end

      

If you only want to have Maintenance subclasses in your database, follow these steps in your Maintenance class:



class Maintenance < ActiveRecord::Base
  validates_presence_of :type
  # other stuff goes here
end

      

This code will create an invalid service class if the parameters are of the wrong type. If they contain the correct type, an instance of the specified class will be created.

If your models use namespaces, you can look at this article instead of using Module.const_get

:

0


source







All Articles