I made a big work refactoring tonnes of code, and also made big changes to db schema. And now I am trying to write a rake task to migrate records from old tables to a new one.
I am having such classes:
#app/models/restream/service.rb
class Restream::Service < ActiveRecord::Base
def self.types
%w(custom multiple_destinations_service one_destination_service) +
Restream::Custom.types + Restream::MultipleDestinationsService.types
end
def self.find_sti_class(type_name) #allows to find classes by short names
type_name = "Restream::#{type_name.camelcase}".constantize
super
end
end
#app/models/restream/custom.rb
class Restream::Custom < Restream::Service
def self.sti_name
"custom"
end
def self.types
%w(periscope odnoklassniki vkontakte)
end
end
#app/models/restream/periscope.rb
class Restream::Periscope < Restream::Custom
def self.sti_name
"periscope"
end
end
Everything works just fine. Until I'm trying to add records manually. In my previous version I had such a structure:
class Restream::Custom < ActiveRecord::Base
def self.types; %w(custom periscope vkontakte); end
end
class Restream::Periscope < Restream::Custom
def self.sti_name; 'periscope'; end
end
And now I'm trying simply get all records from old restream_custom
table and just copy type. Roughly:
Restream::Custom.create(type: old_restream_custom.type)
And that fails saying:
ActiveRecord::SubclassNotFound: Invalid single-table inheritance type: periscope is not a subclass of Restream::Custom
It's obviously not! But anyway I already have a bunch of records with type: 'periscope'
, so that I know it's a valid value.
What's the reason for this, and how can I fix this behaviour?
======
I can see two ways:
1) Set type
to Restream::Periscope
, not just a periscope
. But that creates records, that can't be found by Restream::Periscope.find_each
or Restream::Custom.find_each
or smth like that, 'cause it will search for records with periscope
in its type
column, not a Restream::Periscope
.
2) Select from restream_custom
table only records with each types of custom
, periscope
, etc. and create Restream::Periscope
for periscopes, not Restream::Custom
and trying to provide a correct type here. But I found it kind of unpretty, not-DRY and unnecessary, and wonder if I can do smth more beautiful with it.
If it is not too big of a refactor, I would go with your first option 1) to set type
to Restream::Periscope
and not periscope
primarily just because it is the Rails convention.
If option 1) is implemented, you said your other concern about this which is that Restream::Periscope.find_each
will no longer return records of the other "types" and will be automatically filtered accordingly depending on the subclass... does makes sense because your .find_each
is querying upon Restream::Periscope
, and thus it is intuitive that I will be expecting that all of the returned records will be of type
"Restream::Periscope"
.
Now if you'd like to query for "all types", then you may just query on the "parent" class which is the Restream::Service
, in which you can now do the following:
Restream::Service.find_each(type: old_restream_custom.type)
# or
Restream::Service.create(type: old_restream_custom.type)
This is my suggestion, unless of course it is really a big task refactoring all of your code. Hope this somehow helps.