I am writing a small rails application and I am attempting to add the ability to for users to reset their passwords in a method similar to rails cast 274 (http://railscasts.com/episodes/274-remember-me-reset-password). I was able to add this functionality, but it now seems to have broken. I have a link to a reset_password form from the signin page. The rails code is below:
<h1>Reset Password</h1>
<%= form_tag password_resets_path, :method => :post do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class="actions"><%= submit_tag "Reset Password" %></div>
<% end %>
And below is the html generated by the browser (for the password reset form, not the page source).
<form accept-charset="UTF-8" action="/password_resets" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="1UwA3fS90cean/z/g60M3CJYs1RNtHjyy4a1yAssWo0=" /></div>
<div class="field">
<label for="email">Email</label>
<input id="email" name="email" type="text" />
</div>
<div class="actions"><input name="commit" type="submit" value="Reset Password" /></div>
</form>
I am somewhat new to rails and html, but I think this form is passing data to my controller via the 'post' method. I think this is what I want, because running rake routes gives me (just for password resets - I also have a user and sessions resource):
users GET /users(.:format) users#index
password_resets GET /password_resets(.:format) password_resets#index
POST /password_resets(.:format) password_resets#create
new_password_reset GET /password_resets/new(.:format) password_resets#new
edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit
password_reset GET /password_resets/:id(.:format) password_resets#show
PUT /password_resets/:id(.:format) password_resets#update
DELETE /password_resets/:id(.:format) password_resets#destroy
So I think submitting this form should get me to the create action on my password_resets controller. However, I get:
Routing Error
No route matches {:action=>"edit", :controller=>"password_resets", :id=>nil}
Which means the form is somehow trying to submit to the edit action. If this is the case, id=>nil makes sense because my form is asking for an :email, not :id. I am not sure why/how this is happening, because I am treating password_resets like a resource (as in the rails cast), and just using the routes rails automagically generates.
Below is my routes.rb file and my password_resets controller file.
MyApp::Application.routes.draw do
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :password_resets
root to: 'static_pages#home'
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#help'
match '/signup', to: 'users#new'
match '/remove', to: 'users#remove'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
end
And the password_reset controller:
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
#this will work regardless of whether user is found
#a bit more secure, but also a bit more annoying to the
#user...
redirect_to root_url, :notice =>
"Email was sent with password reset
instructions"
end
def edit
@user = User.find_by_password_reset_token!(params[:id])
end
def update
@user = User.find_by_password_reset_token!(params[:id])
if @user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert =>
"Password reset has expired."
elsif @user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset!"
else
render :edit
end
end
end
I have also created an action mailer to send the emails to the user, but I don't think there is a point to looking there yet because I am breaking before I get to any code that involves actually sending email. I would really appreciate any thoughts/speculation as to why my form is behaving as it is, rather than how I want it to.
Thanks to everyone in advance.
Update:
Thank you cortex! As suggested, I looked at the server log and my form was going to the create action, and the code was going through generating the email. However, the password_reset_token, which I give the user in the email as part of the url where they reset there password, is always nil, Which gives me the error. I produce the token through the following code (which I think is the same as the Rails Cast)
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
I try to assign it to the user via:
def send_password_reset
self.password_reset_token = generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
self.save(validate: false)
UserMailer.password_reset(self).deliver
end
So my question now is why would my generate token methods return a nil value? This is puzzling because I use the generate token method to generate a token to track users throughout a session, and it is working fine there...
Nathan