Skip to content

Instantly share code, notes, and snippets.

@bokmann
Created February 26, 2012 08:48
Show Gist options
  • Save bokmann/1915372 to your computer and use it in GitHub Desktop.
Save bokmann/1915372 to your computer and use it in GitHub Desktop.
Mel's solution
# Mel,
#
# Sorry, I read too much of a previous problem I had into your question and
# probably spun you off in the wrong direction. Jim's comment points you in
# the right direction, but its only going to get you so far because of a
# limitation in combining polymorphism and HasManyThrough. You will run into
# this error trying to do what you want to do:
#
# http://rubydoc.info/github/rails/rails/master/ActiveRecord/HasManyThroughAssociationPolymorphicSourceError
#
#
# but given you probably don't want the :through par anyway (that would ignore
# the :quantity), I think we can do exactly what you need.
#
# watch this code:
# lets create a small inventory:
apple = Item.create(:name => "Apple")
banana = Item.create(:name => "Banana")
strawberry = Item.create(:name => "Strawberry")
get_well_card = Item.create(:name => "Get Well Soon Card")
# lets build a fruit basket
fruit_basket = Kit.create(:name => "Fruit Basket")
# and put a bunch of fruit in it.
fruit_basket.kit_parts << KitPart.create(:part => strawberry, :quantity => 7)
fruit_basket.kit_parts << KitPart.create(:part => strawberry, :quantity => 7)
fruit_basket.kit_parts << KitPart.create(:part => banana, :quantity => 2)
# and we can see what kit parts it contains
fruit_basket.kit_parts
# => [#<KitPart id: 1, kit_id: 1, part_id: 3, part_type: "Item", quantity: 7>, #<KitPart id: 2, kit_id: 1, part_id: 2, part_type: "Item", quantity: 2>, #<KitPart id: 3, kit_id: 1, part_id: 1, part_type: "Item", quantity: 3>]
# and we have a convenience method that unrolls that for us. Note the implementation isn't a has many through because of the polymorphic error that causes. Rails doesn't know how to join 2 disparate tables:
fruit_basket.parts
# => [#<Item id: 3, name: "Strawberry">, #<Item id: 2, name: "Banana">, #<Item id: 1, name: "Apple">]
# lets create a get well package that includes a kit (our fruit basket) and an item (our get well card)
get_well_package = Kit.create(:name => "Get Well Basket")
get_well_package.kit_parts << KitPart.create(:part => fruit_basket, :quantity => 1)
get_well_package.kit_parts << KitPart.create(:part => get_well_card, :quantity => 1)
# and we can inspect it the same way as above:
get_well_package.kit_parts
# => [#<KitPart id: 4, kit_id: 2, part_id: 1, part_type: "Kit", quantity: 1>, #<KitPart id: 5, kit_id: 2, part_id: 4, part_type: "Item", quantity: 1>]
get_well_package.parts
# => [#<Kit id: 1, name: "Fruit Basket">, #<Item id: 4, name: "Get Well Soon Card">]
# Here is the code that makes this happen:
class CreateKits < ActiveRecord::Migration
def change
create_table :kits do |t|
t.string :name
end
end
end
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.string :name
end
end
end
class CreateKitParts < ActiveRecord::Migration
def change
create_table :kit_parts do |t|
t.integer :kit_id
t.integer :part_id
t.string :part_type
t.integer :quantity
end
end
end
class Item < ActiveRecord::Base
has_many :kit_parts, :as => :part
end
class Kit < ActiveRecord::Base
has_many :kit_parts
has_many :containing_kits, :class_name => "KitParts", :as => :part
# if you want to see an Exception that looks like the name was chosen
# by a Java developer, uncomment this association, comment out the
#'parts' method below, and try the sample code.
#
# has_many :parts, :through => :kit_parts
#
# rails can't go through a polymorphic relationship.
def parts
kit_parts.collect do |kp|
kp.part
end
end
end
class KitPart < ActiveRecord::Base
belongs_to :kit
belongs_to :part, :polymorphic => true
end
@ychaker
Copy link

ychaker commented Feb 26, 2012

This is why I love Ruby and RoR!
Although the problem might be complex to wrap your head around, the solution did not require any complex code...

Mel, I'm glad I saw your post to be able to look at this. I've struggled with this before and solved my situation with some fugly code.

@melriffe
Copy link

@bokmann: thanks for your help with this problem. just a quick message to let you know i've found the code. i haven't studied it yet but, and especially after reading your comments on my gist, i think i can see where i went wrong: trying to maintain the traversal path from item to kit... i think...

@bokmann
Copy link
Author

bokmann commented Feb 27, 2012 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment