Основываясь на других ответах, я ожидал, что это будет работать в Rails 4.1:
def update
@company = Company.find(params[:id])
# This separate step is required to change Single Table Inheritance types
new_type = params[:company][:type]
if new_type != @company.type && Company::COMPANY_TYPES.include?(new_type)
@company.becomes!(new_type.constantize)
@company.type = new_type
@company.save!
end
@company.update(company_params)
respond_with(@company)
end
Это не так, поскольку изменение типа не будет сохраняться.Вместо этого я выбрал менее элегантный подход, который работает правильно:
def update
@company = Company.find(params[:id])
# This separate step is required to change Single Table Inheritance types
new_type = params[:company][:type]
if new_type != @company.type && Company::COMPANY_TYPES.include?(new_type)
@company.update_column :type, new_type
end
@company.update(company_params)
respond_with(@company)
end
И вот тесты контроллера, которые я использовал для подтверждения решения:
describe 'Single Table Inheritance (STI)' do
class String
def articleize
%w(a e i o u).include?(self[0].to_s.downcase) ? "an #{self}" : "a #{self}"
end
end
Company::COMPANY_TYPES.each do |sti_type|
it "a newly assigned Company of type #{sti_type} " \
"should be #{sti_type.articleize}" do
post :create, { company: attributes_for(:company, type: sti_type) },
valid_session
expect(assigns(:company)).to be_a(sti_type.constantize)
end
end
Company::COMPANY_TYPES.each_index do |i|
sti_type, next_sti_type = Company::COMPANY_TYPES[i - 1],
Company::COMPANY_TYPES[i]
it "#{sti_type.articleize} changed to type #{next_sti_type} " \
"should be #{next_sti_type.articleize}" do
company = Company.create! attributes_for(:company, type: sti_type)
put :update, { id: company.to_param, company: { type: next_sti_type } },
valid_session
reloaded_company = Company.find(company.to_param)
expect(reloaded_company).to be_a(next_sti_type.constantize)
end
end
end