json_api_client icon indicating copy to clipboard operation
json_api_client copied to clipboard

has_many infers Class from the association name, not the returned type

Open JohnSmall opened this issue 8 years ago • 2 comments

The problem We have a hierarchy over a table with records that use single table inheritance. That means the relation

 has_many :children

Can return any of the types we use. e.g. 'series', 'episode'. But the relation tries to find the class 'Child' and fails because no such class is defined. If I set the class to be the parent class for all the different types like this

has_many :children, class_name: 'Programme'

Then it forces the each returned item to be instances of the root class 'Programme' and sets the value of #type to be 'programmes'. Even though the underlying api actually returns the correct type. And then other things fail because they expect to see instances of the correct class with #type as what's returned from the api

The solution The class should be inferred from the returned type not from the association name, unless overridden by class_name etc. It's fair to infer the type from the association name if the api doesn't return the type. But if it doesn't return the type then it breaks the JSON-API standard.

The order of precedence for working out the class, and setting the value of #type should be

  1. If the class name is explicitly given in the has_many declaration then use that
  2. If the returned type can be translated into a class name then use that. I.e. type.classify.constantize should exist
  3. If the api breaks the JSON-API and doesn't return a type, then work it out from the association name

JohnSmall avatar Jan 27 '17 15:01 JohnSmall

It should be possible to pass a class name inferrer to solve the problem of polymorphism, so class_name could become a proc that yields the actual json:api document and then it's possible to do whatever you want to resolve the class name

eg

class MyApi
  class User
    CHILD_INFERRER = lambda do |jsonapi_doc|
      case jsonapi_doc[:type]
      when 'programs' then MyApi::Program
      ...
      end
    end

   has_many :children, class_name: CHILD_INFERRER
  end

Startouf avatar Jan 20 '19 12:01 Startouf

@JohnSmall would you be willing to open a PR with a sketch of how you think the code should work, or does @Startouf's solution work for you?

gaorlov avatar Mar 20 '19 15:03 gaorlov