25

I'm currently using ActiveRecord single table inheritance.

How can I cast one of my models from type A to B? They have the same parent.

fotanus
  • 18,299
  • 11
  • 71
  • 106

4 Answers4

49

#becomes is what you are looking for:

http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-becomes

abbood
  • 21,507
  • 9
  • 112
  • 218
Omar Qureshi
  • 8,615
  • 3
  • 31
  • 34
  • It looks like becomes is now deprecated. Do you know if there is a method that can take its place - http://apidock.com/rails/ActiveRecord/Base/becomes – NatGordon Mar 13 '11 at 22:59
  • 3
    http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-becomes not deprecated, just moved. – Omar Qureshi Mar 14 '11 at 10:02
  • This is not casting. – schmijos Aug 23 '18 at 15:14
  • 2
    If you want to be pedantic, casting is the wrong word for use here, it achieves what this person wants. – Omar Qureshi Aug 23 '18 at 19:25
  • 1
    @schmijos, independently of how data is persisted, how is "converting one data type into another" not casting? omar, why is the word wrong? – estani Mar 16 '20 at 15:56
5

You shouldn't need to cast since Ruby does not perform any type-checking at compile time. What are you trying to accomplish?

Say you have a class Dad, and child classes Son and Daughter.

You could just have a variable @dad and store in it either a Son or Daughter object, and just treat it as if it were a Dad. As long as they respond to the same methods, it makes no difference. This is a concept called "duck typing".

Raffael
  • 2,491
  • 12
  • 14
Karl
  • 5,489
  • 5
  • 24
  • 38
  • 3
    I don't exactly remember why I did this question, but if I remember well it is needed when you have a STI relationship with a polymorphic association, or else it is saved with the wrong `type` field. – fotanus Feb 24 '14 at 16:16
  • Ruby does not, however, Rails does https://github.com/rails/rails/blob/1d08b98055508d00844cd30cbb68a4afa38a77a1/activerecord/lib/active_record/errors.rb#L14 – Artur Beljajev Feb 15 '18 at 14:23
  • Sometimes (usually) the child classes have more methods than the parent class, so if you need to access one of those methods, you have to cast it to the appropriate class. – mltsy Apr 15 '21 at 15:24
1

If we have something like following

class A < ApplicationRecord
end

Class B < A
end

we can use becomes

a = A.first
b = a.becomes(B)

or vice versa

-2

Create a new instance of B to setting the values for attributes it shares with A.

Something like:

class C < ActiveRecord::Base
end

class A < C
end

class B < C
end

@a = A.new(...)
@b = B.new(@a.attr1, @a.attr2, ..., @a.attrN)
bjg
  • 7,427
  • 1
  • 22
  • 21