OnlySteveH
OnlySteveH

Reputation: 152

Ruby on Rails ActionView::Template::Error

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=>"[email protected]", :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 ["[email protected]"],      mail.to
  assert_equal ["[email protected]"], 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.

Upvotes: 0

Views: 981

Answers (1)

jvillian
jvillian

Reputation: 20263

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 = .....

Upvotes: 2

Related Questions