small medium large xlarge

24 Feb 2008, 07:20
Paul Jewell (2 posts)

Whilst playing with the habtm code I was informed that push_with_attributes was deprecated, and I should look to use :through instead. So, here is my effort at refreshing the code, with comments:

#! /bin/env ruby
#  Model for webshop stock management example in
#  Enterprise Integration with Ruby
#  Modified to remove the deprecated push_with_attributes
#  call. For more information, refer to "Agile Web Development with Rails"
#  chapter 18 - sub section "Using Models as Join Tables"

require 'rubygems'
require 'active_record'

# Modify the connection details to match your setup
# - Book Reference file 5, page 34
  :adapter => 'mysql',
  :host => '',
  :username => 'paul',
  :database => 'webshop'

# One change to the Bouquet class - note the reference to :through
# in the second has_many line. This allows a link to the flowers
# table via an intermediate table called ingredients

class Bouquet < ActiveRecord::Base
  has_many :ingredients
  has_many :flowers, :through => :ingredients

  def price
    ingredients.inject(self.base_price) do |total, i|
      total += i.flower.price * i.quantity.to_i

  # When adding flowers to the bouquet, check to see if the
  # flower is already in the list - if so change the quantity
  # rather than creating a new addition
  # The call is used as the << operator doesn't
  # allow for intermediate table attributes to be set.

  def add(flower,quantity)
    if (ingredient = self.ingredients.find(:first,
                                           :conditions => "flower_id = #{}"))
      ingredient.quantity += quantity
    else =>,
                      :bouquet_id =>,
                      :quantity => quantity).save!

  def to_s
    print "A #{} costs $#{self.price} and contains #{} flowers: \n"
    self.ingredients.each { |i| print, " ", i.quantity, " off\n" }


# The link table known as bouquets_flowers is now not used. It's use is
# not recommended for cases where more detail than the link foreign keys
# are required.
# mysql statements to create:
# CREATE TABLE ingredients(
#   id int unsigned NOT NULL auto_increment primary key,
#   bouquet_id int NOT NULL,
#   flower_id int NOT NULL,
#   quantity int NOT NULL,
# );
# alter table ingredients add constraint fk1_bouquet
#   foreign key (bouquet_id) references bouquets(id);
# alter table ingredients add constraint fk1_flowers
#   foreign key (flower_id) references flowers(id);

class Ingredient < ActiveRecord::Base
  belongs_to :flower
  belongs_to :bouquet

# Note the reference to bouquets :through ingredients in the second
# has_many line

class Flower < ActiveRecord::Base
  has_many :ingredients
  has_many :bouquets, :through => :ingredients
  belongs_to :stock_item

class StockItem < ActiveRecord::Base
  has_one :flower

I am pretty new to ruby and ActiveRecord, and therefore would have no problem with constructive criticism :-)!

Hopefully this can clarify :through for anyone else who comes across the same issue.

Rgds., Paul