Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
JohnSmall opened this issue Jan 27, 2017 · 2 comments
Open

Comments

@JohnSmall
Copy link

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
@Startouf
Copy link

Startouf commented Jan 20, 2019

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

@gaorlov
Copy link
Collaborator

gaorlov commented Mar 20, 2019

@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants