views:

324

answers:

3

I'm trying to model a card game in order to learn Rails. This is different than a standard deck of playing cards in that there can be multiple copies of a card in the deck. I'm running into problems while trying to initialize the deck. So far I've got a basic Card model with various attributes (such as copies_in_deck) but no associations. A DeckCard model which represents the cards in the deck (this is due to having multiples of the same card in the deck):

class DeckCard < ActiveRecord::Base
  belongs_to :card
  belongs_to :deck
end

These DeckCards will eventually have attributes reflecting position; such as in discard pile, in draw pile, etc.

A Deck model with

belongs_to :game
has_many :deck_cards

I'm trying to inialize the deck and am having various problems. I'm trying the following in Deck

def initialize
  @cards = Card.find(:all)
  @cards.each do |card|
    # eventually another loop here on copies_in_deck
      @deck_cards.build(card)
  end
end

When I do Deck.new I get an error on nil.build. Why is @deck_cards nil?

I'm using InstantRails2, which has rails 2.0.2. I found this article about creating multiple models in one action, so I tried using Deck.create! instead, and got an error about the wrong number of arguments.

Any suggestions?

+1  A: 

subclasses of ActiveRecord should not define an initialize method since ActiveRecord takes care of that for you.

Sounds like you want one record per card. And the Model DeckCard can hold more than one pack of cards at a time.

Re: Why is @deck_cards nil? Because you haven't set it anywhere.

@deck_cards is an instance variable. You're trying to call the build method on it, but you haven't set it to anything. You should be calling the build (or create or new method) on the class DeckCard.

If you want to create and save a card in the DeckCard model:
DeckCard.create(card) # not @deck_cards.build(card)

Larry

Larry K
I guess I thought @deck_cards was the same as self.deck_cards; and I got the build from what I had read about associations and/or association proxies (might not have the right term there)
A: 

Don't use #initialize on objects that inherit from ActiveRecord::Base. It'll break all sorts of internal Rails magic. Instead, use the #after_initialize callback documented here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Note that after_initialize is called after finds as well as on newly created objects, so you'll want to wrap your initialization code to make sure that the object hasn't been saved previously (use #new_record?):

def after_initialize
  if new_record?
    # initialize deck_cards here.
  end
end

In the past, I've taken this alternate approach with a nicer syntax, but be warned that it doesn't kick in until you try to save or validate the record.

before_validation :build_default_associations, :if => :new_record?

def build_default_associations
  # initialize default associations here.
end
+1  A: 
class Deck < ActiveRecord::Base
  belongs_to :game
  has_many :deck_cards

  # To create new: Deck.new.build_deck
  def build_deck
    Card.find(:all).each do |card|
      self.deck_cards << DeckCard.new(:card => card)
    end
  end
end
Sarah Mei
Thanks, that did it! I would have marked it up but I don't have the rep