RESTful way of updating a model from different view - ruby-on-rails

My application has a User model.
In the home#index view, I present a form for the current User to update their email address, using form_for.
Now if my application is to remain RESTful, as I understand it the Haml in app/views/home/index.html.haml should look something like:
- form_for current_user, :url => { :action => "update", :controller => "user" } do |f|
= f.text_field :email
= f.submit "Update email address"
My question is as follows: what is the correct way to get the User model to redirect to the home#index view after the update?

You can give redirect to your format.html on your controller def update, like
format.html { redirect to :back }
this will redirect to the page that send request before.
That's the simplest way..!

In this case I actually reverted to using resourceful routes... far cleaner.
Thanks for the other suggestions.

Related

How to use custom view to update user via devise? (rails gem)

I have 2 custom views I built for devise, 1 changes the password the other updates other custom information. The password update worked, so I copied the method for the second view, however I am getting an error when submitting the data
No route matches [PATCH] "/users/discovery_settings/update"
which does not make sense because my route is
devise_for :users, controllers: {registrations: 'users/registrations'}
as :user do
end
get 'users/discovery_settings' => 'users#discovery_settings'
post 'users/discovery_settings/update' => 'users#update_discovery'
in my controller i user the same method that works for password updates
def update_discovery
#user = User.find(current_user.id)
if #user.update(user_params)
# Sign in the user by passing validation in case their password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
then i call it in my form_for view
<%= form_for(#user, :url => { :action => "update_discovery" }, html: {class: "form floating-label"}) do |f| %>
Any ideas how to fix the routing error?
I am at a lost as to why it is looking for "patch" when i have already specified "post"
The error's telling you. You have a POST route and the update is asking for a PATCH route.
try with patch instead of post:
patch 'users/discovery_settings/update' => 'users#update_discovery'

Render to named path

I have a rails project with the following route:
get 'login', to: 'user_sessions#new', as: :login
In my UserSessionsController I have
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
# Do all the happy stuff
else
format.html { render :action => 'new' }
format.xml { render xml: #user_session.errors, status: :unprocessable_entity }
end
end
end
That's working ok, except that when the user enters incorrect parameters the route is via /user_sessions instead of /login, which is untidy (and means my test assertions are confusing).
Obviously I could just redirect_to login_path, but then my #user_session.errors don't seem to be available so by page doesn't show what was wrong.
How do I redirect back to /login and still have the errors show?
Edit:
It looks as if Rails makes this difficult because it's something I shouldn't try to do. The RESTful path isn't really something the user cares about so I shouldn't be using it as part of my UI testing. Instead, I am looking at the actual content of the rendered page, which the user does care about. Thanks all.
You can add
post 'login', to: 'user_sessions#create', as: :post_login
and change the form action accordingly.
This is happening because when you get validation errors in your form then you are on create action and not new action. Your create action simply render your new actions template with errors, it doesn't send a request to server and hence your url remains same so to fix it you can simply change the route for your create action to this:
post 'login', to: 'user_sessions#create', as: :login
Update:
You'll just have to change your route for create action and then make changes in your form, something like this:
<%= from_for #resource, url: login_path do |f| %>
// form fields
<% end %>
If you'll inspect your form you'll see that its method is POST so when you'll submit it, your form will send a POST request and when you hit /login in your browsers address bar it'll send a GET request so in first case you'll go to create action and in second one you'll go to new action

rails multiple forms in one view

I have index method that displaying two forms Sign in and Sign up, that means user can create an account and a log in from same place.
so I have users controller with index method that displaying a view with Sign in and Sign up form with two partials one is _signin.html.erb and _signup.html.erb in index.html.erb.
Any Idea How can I handle new and create methods from users and sessions controllers (may be I can ignore new method)?
As long as each form is being rendered with the correct model object and/or the correct value to the :url option, each form should send the expected request (assuming you're rendering your forms with form_for).
For example, your sign in form should start with something like this::
<%= form_for :session, :url => sessions_path %>
#...
<% end %>
As long as a POST request (the default from form submissions) is sent to a "collections" resource (i.e. /sessions) it will route the request to the create action in your SessionsController or whatever you named your controller.
For signing up, you probably have something like this:
<%= form_for #user do |f| %>
# ...
<% end %>
The #user model object will assume the request should go to /users. Again this will call your create action in your UsersController.
Of course, all this is also assuming your config/routes.rb file is just declaring each resource with something like:
resources :users
resources :sessions, :only => [:create, :destroy]
You usually have each form in separate views such as
match 'sign_in', :to => 'sessions#new'
match 'sign_up', :to => 'users#new'
But if you only want to display these forms in an index.html.erb view then these routes are no longer necessary.
<%= form_for :signin,:url=>{:controller=>"yourcontroller",:action=>"signin"},:html=>{:id=>"signin_form"} do |f|%>
...
<%end%>
<%= form_for :signup,:url=>{:controller=>"yourcontroller",:action=>"signup"},:html=>{:id=>"signup_form"} do |f|%>
...
<%end%>
One simple workaround that I found was that if the two forms have at least one uniquely named parameter, then you can simply route the POST request to a single action. Then within the action check which parameter exists and execute the corresponding code. You end up having essentially two actions within one action in your controller.
def create
if params[:username] and !params[:name]
# You know that the user pressed submit on whichever form
# has a field that fills params[:username].
# So do the action with that form's parameters here
# i.e, login an existing user
respond_to do |format|
format.html {redirect_to '/home', notice: "Login successful"}
end
elsif params[:name] and !params[:username]
# You know that the user pressed submit on whichever form
# has a field that fills params[:username].
# So do the action with that form's parameters here
# i.e, create a new user
respond_to do |format|
format.html {redirect_to '/onboard', notice: "Thanks for signing up"}
end
end
end
Just be sure to have it configured in your routes.rb so that when a POST request comes from the page the two forms are on, it will direct to this action in this controller.
Hope this helps!

Ruby on Rails: Send email from show page

I am a Rails noob and have a problem sending an email from a show page. There are several contact form tutorials out there but I cannot find one where I send an email from a page like a 'show' page. I have big errors in my routes I believe. In the model I state that Users have several Promotions and on the promotions show page I want to allow the current_user to send an email to #user.
here is app/mailers/quote_mailer.rb
class QuoteMailer < ActionMailer::Base
default :from => "tim#example.com"
def quote_mail(promotion)
#user = user
mail(:to => user.email, :subject => "You have an inquiry homeboy!")
end
end
In promotions_controller I put this action which I think might be wrong:
def quotedeliver
QuoteMailer.quote_mail.deliver
flash[:notice] = 'report sent!'
redirect_to root_path # or wherever
end
Here is the form that I use to send the email (the :url is probably wrong but I dont know how it should look)
<%= form_for quote_mail, :url => quotedeliver_promotion_path(promotion), :html => {:method => :put } do |f| %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
I would love some help with this. I cannot find anything like it on stackoverflow, I have been trying for days. Thank you!
You are probably missing the route in config/routes.rb
you can define it like
post '/quotedeliver_promotion' => 'promotions#quotedeliver', :as => quotedeliver_promotion
Note that quotedeliver has to be rewritten quote_deliver to follow ruby syntax conventions. When you call
QuoteMailer.quote_mail.deliver
You are not giving the parameter, so try this
QuoteMailer.quote_mail(current_user).deliver
And change your method with
def quote_mail(user)
mail ....
end
and you are all good
There are some excellent screen casts (through Railscasts) regarding sending e-mail http://railscasts.com/?tag_id=28.
One last thing, do not attach your mail sending method to a show action (if you are doing currently), the reason is show action is only for view something and users might be refreshing that page, So if you attach a mailer to that, mails might go out for each refresh.
def quote_mail(promotion)
#user = user
mail(:to => user.email, :subject => "You have an inquiry homeboy!")
end
from where you are access user variable, it should be promotion i think.

How do I maintain the same controller & action in a page URL upon re-rendering an action in Rails?

I am using AuthLogic to authenticate users in my rails app. That part is set up and workign properly.
I have the following route defined:
map.login '/account/login', :controller => :user_sessions, :action => :new
Calling rake routes returns what I expect:
login /account/login {:controller=>"user_sessions", :action=>"new"}
When someone submits a login, it calls UserSessionsController.create:
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
redirect_back_or_default account_url
else
render :action => :new
end
end
If #user_session.save fails, the appropriate error messages appear on the screen. However, the browser URL also changes to "http://localhost:3000/user_session" instead of staying on "http://localhost:3000/account/login".
I assume the problem is what I am feeding to the render method. What should I be feeding it?
This is actually the intended behavior for this process. In a standard scaffolded RESTful controller, a validation error in the create and update actions will simply render the original template without redirecting. This results in what you are seeing – the new template will be displayed with the create action's URL in the URL bar. The reason for this is that in order to display information to the user about what errors occurred, the view must have access to the invalid model object, which is #user_session in your case.
You can use redirect_to instead of render if you want to force a redirect to the original URL, but this will cause you to lose information about the errors. You would need to manually persist the errors in the session, which would be messy. My advice is not to worry about the fact that the URL doesn't match that of the original as this is pretty standard in all Rails apps.
Just adding solution for Rails 4 (based on Shaun's answer here):
Add new route to routes file:
post '/carts/new' => 'carts#create', as: :create_post
Add url: create_post_path to form tag
Done.
After further digging, I found the solution in another StackOverflow question: Use custom route upon model validation failure
I simply modified my routes to add a new one for posing to '/account/login':
map.login '/account/login', :controller => :user_sessions, :action => :new, :conditions => {:method => :get}
map.login_post '/account/login', :controller => :user_sessions, :action => :create, :conditions => {:method => :post}
Then, I updated my view to utilize the new route:
<% form_for #user_session, :url => login_post_path do |f| %>
This works perfectly. A failed login gives the appropriate error messages and maintains the '/account/login' URL.

Resources