How to make a copy of a collection in the update controller method


#1

I can’t figure this out.

Imagine a Ticket object and this object has_many LineItems

Now, my ticket edit form will allow user to modify Ticket attributes
and add/modify/delete LineItems at will.

So the edit action sends the LineItems to the form and what comes back
could be LineItems in any which way/order or even no LineItems.

With that in mind, I thought, okay, well, I better hold on to the
original list of line items somehow in case validation of the Ticket
item fails. (no validation of the individual line items, we just throw
away the ones that have a blank “item” attribute, but the main ticket
attributes do have some validation happening)

In the update controller method I wanted to:

store original line_items in an array or maybe a hash doesn’t matter

take the new line items from the form hash and “build” them on to the
ticket
so, now I have a line_items list that is the original list + what just
got added from the form

then, if the Ticket validates,
take that original line_items list and delete from ticket’s current
line_items which again is original list + what got added from the form

the problem is, no matter what I do, I can’t seem to save the
original_line_items so they don’t get touched when processing the
line_items from the form

At the very beginning of the update controller method I have:

@ticket = Ticket.find(params[:id])
@line_items = @ticket.line_items

I think my problem is that I don’t understand what is happening here.
I thought this said:

  1. ok, look up the ticket you want to work on, and here’s the original
    line_items for the ticket.
  2. now manipulate those via the params from the form …

I tried stuff like:

orig_line_items = @ticket.line_items

orig_line_items = Array.new(@line_items)

but no luck, no matter what I do, as soon as I start working with
@line_items and the params from the form, orig_line_items seems to get
changed

I know this because if I start with a ticket with zero LineItems (from
script/console I look up the ticket, assign its line items to an array
called orig_line_items and then say “is that array empty”, answer =>
true)

but in my controller, even if I added no line_items via the form

@orig_line_items.empty? ----- this is always false, 100% of time, no
matter what happened with the form

I’m not understanding a basic form processing concept here I think.

Here’s an example of what the form returns, this is good, looks
exactly as I expect/want

{“commit”=>“Update Ticket”,
“ticket”=>{“status”=>“new”,
“title”=>“tested”,
“owner”=>“1”,
“email”=>“removed_email_address@domain.invalid”},
“id”=>“7”,
“ticket_extra”=>{“uploaded_data”=>""},
“line_items”=>{“0”=>{“date_created”=>Sat,
06 Dec 2008 20:26:29 +0000,
“is_hidden”=>“0”,
“item”=>“ff”},
“1”=>{“date_created”=>Sat,
06 Dec 2008 20:32:41 GMT +00:00,
“is_hidden”=>“0”,
“item”=>“dd”}}}


#2

On 6 Dec 2008, at 23:42, jim wrote:

There’s two levels of trickery here. First @ticket.line_items isn’t an
array. At this point the line items have not been loaded. It’s a
magical association proxy. Even if you were to sidestep that, you’re
still not copying the array. To put things another way, is

a = [1,2,3]
b = a
Then b and a are exactly the same array:
b << 4
a #=> [1,2,3,4]

I tried stuff like:

orig_line_items = @ticket.line_items

orig_line_items = Array.new(@line_items)
Creating a new Array like that (or duping the original array) should
work. I suspect that since you seemed to have been changing stuff
slightly in desperation you did something else at the same that
clouded the issue.

Fred


#3

Exactly, after more tinkering and such I made it work by copying to an
array.

Thanks for taking the time to read my post,

On Dec 7, 6:10 am, Frederick C. removed_email_address@domain.invalid