views:

396

answers:

2

I am developing an Rails 2.3.1 Web site. Throughout the Web site, I need to have a form for creating Posts on various pages (Home page, Create Posts page, Post listing page, Comment listing page, etc. -- suffice to say this form needs to be on many pages served by a variety of controllers). Each of these pages displays a wide variety of other information that is retrieved in the corresponding controller/action. Ex, the home page lists latest 10 posts, content pulled from the DB, etc.

So, I've moved the Post creation form into its own partial, and included this partial on all the necessary pages. Note that the form in the Partial POSTs to /questions (which routes to PostsController::create -- this is default rails behavior).

The problem I am running into is when the the Posts form is not completed correctly, by default the PostsController::create method render's questions/new.html.erb, even if the form was submitted from the home page (/home/index.html.erb).

I tried changing the form in the partial to submit the "submitting_controller" and "submitting_action", and in PostsController::create, when @post.save? == false, I render action => "../submitting_controller/submitting_action" (Which is slightly hacky, but lets you render actions from non-PostsController's).

This seemed to work OK on the surface. The incomplete form was rendered in the view that submitted it with all the correct @post.errors message, etc. The problem was ALL the other data on the pages didnt show up, because the actual submitting_controller/submitting_action methods weren't called, just the associated view. (Remeber, I did a render which preserves instance objects, rather than a redirect_to which does not preserve the @post instance object which has all the error messages and submitted values.)

As far as I can see I have two options:

1) I can store the @post object in the session when @post.save? fails in PostsController::create, redirect_to submitting_controller/submitting_action, at which point i pull the @post object out of the session and use it to re-populate the form/error messages. (As far as I understand, storing objects in the session is BAD practice in rails)

2) I can move all the logic used to pull non-post creation form data from the various submitting_controller/submitting_action, put it in the ApplicationController, create a giant switch statement in PostsController::create for submitting_controller/submitting_action and call the methods in the ApplicationController to grab all the extra data needed for each submitting page's render.

Thoughts on the best way to do this within Rails?

+1  A: 

Hi,

This is about the point that you move from full page updates to updating sections of a page through the use of AJAX. There are a bunch of things you should consider but the most rails-esque approach would be to split the response between an AJAX response and a plain HTML response. Check out this ONLamp article, this register article or the awesome agile web development with rails book. Essentially your controller renders a new div replacing the old div containing the result of submitting the partial.

In your question you mention two approaches and so I'll try and give you some pointers on why and why not here:

Option 1) Ths option is not so bad with a couple of tweaks. The main tweak is is to store the object in a serialized form in the DB. Then simply pass around the ID of the serialized object. Your upsides are that the session data gets persisted so recovering a a session is neater and your session stays light. The downside of this is that having a bucket of session cruft in your DB will pollute your app and you'l need to do some thinking as to how you expire unused session cruft from the DB. I've never seen this end well...

Option2) Eeek not inside the application_controller! :) Seriously, keep that as your weapon of last resort. You can pop things insde the helpers though and get access to those methods inside your controllers and views. However the testing of that stuff is not so easy so be careful before choosing that route. Switch statements can be replaced in OO apps with a little thinking, certainly in his case you can use option hashes to get a railsy way of having some smarts about the state of the app at the time the request is made.

robertpostill
Thanks, A couple thoughts:Option 1) Tracking objs in the DB sounds like it could turn into a nightmare, a slippery slope indeed.Option 2) I didnt think Controllers could access helpers (must be CakePHP influence). I agree the ApplicationController is a gross place to put them ;) The Hash is a good idea, though I still don't like that all this view-specific data-selection would be moved into into helpers -- just seems like it could easily become a maintenance nightmare.I'll read over the articles you provided, but I think AJAX is going to be the way to go.
empire29
+1  A: 

By any chance is your Post model in a belongs_to relationship with each model who's controller you'll be using to render your form? Are you doing any special processing in the Post controller beyond Post.create(params[:post])?

If you answered yes to the first question and no to the second, you could get by with minimal mangling by adding accepts_nested_attributes_for to each of the controllers on which a user can create a post.

Regardless, Robertpostill is correct in that this is probably when to start looking at AJAX, to just replace the section of the page. The only problem is what to do if a user has javascript disabled. Personally I like to do design for the non-javascript case and add convenience methods.

As for thoughts on what you consider your two options,

1) I've used this method to store a shallow copy of an object in the flash hash. Preserving it across redirects. However this might not work for you given the variable nature of posts. As you can only send about 4K worth of data, and that includes other information in addition to your shallow copy.

2) See robertpostill's response

EmFi
Emfi,Nice point about the nested attributes. That hadn't occurred to me.Also the point you make about defaulting to non-JS I think is important. The feeling I got though is that the questioner (for lack of a better handle :) will end up hurting the application performance through the amount of DB action he/she would need to perform reconstituting all the objects.
robertpostill
On this we are agreed. However, the difference between non-javascript and AJAX is usually a link_to_remote call, a RJS that renders a template, and possibly a little controller logic to avoid database calls you would normally make otherwise.
EmFi
EmFi, Unfortunately the Post model and the models who's controller you'll be using to render the form may not be related. (HomeController doesn't have a backing model even). I do some checking on a captcha mechanism in the PostController::create (the captcha field is not part of the Post Model, but rather its own module).I think both you and Robertpostill are right in AJAX is the way to tackle this problem. I just have to figure out how to do it with jQuery (i opted out of using Prototype, personal pref) and not muck up anything in Rails.
empire29