Rails best practice for having same form on multiple pages - ruby-on-rails

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?

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

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.

Related

How would I setup routing and partial loading for a rails application with a central dashboard?

I am having trouble figuring out how to make my rails app work with a central dashboard. Basically I want it to work so that when a user clicks on a course from the list, the details of the course are loaded into the dashboard. The ajax part isn't too complicated, I am however struggling slightly with loading the partial in when ajax isn't being used. I figure I can simply put some logic in the view so that if #course != null it will load the partial.
The routes are also giving me a bit of difficulty. So I wanted the url to resemble the following: dashboard/student/:id/university/:id/course/:id
I came up with the following, but is there a better way?
http://pastebin.com/keEmaznw
It's hard to understand your question. You've given us scant detail, and insufficient code. That said, regarding loading a partial with a nil object:
If you pass an object to a partial, Rails will take care of the rendering for you automatically, and no nil (instead of null, in Ruby talk) check is necessary:
= render #object
Regarding routes, yes, there is a better way: You should generally avoid nesting your routes more than two levels. This becomes a nightmare to test, and you end up writing very long method names:
edit_dashboard_student_university_course_path(#university, #course, #student)
You really don't want to be writing that in your view or your tests. think about it, if you want to edit a student, is it necessary to load the university and the course as well?

A way to make use of form automagic for a controller without a model

I have a controller without a model that handles a very cyclical task.
The user is basically filling out a form, sending it up to the server for some minor analysis, and then the server regurgitates nearly the same thing back to the user.
The user makes some adjustments, and the process starts over again.
There is nothing to save or store from this process, so it doesn't have a model.
The form i am working with has a large number of fields to deal with. Is there a way I can pass the params hash back into the view with the least amount of mess getting the fields repopulated?
Basically, I would want to tweak a couple of values in the params hash, but more or less pass the hash back so that it can repopulate the view nearly exactly how it was when it was submitted.
The old way of using OpenStruct seemed ideal.
Will this help?
In controller action, just initialize a variable like #params (or some thing like this), so that can be able to use all the values in the params can be available in that view page.
Maybe an ActiveForm is good for your application. It use to manage (new/create/validate/...) forms with no or several models.
https://github.com/realityforge/rails-active-form

Handling forms in rails

I am a little confused with forms in RoR.
I have a contacts page with a corresponding method in my controller. What I am trying to do is have a form so people can leave a message. I will then be emailing that message to myself.
I have the form and everything created. However, I am a little confused on how I would actually get the data from the form once they hit the submit button. would it just be accessible through my contacts method in my controller using params[:message]?
Also, what if I had multiple forms on one page? Would I just be doing params[:message1], params[:message2], etc., in the contacts method in my controller?
Take a look at this answer for details on what exactly the params hash is. When you reference params[:message], this implies that you are POST'ing to your controller action with, say, "message[subject]=abc123", which Rails helpfully turns into a hash with a key like: params[:message]['subject'].
If you're looking to send an email, check out mail_form, which simplifies creating a non-database-backed model that get's turned into an email.
Lastly, about having multiple forms on a page: each form POST's to its action and includes all of the form elements that are children of that form in the DOM. So, only the children of that message will actually be included in the params[:message] hash. You can use fields_for to have multiple models within a single form.

What's the best way to do UJS in rails when you have a re-usable widget?

In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.

How to persist data about what page you are on in Rails app

This will probably be easiest if I explain what I'm trying to do. I have three actions in my Rails app controller, each rendering a different page. The page-render is done with a single partial which uses variables that were set in the controller action code. For example, each page has a list on it, but on one page the list is sortable. Up to now I've been handling this by setting a #sortable flag to true or false in the code for my actions.
This works fine when an action is initially run. The problem is that I have AJAX stuff going on (e.g. adding a new element to the list) and when this happens, I need to know the value of the #sortable variable again. It seems to have gone, even though I'm still technically on the same page. What I want is a variable store that is linked to the page you are on.
What are your recommendations for doing this? (Storing it in the Rails session hash seems like overkill - too much chance that the wrong value will get left in there by some yet-to-be-implemented action.)
Ben
In rails I've only managed to set page scoped variables for initial setup too.
I think the only solution would be to pass the sortable flag from the page on the ajax request. You can store it either with a javascript variable, in a hidden field, custom attribute on your list or anyway you wish and then in the ajax you simply add that to the request so you can treat that on the server side persistently.
Why do you don't want use session? As for me before_filter works fine for such tasks
in ApplicationController
before_filter :init_actions
def init_actions
session[:action] = action_name
session[:controller] = controller_name
end

Resources