Multiple screens before save called


#1

Is there a clever rails way to chain multiple forms together to collect
all the info that I need before finally calling “save”?

For example consider an app which needs to create a “Order” object which
is tied to a “Contact” object which may or may already exist. Lets
pretend we don’t want to save the order to the DB until the contact info
is known and both objects must be saved atomically.

We have a form for editing Orders, and a Form for editing Contacts. We
call the Orders form first and obtain the info, now I want to stash the
detail somewhere while I go and obtain the Contact data. What is a good
way to serialise that data while we call the Contact form? Lots of apps
like to stuff it in as a serialised hidden field so that you don’t have
problems with multiple sessions at the same time - how might we go about
this in Rails?

Ed W


#2

You could save it in the session or flash.


#3

Kyle M. wrote:

You could save it in the session or flash.

But that’s going to be a problem when you have a user logged in and they
open up several windows to your server, isn’t it?

Ed W


#4

El 20/12/2005, a las 02:59 PM, Ed W
escribió:

Is there a clever rails way to chain multiple forms together to
collect all the info that I need before finally calling “save”?

How about marshalling your objects using YAML and putting them into a
hidden form field. Have never tried it, but ought to work.

Patrice_______________________________________________
Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails


#5

On 12/21/05, Ed W removed_email_address@domain.invalid wrote:


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails

Not if you’re smart about it.


Kyle M.
Chief Technologist
E Factor Media // FN Interactive
removed_email_address@domain.invalid
1-866-263-3261


#6

To save you from having to store all the form data in a hidden field,
you could implement a ‘session inside session’ approach, i.e. store
your form data under a unique key in the user session.

Each time a user submits the first of your forms, a new key has to be
generated and kept in a hidden field on all following pages. The key
now allows you to retrieve the correct order object from the user’s
session, even if the user has opened more browser windows to your app.

How does this compare with simply marshaling the data and putting that
in a hidden text field on the form?

Arguably the data is harder to forge if you leave it in the session, but
I’m thinking that the validation is all done at the end and if there are
problems we popup the correct screen for the user to correct (rinse and
repeat until we are happy)

Any pros/cons to either technique?

Thanks

Ed W


#7

On 12/21/05, Ed W removed_email_address@domain.invalid wrote:

Thanks

Ed W


Rails mailing list
removed_email_address@domain.invalid
http://lists.rubyonrails.org/mailman/listinfo/rails

I would use single table inheritance to achieve the results you
desire. Make a base class called EmptyOrder, then an inherited class
called PartlyCompletedOrder, then a class inherited from
PartlyCompletedOrder called CompletedOrder. EmptyOrder has all of the
instance methods you wish to have in this class, as well as a method
called to_class, which takes a class name as an argument, and attempts
to type cast the row to that class. PartlyCompletedOrder has some
validations, and CompletedOrder has yet more validations. As should
be apparent, you don’t need exactly three classes in this system,
2…n will work.

My one concern with this system is the associations. They will have
to be particularly complex, as you may need to specify an association
in the EmptyContact class for each class in the Order hierarchy, and
vice versa. I haven’t had to use this technique for multi-model forms
yet. Let me know how it goes, should you choose this route.


Kyle M.
Chief Technologist
E Factor Media // FN Interactive
removed_email_address@domain.invalid
1-866-263-3261


#8

To save you from having to store all the form data in a hidden field,
you could implement a ‘session inside session’ approach, i.e. store
your form data under a unique key in the user session.

Each time a user submits the first of your forms, a new key has to be
generated and kept in a hidden field on all following pages. The key
now allows you to retrieve the correct order object from the user’s
session, even if the user has opened more browser windows to your app.

I have thought about this a bit more and I like the idea. Actually this
technique could be useful in lots of other areas, for example where you
want to track the “redirect to previous” location, it would obviously be
nice if this didn’t go wrong just because you were editing two things in
seperate browser windows.

The biggest issue I see is that the session is going to fill up with
crud data? Assuming that the users in question are using the system day
in and out then the session isn’t going to expire so the session will
eventually fill up with huge amounts of dead data every time a
transaction is aborted.

Is there a smart way to handle this? I can imagine making the session a
hash and storing a “last used” time as well as the object. Then perhaps
every time we access the session we need to scan through for old data.
It seems to incur a bit of overhead, but it’s not obvious how else to do
it?

Is is possible to build something which can “walk” every session on a
timed basis and examine all the objects and remove anything which has
expired?

Any other suggestions?

Ed W


#9

On Wed, Dec 21, 2005 at 08:13:57AM +0000, Ed W wrote:

Kyle M. wrote:

You could save it in the session or flash.

But that’s going to be a problem when you have a user logged in and they
open up several windows to your server, isn’t it?

To save you from having to store all the form data in a hidden field,
you could implement a ‘session inside session’ approach, i.e. store
your form data under a unique key in the user session.

Each time a user submits the first of your forms, a new key has to be
generated and kept in a hidden field on all following pages. The key
now allows you to retrieve the correct order object from the user’s
session, even if the user has opened more browser windows to your app.

Jens


#10

Ed Wildgoose wrote:

transaction is aborted.

Any other suggestions?

We just submit the form along and create a new (hidden) form on each
page in the track.

I.e., in the controller:

@order = Order.new(params[:order])
(now you can validate it, whatever, but not save it until they commit
the order in another method)

in the pages in the track:

<% @order.attributes.each do |column| %> <% end %>

Notice the action is invalid. We have two buttons, edit and submit. JS
overrides the action name and submits for us either backwards or
forwards in the track. With no JS support they go to the error page.

BTW- Be careful what you store on the web server if you are subject to
Visa or MC audits. You are not supposed to store credit card numbers on
a public facing server, for example. And, you never should store CSC
card #'s (those 3 or 4 digits on the back of the card). I noticed the
Rails book uses examples where they store credit card numbers and such,
which can be a no-no in many cases.

Oh, and be careful what method you use to store sessions. We switched
to storing sessions in the SQL backend, and things broke. We had to go
back to files. This seems to be especially true for sessions which
store larger chunks of data.

Phil