0

I am following the Ruby on Rails Tutorial and am coming across an error which says, in full: ActionView::Template::Error: No route matches {:action=>"edit", :controller=>"password_resets", :email=>"steve@example.com", :id=>nil}, possible unmatched constraints: [:id]

This relates to code allowing the user to reset their password from a form. The error refers to a route, so I'll start there with routes.rb:

get 'sessions/new'

root    'static_pages#home'
get     '/about',     to: 'static_pages#about'
get     '/contact',   to: 'static_pages#contact'
get     '/help',      to: 'static_pages#help'

get     '/signup',    to: 'users#new'
post    '/signup',    to: 'users#create'
get     '/login',     to: 'sessions#new'
post    '/login',     to: 'sessions#create'
delete  '/logout',    to: 'sessions#destroy'

resources :users
resources :account_activations, only: [:edit]
resources :password_resets,     only: [:new, :create, :edit, :update]

The error mentions the edit action which is blank within the controller. The reset functionality is produced from the create action in the password_resets_controller like:

def create 
  @user = User.find_by(email: params[:password_reset][:email].downcase)
  if @user
    @user.create_reset_digest
    @user.send_password_reset_email
    flash[:info] = "Email sent with reset instructions"
    redirect_to root_url
  else
    flash[:danger] = "Email address not found"
    render 'new'
  end
end

def edit
end

This pulls the user from the database using the email submitted on the form. Then calls two methods from the model to walk through the reset process. First, the create_reset_digest method which creates a token and updates the attributes for that user:

def create_reset_digest
  self.activation_token = User.new_token # <- error here; wrong attribute
  update_attributes(reset_digest: User.digest(reset_token), 
                    reset_sent_at: Time.zone.now)
end

Then, it sends the password reset email:

def send_password_reset_email
  UserMailer.password_reset(self).deliver_now
end

In the email, there is mention of the edit_password_reset_url which I think is the cause of this error. The view for that email is:

<%= link_to "Password reset", edit_password_reset_url(@user.reset_token, 
                                                      email: @user.email) %>

This method takes the user's reset token and email and presents this in a url that the user can visit to reset their password. The url takes the form http://example.com/password_resets/reset_token/edit?email="steve%40example.com"

The error implies that there's a constraint that remains unmatched; that the [:id] isn't matched - indeed, it is shown to be nil. Where does that :id=>nil come from and how can I get it to match to, presumably, the user id? Can I imply anything else from the error message?

I am getting this error when I run a rudimentary test:

test "password_reset" do
  @user.activation_token = User.new_token
  mail = UserMailer.password_reset(@user)
  assert_equal "Password reset",        mail.subject
  assert_equal ["to@example.org"],      mail.to
  assert_equal ["noreply@example.com"], mail.from
  assert_match "Hi",                    mail.body.encoded
end

When I use the web app to fire the password reset, I get a slightly different error in that it is a ActionController::UrlGenerationError in PasswordResets#create. This highlights the edit_password_reset_url((@user.reset_token, email: @user.email) method. I've checked in the book and can't see that this line is incorrect.

Your advice is welcomed.

OnlySteveH
  • 152
  • 1
  • 2
  • 11
  • 1
    Where do you set `@user.reset_token` (used in `link_to`)? I see something like `self.activation_token = User.new_token`. But not something like `self.reset_token = ...`. – jvillian Nov 11 '17 at 15:39
  • Thanks @jvillian! That's what happens when I blindly cut & paste methods. You're absolutely correct - I was setting `self.activation_token` and not ```self.reset_token`. So, presumably, `reset_token` was `nil`. Anyway; I've changed that part of the code and all seems fine. Thank you for your response. – OnlySteveH Nov 11 '17 at 15:44
  • Glad to help. I added as an answer in case you care to upvote/accept. – jvillian Nov 11 '17 at 16:04
  • Done! :) All up-votes help, right! – OnlySteveH Nov 11 '17 at 16:07

1 Answers1

2

It appears you do not set @user.reset_token (used in link_to)? I see something like self.activation_token = User.new_token. But not something like self.reset_token = .....

jvillian
  • 19,953
  • 5
  • 31
  • 44
  • could you look at another outstanding question I have on here, if you have a minute. TIA. https://stackoverflow.com/questions/47105824/password-validation-fails-in-two-opposed-scenarios – OnlySteveH Nov 13 '17 at 12:15