Implementing STI in Ruby on Rails, how can I duplicate an object from one class to the other?

245 Views Asked by At

I implemented STI to separate 2 types of Skill objects: DefinedSkill and DeployedSkill. They are physically very close, but are managed different ways.

One method of the DefinedSkill is the deploy method, which creates a nearly identical DeployedSkill.

Initially, I wrote this:

def deploy
  @template_skill = DefinedSkill.find(params[:id])
  if @template_skill.status.code == "ACCEPTED"
    @deployed_skill = @template_skill.deep_clone include: [:translations]
    @deployed_skill.type = 'DeployedSkill'
---
    @deployed_skill.save
  end
end

But this produces an object of DefinedSkill class, even though I try to assign the type attribute.

Then I tried to work at attributes level, and wrote this:

def deploy
  @template_skill = DefinedSkill.find(params[:id])
  if @template_skill.status.code == "ACCEPTED"
    @deployed_skill = DeployedSkill.new(@template_skill.attributes.except(:id, :type))
    # @deployed_skill.type = 'DeployedSkill' (useless as type is managed by STI feature)
---
    @deployed_skill.save
  end
end

But this produces the following error:

ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: DefinedSkill is not a subclass of DeployedSkill)

So this is my question: how can I create an object of a sibling class in the context of STI?

2

There are 2 best solutions below

0
user1185081 On BEST ANSWER

Many thanks to msencenb and felipeecst for putting me on the way. After reading the doc and trying, I came to the conclusion that the existing object could not be converted, but class conversion should be done when creating the new instance.

The solution I applied is:

@deployed_skill = @template_skill.becomes!(DeployedSkill).deep_clone include: [:translations]

which solved my issue.

0
felipeecst On

Instead of setting @deployed_skill = type, try using the becomes method:

@deployed_skill.becomes(DeployedSkill)