What is the best way to route a static controller in Rails? - ruby-on-rails

I have a static_controller that is in charge of all the static pages in the site and works as follows in routes.rb:
map.connect ':id', :controller => 'static', :action => 'show'
I have a static page called about that among other information, has a contact form.
I currently have a contacts_controller that is in charge of inserting the contact information to the database.
Inside my routes.rb file, I have:
map.resources :contacts
My contact form (simplified) looks like this:
<% form_for #contact do |f| %>
<p class="errors"><%= f.error_messages %></p>
<p>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
</p>
<p class="buttons"><%= f.submit %></p>
<% end %>
Which in turn submits to the create action of my contacts_controller.
My create action looks like this:
def create
#contact = Contact.new(params[:contact])
if #contact.save
flash[:notice] = "Email delivered successfully."
end
redirect_to "about"
end
The problem is, is the that when I redirect back to my about page the error_messages for the form get lost (since the error_messages for the form only exist for one request, and that request ends upon redirect).
How would I go about preserving the error_messages and still linking the users back to the about static url?
Would a session/flash be sufficient (if so, what code would I use to pass error messages) or am I going about this whole thing wrong?
Thanks!

I think what might be going on is you need to render rather than redirect.
Redirect terminates the request, and tells the client to make a new request to a different address. That will lose your errors.
If your save attempt fails your want to complete the request by rendering the action again with the errors shown.
def create
#contact = Contact.new(params[:contact])
if #contact.save
flash[:notice] = "Email delivered successfully."
redirect_to #contact #make a new request for the address of the new record or some other address if you want
else
render :action => "new" #complete the request by rendering the new action with the #contact variable that was just created (including the #errors).
end

Related

Repopulating form on failed validation

I have a form for a user to create a question (in additon to user model, there's a question model, with nested answers) on their profile page. It submits from the users profile page /views/users/show.html.erb to the create action of the questions_controller.rb. If it doesn't validate, I think the default for Rails is to render the form(with the invalid information in the form for the user to edit). However, since I'm submitting the form for the question model from the users profile page the prepopulation isn't happening upon failed validation; the user is forced to enter all the information in the form again. Is there a way in this context to get the form on the users show page to fill out with the information that was entered prior to submission?
questions_controller
def create
#question = current_user.questions.build(params[:kestion])
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
###render :action => 'show'
redirect_to current_user
end
end
Update
I've changed the end of the create method too
Redirect ( : back ), :notice => "something went wrong.try again"
But I still can't get the form to populate, and the validation error messages aren't showing either, only the flash notice.
Update
The show method of the users controller creates the new Question (along with the user)
def show
#user = User.find(params[:id])
#question = Question.new
3.times {#question.answers.build}
end
The /views/users/show.html.erb
<%= form_for #question do |f| %>
<% if #question.errors.any? %>
<h2><%= pluralize(#question.errors.count, "error") %> prohibited this question
from being saved: </h2>
<ul>
<% #question.errors.full_messages.each do |msg| %>
<li> <%= msg %></li>
<% end %>
</ul>
<% end %>
<p>
<%= f.label :content, "Question"%>
<%= f.text_area :content, :class => 'span4', :rows => 1 %>
</p>
<p>
<%= f.label :link, "QuoraLink" %>
<%= f.text_field :link, :class => 'span4', :rows => 1 %>
</p>
<%= f.fields_for :answers do |builder| %>
<p>
<%= render 'answer_fields', :f => builder %>
</p>
<% end %>
<p><%= link_to_add_fields "Add Answer", f, :answers %></p>
<p><%= f.submit %></p>
<% end %>
the answer_fields partial rendered from the questions partial
<p class="fields">
<%= f.label :content, "Answer..." %>
<%= f.text_field :content, :class => 'span3', :rows => 1%>
<%= f.label :correctanswer, "Correct" %>
<%= f.check_box :correctanswer, :class => 'span1' %>
<%= link_to_remove_fields "remove answer", f %>
</p>
Currently, in views/users/show.rb you do
#question = Question.new
that creates an empty new question. Then you populate the forms with this empty model.
What you could do instead is:
if session[:question]
#question = #user.questions.new(session[:question])
session[:question] = nil
#question.valid? # run validations to to populate the errors[]
else
#question = Question.new
end
Now all what's left to do is populating session[:question] in your questions_controller before redirecting to :controller=>"users", :action=>"show". Something like:
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
session[:question] = params[:question]
redirect_to current_user
end
You may need to work on serialization/deserialization additionally for populating/using session[:question]. I didn't try to compile, so am not sure.
All this is needed because when you do redirect_to your processing of the user request ends, the user browser gets a redirect status code from your server and goes for another page, sending you a new request (which lands on the path, and eventually controller/action, to which you redirected to). So, as soon as you return from the request processing, all your variables are lost. For the next request you start from scratch.
The render :action => "show" approach (that was in the original scaffold and that you commented out) worked because you didn't return back to user but simply rendered the template with a specific name using the variables you already had in place (including #question, on which 'save' was called and failed, and thus internally validations were called and populated the errors object).
Actually, that reminded me that you may want to use another approach. Instead of passing parameters through session[] and redirecting to UsersController, you may want to populate all required variables and just render the view from that controller. Like below:
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
#user = current_user
render "users/show"
end
Firstly, the reason that using redirect_to instead of render doesn't repopulate the form is that when you redirect_to, the controller logic for the action is run, whereas using render ignored the controller logic.
So when you render :action => 'show' (the "default" behaviour), it renders show.html.erb, with #question set like this:
#question = current_user.questions.build(params[:kestion])
When you redirect_to current_user, it renders show.html.erb with #question set using the code in your show action:
#question = Question.new
3.times {#question.answers.build}
This is why you get a new (blank) form, instead of a pre-populated one.
Is it really that important that you use redirect_to? If it is, you'll need to get your show method to do the validation. For example, you could rewrite your show method to something like:
def show
#user = User.find(params[:id])
if params.has_key?(:kestion)
#question = #user.questions.build(params[:kestion])
else
#question = Question.new
3.times {#question.answers.build}
end
end
and then make your form point at that page, with something like:
<%= form_for(#question, url: users_path(#question.user) do |f| %>
...
<% end %>
(depending on how your routes are set up and named). Of course, by that point the whole thing become horribly un-RESTful, a bit of a mess, and definitely not the Rails way of doing things. (The other, worse option would be to redirect back and pass the params through a get query.) In my opinion, you lose a lot for a minor gain, and I'm not sure that I'd really recommend it.

Rails - Redirect_to error in controller - "The page isn't redirecting properly" "

In the process of learning Rails so excuse my noob question.
What I want to do:
There is a text_field_tag that allows a user to enter an item id. I then want to get that value and use it as a parameter in the url.
ex.
The user types '4Qe6' into the text box and clicks submit. Then the page navigates to 'trckr.net/tracker/track/4Qe6'
Here is the code for my form:
<h1>Tracker#index</h1>
<p>This is the landing page</p>
<p>
<u> Track an item: </u>
<%= form_tag(:action => 'track') do %>
Item ID: <%= text_field_tag(:id) %>
<%= submit_tag('Track Item') %>
<% end %>
</p>
And in TrackerController:
class TrackerController < ApplicationController
def index
end
def track
puts "navigating to track view"
#id = params[:id]
redirect_to "/tracker/track/#{#id}"
end
end
But I'm getting the error:
The page isn't redirecting properly - Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
However, if I link directly to a page like this:
<%= link_to("Track item 2", {:action => 'track', :id => '6969'}) %>
It works fine. Here is my output when I run rake routes:
Calvins-Air:trckr Calvino$ rake routes
root / tracker#index
/:controller(/:action(/:id))(.:format) :controller#:action
If I use a different action, I can't use the instance variable I set in the controller.
New Controller Code:
def track
puts "navigating to track view"
end
#redirects to track after retrieving the url parameters
#want a url parameter so users can link to the page
def track_helper
#id = params[:id]
redirect_to "/tracker/track/#{#id}"
end
But then the track view, #id can't be accessed:
<h1>Tracker#track</h1>
<p>This page will be used to view an items details</p>
<p><b>Item id: <%= #id %> </b></p>
<%= link_to("Back to index" , {:action => 'index'}) %>
EDIT: Fixed that last error by declaring the #id variable in the track action. Fixed code:
def track
puts "navigating to track view"
#id = params[:id]
end
I guess that's because your redirect path is handled by the same action(and controller) from where you are sending this request. You can either create a new action for it or route this to a different handler.

How to pass variable from instance method into controller?

I need to call method for each user(admin part), which has email parameter. It is a function for paying in PayPal, but I can't use redirection in instances.
Code from my view payments.erb:
% #users.each do |user| %>
<li>
<%= user.email %>
<%= link_to "Pay", user.pay(user.email) %>
</li>
Code of pay method
def pay email
//making post request to PayPal
//res = clnt.post(uri, data, header)
//if res.status ==200
//redirect_to PayPal
//else redirect_to :back
end
How I can pass parameters or how can I reorganize this all ?
Do I need to create an action in pages controller, or can I use some after_call_pay function ?
It isn't the controllers job to respond to instance methods. It's the controllers job to respond to requests.
So you want to be able to link_to an action that responds to mydomain.com/users/1/pay or something like that.
In routes
resources :users do
member do
post 'pay'
end
end
then in your controller
def pay
#user = User.find(params[:id])
#route user to paypal or somewhere else based on some condition
end
And finally in the view
<%= link_to "Pay", pay_user_path(user) %>
I think you should be handling this in a form rather than a link.
If payment is a method associated with a user object then you would want to do something like this:
View -
<%= form_for #user, :url => { :action => "make_payment" } do |f| %>
#any form fields associated with making the payment (ie credit card number)
<%= f.submit %>
<% end %>
This would route the form to the Users_controller and to an action named "make_payment". Make sure to provide a route to this action in your config/routes file as this will not be reachable if you are using the standard resourceful routing.
Controller -
def make_payment
#user = User.find(params[:id])
user.submit_payment(params[:credit_card_num])
redirect_to #user
end
That should accomplish what you are looking to do. Check here for some more explanation on the rails form helpers http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for

Rails form not invoking create controller method on POST

I have the following form in my /app/views/password_resets/new.html.erb view
<% form_tag password_resets_path do %>
<label><%= t(:email) %>:</label><br />
<%= text_field_tag "email" %><br />
<br />
<%= submit_tag t("reset_password") %>
<% end %>
along with a controller called PasswordResetsController containing a create method:
def create
#user = User.find_by_email(params[:email])
if #user
#user.deliver_password_reset_instructions!
self.notice = t("password_reset_instructions_are_mailed")
redirect_to root_url
else
flash[:error] = t("no_user_found")
render :action => :new
end
end
When I go to /password_resets/new, fill out the form, and submit, the create method is invoked properly, since the PasswordResetsController::create() method is invoked when a POST happens to /password_resets.
However, when I put the form in another view, say, /app/views/test/index.html.erb, fill out the form, an submit, I get
Unknown action
No action responded to index. Actions:
access_forbidden, admin_created?,
check_roles, create, edit, find_order,
included, new, role_requirements,
role_requirements=, title, title=, and
update
Any ideas why transplanting the form is not working?
Turns out the issue was related to SSL. This showed up in the log:
Filter chain halted as [:ensure_proper_protocol] rendered_or_redirected.
The page I had the form on required SSL, and Rails did not like me submitting the form from an SSL page to to a non-SSL one. So, as a workaround, since I'm using ssl_requirement, I just put
ssl_required :all
in the password_resets controller, and now things work.

Ruby on Rails -Problem using subdirectories

I have tried to set up a separate section of my app using a subdirectory called controlpanel to manage various parts of the site.
I've set up the namespace in my routes.rb
map.namespace :controlpanel do |submap|
submap.resources :pages
# other controllers
end
And placed the controller and views into the relevant subdirectories.
Controlpanel::PagesController
def new
#page = Page.new
end
def create
if #page = Page.create_with_author(current_user, params[:page])
flash[:notice] = 'Page was successfully created.'
redirect_to ([:controlpanel, #page])
else
render :action => 'new'
end
end
Using this mixed in class method
def create_with_author(author, params)
created = new(params)
created.author = author
if created.save
created
end
end
And the view (controlpanel/pages/new.html.erb renders a partial called _form
<%= render :partial => 'form' %>
Which is as follows:
<% semantic_form_for([:controlpanel, #page]) do |form| %>
<% form.inputs do %>
<%= form.input :title %>
<%= form.input :body %>
<% end %>
<%= form.buttons %>
<% end %>
If I fill in the form correctly, it works as expected, redirecting me to the new page, however, if I leave fields blank, violating the validation constraints, I get the following error:
RuntimeError in Controlpanel/pages#create
Showing app/views/controlpanel/pages/_form.html.erb where line #1 raised:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Can anyone see what is going wrong?
I'm using the formtastic plugin to create the form, but it still happens if I use a regular form.
Any advice greatly appreciated.
Thanks.
Given that the create action is called and new is rendered, Page.create must evaluate to nil.
You probably want to pass params[:page] to create.

Resources