4
votes

I'm working on building an app to keep track of product designs, and I'm having some trouble with my associations. Basically I have a model (Assembly) which needs to have polymorphic association, but also needs to be able to belong to itself.

To illustrate, I have three models: Product, Assembly, and Part.

  • A Product can have many Assemblies.
  • An Assembly can have many Parts AND Assemblies.
  • An Assembly belongs to a Product OR an Assembly.
  • A Part belongs to an Assembly.

My model definitions are currently like this:

product.rb

class Product < ActiveRecord::Base
  belongs_to :product_family
  has_many :assemblies, as: :assemblable
end

assembly.rb

class Assembly < ActiveRecord::Base
  belongs_to :assemblable, polymorphic: true
  has_many :parts
  has_many :subassemblies, as: :assemblable
end

part.rb

class Part < ActiveRecord::Base
  belongs_to :assembly
  belongs_to :product_family
end   

What I would like to be able to do is, given an assembly called "top_assy":

top_assy.subassemblies.create

However, when I try this, I get the following error:

NameError: uninitialized constant Assembly::Subassembly

I'm clearly doing something wrong here - what am I missing? I have already tried adding 'class_name: "Assembly"' as an argument to the 'has_many :subassemblies' command.

Thanks in advance!

3
I think that basically what I need is something that will generate a "polymorphic join table". So it would basically be a join table with three columns: "parent_type", "parent_id", and "assembly_id". "parent_type" could then be either "Product" or "Assembly".rjblake1984

3 Answers

1
votes

has_many :subassemblies, as: :assemblable

by

has_many :subassemblies, as: :assemblable, class_name: 'Assembly'

Carlos's solution works because now rails knows which class to query, as follows:

Before specifying :class_name:

When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.

After specifying :class_name:

Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.

Demonstation of flow:

# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
    # 1. Rails checks the :subassemblies association
    # 2.a. There it is specified to query the class 'Assembly'
    # 2.b. and it is to match the "id" column of ex_asm
    # 2.c. with the 'assemblable_id' column of the Assembly table
    # 3 Rails returns the assemblies matching criteria (2) as
    #   :subassemblies of ex_asm.
1
votes

I don't know why this works, but I had the same problem and solve as it:

In Assembly class replace

has_many :subassemblies, as: :assemblable

by

has_many :subassemblies, as: :assemblable, class_name: 'Assembly'

=====================================================================

Edit: explanation of solution

Before specifying :class_name:

When calling .subassemblies method, rails queries a supposed 'Subassembly' model class to match the 'assemblable_id' column in that class. However, 'Subassembly' model class is not defined (it doesn't make sense to define it anyway) here and hence the error.

After specifying :class_name:

Because the class 'Assembly' was specified as :class_name, now rails knows it is to query the 'Assembly' model class and match the 'assemblable_id' column.

Demonstation of flow:

# :class_name has been specified to be 'Assembly'
ex_asm = Assembly.new # an example assembly
ex_asm.subassemblies # flow:
    # 1. Rails checks the :subassemblies association
    # 2.a. There it is specified to query the class 'Assembly'
    # 2.b. and it is to match the "id" column of ex_asm
    # 2.c. with the 'assemblable_id' column of the Assembly table
    # 3 Rails returns the assemblies matching criteria (2) as
    #   :subassemblies of ex_asm.
0
votes

you can try this

product.rb

class Product < ActiveRecord::Base
  belongs_to :product_family
  has_many :assemblies
end

assembly.rb

class Assembly < ActiveRecord::Base
  attr_accessible :top_assembly_id
  has_many :sub_assemblies, :class_name => "Assembly", :foreign_key => "top_assembly_id"
  belongs_to :top_assembley, :class_name => "Assembly"
  has_many :parts
end

part.rb

class Part < ActiveRecord::Base
  belongs_to :assembly
  belongs_to :product_family
end  

and now you can referer

top_assembley.sub_assemblies.create