0

I'm have a user class that can optionally have a billing address. When I post a payment form, assuming the user has indicated they want to save their billing address details, I want to either create a new address record or update the original one.

I have tried many things but the closest I can get to working code is...

class User
  include DataMapper::Resource
  property :id,          Serial
  property :provider,    String, :length => 100
  property :identifier,  String, :length => 100
  property :username,    String, :length => 100
  property :remember_billing, Boolean
  has 1, :billing_address
end

class BillingAddress
  include DataMapper::Resource
  property :first,       String, :length => 20
  property :surname,     String, :length => 20
  property :address1,    String, :length => 50
  property :address2,    String, :length => 50
  property :towncity,    String, :length => 40
  property :state,       String, :length => 2
  property :postcode,    String, :length => 20
  property :country,     String, :length => 2
  property :deleted_at,  ParanoidDateTime
  belongs_to :user, :key => true
end

post "/pay" do
  @post = params[:post]
  @addr = params[:addr]
  if @addr == nil
    @addr = Hash.new
  end

  user = User.first(:identifier => session["vya.user"])
  user.remember_billing = !!@post["remember"]

  if user.remember_billing
    user.billing_address = BillingAddress.first_or_create({ :user => user }, @addr)
  end
  user.save
  ...

which works fine when there is no record. But if there is already a record, it keeps the original values.

I saw a similar post DataMapper: Create new record or update existing but if I alter the code to be

user.billing_address = BillingAddress.first_or_create(:user => user).update(@addr)

I get the error

DataMapper::ImmutableError at /pay
Immutable resource cannot be modified

Any help much appreciated

Community
  • 1
  • 1
Allan G
  • 60
  • 1
  • 6

1 Answers1

0

You're chaining lots of things together, there. How about:

billing = BillingAddress.first_or_new(:user => user, @addr) #don't update, send the hash as second parameter
billing.saved? ? billing.update(@addr) : billing.save
raise "Billing is not saved for some reason: #{billing.errors.inspect}" unless billing && billing.saved?
user.billing_address = billing
user.save
stef
  • 14,172
  • 2
  • 48
  • 70
  • Thanks for the response but not sure how this solves the UPDATE scenario? If a billing record already exists, it's not going to get overwritten with the new values. – Allan G Mar 28 '11 at 10:46
  • Okay - adjusted for updating in the same route. All looking a bit verbose now, however. – stef Mar 28 '11 at 11:01
  • Thanks for your ideas. This got me to try out a few more ideas. I believe the reason its immutable is because I am referencing the same object both from the user class and directly from the billing class. Because I made an alteration to the user class early on, it effectively stopped me from modifying the same object via another route - the billing address class. Still surprised there isn't a neat one liner to do this in Ruby! – Allan G Mar 29 '11 at 14:17