Belongs_to causing endless loop on first call to save!

Hi,

I have a situation I’m hoping someone out there may be able to shed some
light on. I have a Rails app (2.1.0 on Ruby 1.8.7) with a wizard-based
sign
up process, that has recently been changed from storing incremental data
in
the database to having a medium sized object graph living in the user
session until the user completes the entire sign up process (this is a
business requirement now). After the user commits all relevant data,
I’m
now calling save! on the top-level object to save all the rows at once.
However, when the call to save! happens–and because I have two objects
that
have a belongs_to association, each pointing to other in addition to the
normal bidirectional association–I end up in an endless validation
loop.
Funny thing is, when I was incrementally saving the data, this issue
never
arose–another way to put it, those relationships never caused a problem
when saving/retrieving already-existing rows.

In the model tier, I have the following classes defined:

class PrimaryAccount < Account
has_many :payment_methods, :foreign_key => “account_id” #using single
table inheritance, which is why foreign key is not “primary_account_id”
belongs_to :current_payment_method, :class_name => “PaymentMethod”,
:foreign_key => “current_payment_method_id” #points to same table, so I
can
have many payment_methods, but also mark as being the current one to
bill

class PaymentMethod < ActiveRecord::Base
belongs_to :account #normal bidirectional association

class CreditCard < PaymentMethod

In the controller tier, I have a session object that–on the final
step–loads an Account model object based on the current session state,
and
then calls save! on that model:

class PrimaryAccountUser < User

attr_reader :credit_card

def initialize(service_id)
@credit_card = {} #hash for storing credit card attributes
end

def persist_to_database
add_session_data_to_account(PrimaryAccount.new)
account.save!
@id = account.id
return account
end

private

def add_session_data_to_account(account) #adds the impl-specific
things to
the account

if(!@credit_card.blank?)
  card = CreditCard.new do |cc|
    cc.account_type = @credit_card[:type]
    cc.account_name = @credit_card[:name]
    cc.account_number = @credit_card[:number]
    cc.cvv = @credit_card[:cvv]
    cc.account_expiration = @credit_card[:expiration]
    cc.address = @credit_card[:address]
    cc.city = @credit_card[:city]
    cc.state = @credit_card[:state]
    cc.zip = @credit_card[:zip]
    cc.country = @credit_card[:country]
    cc.phone = @credit_card[:phone]
  end

  #strange...this won't populate the account_id on save!
  account.payment_methods << card

  #again, if I don't do this, then the payment_method won't have a

populated account_id on save!
card.account = account

  #doing this causes the endless validation loop
  account.current_payment_method = card

end

return account

end

Am I crazy for thinking this ought to work?

Thanks in advance,

Ryan Frith
[email protected]

It just seems way too overengineered to me. When you add a card to the
system, just add the card

In the controller, for example:

def create
card = Card.new(params[:card]
card.account_id = session[:user_id] # or current_user.id or
wherever
you store the id of the currently logged in user.
if account.save

else

end
end

No need to do all that account << card stuff because the web is
stateless.
When it’s saved, you’re going to redirect them, a new request will be
made
by your user, and the records get queried again. Plus you save a ton of
overhead.

just my .02.

Or maybe I’m oversimplifying your situation, but I just like to keep
things
as simple as possible.

Maybe you didn’t see the fine print–I’m saving the entire object
graph at once; there is no session[:user].id or current_user. I
greatly simplified the code for the sake of brevity, but all the
objects get assembled in the final sign-up step from state stored in
the session over many screens (wizard) and then all the objects in the
graph get persisted from the call to the top-level object’s save!
method. This was a design decision to not save session state to DB in
between requests for the sign up process. Anyways, the issue may be
related to this one:

http://dev.rubyonrails.org/ticket/1796

Anyone else have any ideas?

Thanks,

Ryan