Rails STI and Polymorphism in Ember JS

644 views Asked by At

My Setup:

Rails 4.1 with active-model-serializer gem
Ember 1.9.0
Ember-Data 1.0.0 beta with ActiveModelAdapter used as ApplicationAdapter

I have an STI table called Jobs that has many subtypes. Each Job hasMany LineItems. I am using ActiveModelSerializers in Rails to serialize my JSON data in a way that Ember-Data understands. My Models are set up this way in Rails:

class Job < ActiveRecord::Base
  has_many :line_items
  ...
end
class DisposalJob < Job
  ...
end
class SupplyJob < Job
  ...
end
class LineItem < ActiveRecord::Base
  belongs_to :job, polymorphic: true
  ...
end

And this way in Ember:

App.Job = DS.Model.extend
  line_items: DS.hasMany "line_item"
  ...

App.DisposalJob = App.Job.extend
  ...

App.SupplyJob = App.Job.extend
  ...

App.LineItem = DS.Model.extend
  job: DS.belongsTo "job",
    polymorphic: true

My serializers in Rails are set up like so:

class BaseSerializer < ActiveModel::Serializer
  ActiveModel::Serializer.setup do |config|
    config.embed = :ids
    config.embed_in_root = true
  end
end

class JobSerializer < BaseSerializer
  ...
  has_many :line_items
end

class DisposalJobSerializer < JobSerializer
  ...
end

class SupplyJobSerializer < JobSerializer
  ...
end

class LineItemSerializer < BaseSerializer
  belongs_to :job, polymorphic: true
end

My problem is when I try to save a line_item to a job subclass (I don't use the Job class directly), Ember refuses to see that a DisposalJob is a type of job and throws an error that it expects a "Job" record to be saved to that relation, rather than a "DisposalJob" or "SupplyJob", despite that these are both subclasses of Job. I'm an Ember newbie, so any help would be greatly appreciated.

EDIT: Here is the error I get when trying to save a DisposalJob to the Job relation on LineItem in Ember:

 Uncaught Error: Assertion Failed: You can only add a 'job' record to this relationship

EDIT: I've tried refactoring this so that line_item accepts both job_id and job_type instead of just job_id. Ember still tells me it's expecting a "job" record in this relation. I've tried skirting the issue by eliminating the "belongsTo on the ember side and just have line_item include job_id: DS.attr "number" and job_type: DS.attr "string". This allows me to save the record, but without the belongsTo association, the line-items won't be found when Ember displays the job they are attached to.

1

There are 1 answers

1
Andrew Hacking On BEST ANSWER

If you have MODEL_FACTORY_INJECTIONS enabled you will not be able to use subclasses as the prototype based inheritance checks fail Issue #5573 and Issue #2342 and Issue #2316.

I found doing STI at the model layer with Ember.Data and indeed Ember.Model is not really workable for the following reasons:

  1. Each model subclass is a separate collection as far as the store is concerned.
  2. Becasue of 1. you can't query the store to get an aggregated set of results which includes all (or only some) subclasses. You could in theory workaround this by querying the store for each of the desired subclasses and merging the result using Ember.computed.union() / Ember.computed.uniq() but its not going to be efficient.
  3. Ember cannot dynamically extend object instances with mixins that have properties, which would be another way to handle STI by extending a model instance based on the type attribute in the model data.

So what you're left with is to treat things kind of like you do in the DB on your server, that is support all possibilities in your data model. This means just define all possible relationships and attributes.

When it comes to validation and other business rules, controllers can provide the façade you need. The Ember way is for controllers to decorate models and you can certainly create different classes of controller to decorate models how you please.