1

This one really has me scratching my head. I'm working with polymorphic relations relating to multiple classes that all inherit from one abstract class. This is working well, but I'm running into an error when I try and access the associated object from the polymorphic relations. Here's my implementation:

class Foo < ApplicationRecord
  belongs_to :options, polymorphic: true
  accepts_nested_attributes_for :options

  def build_options(params)
    self.options = options_type.constantize.new(params)
  end
end

module Mod
  class Option < ApplicationRecord
    self.abstract_class = true
    has_one :foo, as: :options

    self.table_name_prefix
      'foo_'
    end
  end

  class Bar < Options
  end

  class Baz < Options
  end
end

This has been working well so far, but I'm now running into an issue when I try something like this:

Bar.first.foo

I get this error:

ActiveRecord::AssociationNotFoundError (Association named 'foo' was not found on Foo::Bar; perhaps you misspelled it?)

The thing that makes this weird to me is that if I call Bar.first.methods, I get :foo as an option.

Any idea what I need to do to fix this and still use the Options class inheritance? I know that I can just define the has_one on the subclasses, but it applies to all the children of Options and if I can keep the association there, I would like to.

Edit: The plot thickens! After playing around for a bit, I have now realized that SOME of the child classes have the association working, but some don't. I also can't seem to find any difference between the classes that are working vs. the ones that aren't.

Charlie Pugh
  • 342
  • 2
  • 18
  • Would you push your codebase to Github' public repository? – Cong Chen Jun 19 '19 at 09:40
  • This is a private repo for my company, so I cannot, unfortunately. – Charlie Pugh Jun 20 '19 at 03:29
  • you can create an example codebase just containing the association models – Cong Chen Jun 20 '19 at 06:24
  • Red flags: (1) `Foo::Bar` in the error message. Do you have another `Bar` class defined? I expect to see `Mod::Bar` as written. (2) Is it `class Option` or `class Options`? The association implies it ought to be the latter. (3) What's the table_name_prefix for? (And are you missing a `def`?) – Andrew Schwartz Jun 24 '19 at 21:01
  • (4) When you say `Bar.first` do you mean `Mod::Bar.first`? I am trying to minimally replicate your code and am confirming that once nested in the module I can't reference `Bar` by itself without the module namespace. – Andrew Schwartz Jun 24 '19 at 21:10
  • yes, it would be `Mod::Bar.first` – Charlie Pugh Jun 26 '19 at 18:32

2 Answers2

0

You may have an issue with inflections, belongs_to is supposed to be a single instead of plural (option instead of options). Same for the has_one :foo, as: :options to be changed to as: :option.

table_name_prefix is not necessary here, in the STI all classes will share the same table.

This code does not show an issue for me:

# app/models/foo.rb
class Foo < ApplicationRecord
  belongs_to :option, polymorphic: true
end

# app/models/mod/option.rb
module Mod
  class Option < ApplicationRecord
    self.abstract_class = true
    has_one :foo, as: :option

    self.table_name = "mod_options"
  end
end

# app/models/mod/baz.rb
module Mod
  class Baz < Option
  end
end

# app/models/bar.rb
module Mod
  class Bar < Option
  end
end
rogercampos
  • 1,676
  • 1
  • 10
  • 8
  • All of the subclasses actually have their own table in the DB, since each subclass has different data attached to them. For example, one of them contains information about a specific amount of time, and another one references a specific object from a different class. I see what you mean about inflections though, the `belongs_to :options` isn't technically the right inflection. I'll try it as `belongs_to :option` and see if that helps. – Charlie Pugh Jun 20 '19 at 03:31
0
# /app/models/user.rb
class User < ActiveRecord::Base
  has_one :profile, as: :profileable, dependent: :destroy
end

# /app/models/business.rb
class Business < ActiveRecord::Base
  has_one :profile, as: :profileable, dependent: :destroy
end

# /app/models/profile.rb
class Profile < ActiveRecord::Base
  belongs_to :profileable, polymorphic: true
end

refere above example.

Deepak Mahakale
  • 22,834
  • 10
  • 68
  • 88
Axel Blaz
  • 29
  • 7