Added customizable STI type serialization. #7541

Closed
wants to merge 1 commit into from

6 participants

@pivotalcommon

Rails will store the name of a child class in the database to use for re-instantiating the record as the correct class later. By specifying an inheritance_serializer and inheritance_deserializer, you can customize the stored identifier.

This is primarily useful for working with legacy data models.

class Foo < ActiveRecord::Base
  self.inheritance_serializer = ->(klass) do
    # Map the class to the appropriate type identifier.
    # Defaults to `klass.name`.
    if klass == Child1
      1
    elsif klass == Child2
      2
    end
  end

  self.inheritance_deserializer = ->(type_before_cast) do
    # Map the type identifier back into the appropriate class.
    # Defaults (approximately) to `type_before_cast.constantize`.
    case type_before_cast.to_i
    when 1
      Child1
    when 2
      Child2
    end
  end
end

class Child1 < Foo; end
class Child2 < Foo; end
Ian Lesperance & Matt Parker Added customizable STI type serialization.
Rails will store the name of a child class in the database
to use for re-instantiating the record as the correct class later.
By specifying an `inheritance_serializer` and
`inheritance_deserializer`, you can customize the stored identifier.

    class Foo < ActiveRecord::Base
      self.inheritance_serializer = ->(klass) do
        # Map the class to the appropriate type identifier.
        # Defaults to `klass.name`.
      end

      self.inheritance_deserializer = ->(type_before_cast) do
        # Map the type identifier back into the appropriate class.
        # Defaults (approximately) to `type_before_cast.constantize`.
      end
    end

This is primarily useful for working with legacy data models.
d3bfcba
@al2o3cr

Not sure what the best API would be, but some way to ask the class what name actually got written would be handy. For instance, you may need that string for SQL conditions, etc.

@pivotalcommon

Hi @al2o3cr - that actually exists already, it's called "sti_name", and it's a public method (MyModel.sti_name). It's been part of Rails for a while, just never really been documented to my knowledge.

@pivotalcommon

Also, the value will still be stored in record[Model.inheritance_column], so you can continue to access it that way.

@pivotalcommon

any word on whether or not this pull request might get accepted?

@steveklabnik
Ruby on Rails member

It needs rebased, for one. I don't use STI, so I can't comment on that...

@pixeltrix
Ruby on Rails member

I have to say I'm 👎 on this - I'm pretty sure that there's some association code that assumes that the stored value is a class name (though it may have been refactored away at some point by @jonleighton) and without some more tests around that area I wouldn't be confident of it working correctly.

I feel your legacy pain but I think in this case it isn't worth the maintenance cost of including it.

@al2o3cr

@pixeltrix - most of the code that needs to use an STI type column uses sti_name, as that's technically required for things to work correctly for both settings of store_full_sti_class. There are two places I could find (on a rough search) that still make assumptions:

At a minimum, the two points noted above should be fixed, so that people who want to override the existing behavior from outside (by, for instance, patching sti_name and find_sti_class) won't have problems.

@jonleighton
Ruby on Rails member

I am very -1 on this API. I think we should just make sti_name a publicly documented method and allow users to reimplement it to their needs as necessary. And fix the places where sti_name is not called but should be. Does that address the problems this PR is trying to solve?

@frodsan

@jonleighton any decision on this?

@steveklabnik
Ruby on Rails member

We have two core people with a 👎, and no answer in a month from @pivotalcommon. So I'm closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment