Ruby on rails 3.1.3
I'm trying to figure out the correct route to my change_password method of my users_controller.
I'm currently getting this error:
Routing Error
No route matches {:action=>"change_password", :controller=>"users", :format=>#<User id: 1, email: "foo#bar.baz", encrypted_password: "$2a$10$lgmrRTYFUUrWStLl1Y.Oo.LqQ2Ybxa29YkDFw61/KG9O...", password_salt: "$2a$10$lgmrRTYFUUrWStLl1Y.Oo.", username: "foobar", created_at: "2012-01-07 23:02:29", updated_at: "2012-01-13 11:16:45", password_reset_token: nil, password_reset_sent_at: "2012-01-08 12:23:30">}
In users_controller:
def change_password
#user = current_user
#user_password_matches = User.authenticate(#user.email, params[:current_password])
if #user_password_matches.update_attributes(params[:user])
login #user
format.js { render :js => "window.location = '#{settings_account_path}'" }
flash[:success] = "Password updated"
else
format.js { render :form_errors }
end
end
Inside view:
<%= form_for #user, :remote => true, :url => change_password_path(#user) do |f| %>
Current password: <%= password_field_tag :current_password, :placeholder => "Password" %><br />
New password: <%= f.password_field :password, :placeholder => "Password" %><br />
Confirm password: <%= f.password_field :password_confirmation, :placeholder => "Confirm Password" %><br />
<%= f.submit 'update' %>
routes:
resources :users do
member do
put :change_password
end
end
resources :users
resources :sessions
resources :passwords
resources :profiles
root :to => "users#new"
match 'success' => "users#success"
match 'login' => "sessions#new"
match 'logout' => "sessions#destroy"
match 'reset_password' => "passwords#new"
match 'setup_new_password' => "passwords#edit"
match 'settings', :to => "users#settings"
match "/settings/account", :to => "users#account"
match "/settings/edit_profile", :to => "profiles#edit_profile"
match '/:username', :controller => 'users', :action => 'show'
All I'm intending to do is only update the attributes in db if users entered password (:current_password) successfully passes authentication (the same method I use to log in users which would confirm password matches the one stored in the db) and if it does take the params of users form , in this case the newly hashed password and store it in the db..
Kind regards
You've got the wrong path name in your form, it's change_password_user_path, not change_password_path.
Use rake routes to see the names of your routes. Also, you don't need a custom route for this, check my answer to your previous question.
Related
I'm trying to update my user from a different view than the default registration/edit view. I haven't set the form up all the way yet, but i'm having routing issues to begin with.
This is my form:
%section#profile-editor
.row
.small-12.columns
%h2 Edit Profile
.row
.small-12.columns
.well
=form_tag(edit_thing_registration_path,method: 'PUT', id: "my-awesome-dropzone.dropzone") do
.row
.small-4.columns
%input#photo-dropzone{:name => "file", :type => "file"}/
.small-8.columns
.row
.small-12.columns
%label{:for => "Name"} Name
%input{:name => "Name", :type => "text"}/
.small-6.columns
%label{:for => "Website"} Website
%input{:name => "Website", :type => "text"}/
.small-6.columns
%label{:for => "Itunes"} Itunes URL
%input{:name => "Itunes", :type => "text"}/
.small-6.columns
%label{:for => "Video"} Youtube Video URL (For Profile)
%input{:name => "Video", :type => "text"}/
.small-6.columns
%label{:for => "Email"} Admin Email
%input{:name => "Email", :type => "text"}/
.row
.small-12.columns.pad-top
%label{:for => "RTE"} Content
%input.jqte{:name => "input", :type => "text", :value => ""}/
= submit_tag "Submit", class: "button button-green"
This is my route:
devise_for :things, controllers: { registrations: "things/devise/registrations", sessions: "things/devise/sessions"}
Controller:
class things::Devise::RegistrationsController < Devise::RegistrationsController
def update
# For Rails 4
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# For Rails 3
# account_update_params = params[:user]
# required for settings form to submit when password is left blank
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
#user = thing.find(current_thing.id)
if #user.update_attributes(account_update_params)
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case their password changed
sign_in #user, :bypass => true
redirect_to thing_path(#user)
else
render "edit"
end
end
What happens when I click submit:
No route matches [PUT] "/things/edit"
Rake routes:
new_thing_session GET /things/sign_in(.:format) things/devise/sessions#new
thing_session POST /things/sign_in(.:format) things/devise/sessions#create
destroy_thing_session DELETE /things/sign_out(.:format) things/devise/sessions#destroy
thing_password POST /things/password(.:format) devise/passwords#create
new_thing_password GET /things/password/new(.:format) devise/passwords#new
edit_thing_password GET /things/password/edit(.:format) devise/passwords#edit
PATCH /things/password(.:format) devise/passwords#update
PUT /things/password(.:format) devise/passwords#update
cancel_thing_registration GET /things/cancel(.:format) things/devise/registrations#cancel
thing_registration POST /things(.:format) things/devise/registrations#create
new_thing_registration GET /things/sign_up(.:format) things/devise/registrations#new
edit_thing_registration GET /things/edit(.:format) things/devise/registrations#edit
PATCH /things(.:format) things/devise/registrations#update
PUT /things(.:format) things/devise/registrations#update
DELETE /things(.:format) things/devise/registrations#destroy
If you want to be able to render devise forms from any controller you should put this in your application helper:
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
Hope this helps.
You're using a url that maps to a GET request in your form: edit_artist_registration_path. Instead, you'll want the url of the form to point to the PUT or PATCH url for artist registration:
form_tag(artist_registration_path, method: 'PUT', id: "my-awesome-dropzone.dropzone")
I've followed the directions in devise's wiki regarding overriding update method to update user information without adding password, but I'm still getting notice saying that I can't leave password blank. I'm stuck and I need some assistance on tracking the problem down.
<%= form_for(resource, :as => resource_name, :url => custom_update_user_registration_path, :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.email_field :email, :autofocus => true %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<%= f.submit "Update" %>
in my routes :
devise_for :user, :controllers => { :registrations => "registrations" }, :skip => [:registrations, :sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
post 'signup' => 'devise/registrations#create', :as => :custom_user_registration
get 'users/cancel' => 'devise/registrations#cancel', :as => :cancel_user_registration
get 'account/edit' => 'devise/registrations#edit', :as => :custom_edit_user_registration
put 'account' => 'devise/registrations#update', :as => :custom_update_user_registration
delete 'users/cancel' => 'devise/registrations#destroy'
# devise/sessions
get 'signin' => 'devise/sessions#new', :as => :new_user_session
post 'signin' => 'devise/sessions#create', :as => :user_session
delete 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
Here's my controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def update
#user.attributes = params[:user]
# required for settings form to submit when password is left blank
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
end
I'm not sure why I'm getting the error that I need to submit password still? Can someone see something that I'm overlooking?
Thanks
You could handle that in your model normally.
Just edit your valdiations. This way you normally do not need to override the whole controller..just edit the model:
validates :password, :presence => true, :on => :create
I just found out that I've been overriding my devise registration incorrectly.
This was the correct way of having registration controller
Rails 3 /devise seems to ignore custom controller
Try to change
if #user.update_attributes(params[:user])
to device method
if #user.update_without_password(params[:user])
In class RegistrationsController < Devise::RegistrationsController you are missing update parameters for devise.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, ...) }
end
def update
# Get all params for :account_update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# Allow user to update without using password.
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
# Set current_user
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
After Googling everywhere about how I can fix this I decided to post a question. I'm trying to add a custom action "create_pro" to the Devise Registrations controller so I can create a user with additional fields (A pro user). This form is on the same page as the devise create action for a user.
I keep getting the following error after submitting the form:
Unknown action
The action 'create_pro' could not be found for
Devise::RegistrationsController
How can I fix this?
Routes
devise_for :users,:controllers => { :registrations => "registrations" }
devise_scope :user do
post "gopro", :to => "devise/registrations#create_pro"
end
Registrations Controller
class RegistrationsController < Devise::RegistrationsController
before_filter :authenticate_user!, :only => :token
def new
super
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:notice] = "Welcome to Banking Analytics."
redirect_to '/mybank'
else
render :action => :new
end
end
def create_pro
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:notice] = "Welcome to Banking Analytics."
redirect_to '/mybank'
else
render :action => :new
end
end
end
View
<div class="signup_form_basic">
<%= simple_form_for #user, :url => url_for(:controller => 'registrations', :action => 'create_pro') do |f| %>
<%= f.error_notification %>
<%= f.input :email, :required => true, :autofocus => true, :label => 'Username ( Your Email )', :placeholder => 'Email Address' %>
<%= f.input :password, :required => true, :autofocus => true, :label => 'Password', :placeholder => 'Password' %>
<%= f.input :password_confirmation, :required => true, :autofocus => true, :label => 'Confirm Password', :placeholder => 'Password Confirmation' %>
<%= f.button :submit, "Sign Up >>", class: 'btn btn-inverse' %>
<% end %>
</div>
I don't have an answer for the 'Unknown Action error' that you are facing, but I think the current solution is sub-optimal.
Since your goal is to have the following two classes of users
Pro-users, and
Regular (Non-pro) users,
it is probably best to follow the suggestion at How To: Customize routes to user registration pages and set up parallel structures for the two types of users.
That would be much more in line with the relationship between the two types of users.
I'm following this Ruby Railcasts episode to get some simple auth going, and I'm getting the error uninitialized constant Sessions when I navigate to the login page. I've checked my classes and the names are proper, which seems to be the problem most people report. Any idea what might be happening here?
The name of my controller file is sessions_controller.rb and the code is as follows:
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => 'Logged In!'
else
flash.now.alert = 'Invalid email or password'
render 'new'
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => 'Logged Out!'
end
end
routes.rb file:
Albumtracker::Application.routes.draw do
get "login" => "sessions/new", :as => "login"
get "logout" => "sessions/destroy", :as => "logout"
get "signup" => "users/new", :as => "sign_up"
root :to => 'users#new'
resources :users
resources :sessions
get "pages/index"
end
sessions/new view file:
<h1>Log in</h1>
<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %><br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>
You have typos in your routes. / should be # when specifying a controller and action. Your routes should look like this:
get "login" => "sessions#new", :as => "login"
get "logout" => "sessions#destroy", :as => "logout"
get "signup" => "users#new", :as => "sign_up"
When you use / in your route the preceding portion is matched to a namespace. So from "sessions/new" the router was trying to locate a controller named Sessions::NewController. Since the namespace Sessions doesn't exist in your app, you got the uninitialized constant error.
I'm trying to incorporate Devise and Cancan into a web app. I want users with :role => "admin" to be able to delete users, and Devise's destroy action only allows users to delete themselves, so I've created a custom action for this purpose. (To override the plugin's controller file, I've copied the registrations controller over to app/controllers/registrations_controller.rb.)
Here is my custom action in my registrations_controller.rb:
def destroy_user_account
#user = User.find_by_id(params[:user])
#user.destroy
redirect_to profiles_path, :flash => { :success => "User deleted!" }
authorize! :destroy, User, :message => "You don't have authorisation to delete this user."
end
Here is how I'm trying to use it, in a link on the page where you view a user's profile. (I have things set up so that each user has_one profile; profiles are what you see at the front end. A profile is automatically created in the profiles table on user registration.)
<% if can? :update, #profile %>
| <%= link_to 'Edit Profile', edit_profile_path(#profile) %>
| <%= link_to 'Edit Settings', edit_settings_path %>
<% end %>
<% if can? :destroy, #profile.user %>
| <%= link_to "Delete User", destroy_user_account(#profile.user),
:class => "delete",
:confirm => "Are you sure?",
:title => "Delete #{#profile.user.name}"
%>
<% end %>
My tests are showing 2 failures that I can't resolve:
1) ProfilesController GET show when signed in as an admin should
have a link to edit the profile
Failure/Error: get :show, :id => #profile
ActionView::Template::Error:
undefined method destroy_user_account' for #<#<Class:0x105b474a8>:0x1057f32e8>
# ./app/views/profiles/show.html.erb:41:in_app_views_profiles_show_html_erb___917863454_2195331000_0'
# ./spec/controllers/profiles_controller_spec.rb:143
2) ProfilesController GET show when signed in as an admin should
have a link to delete the user's account (using the
destroy_user_account action in the registrations controller)
Failure/Error: get :show, :id => #profile
ActionView::Template::Error:
undefined method destroy_user_account' for #<#<Class:0x105b474a8>:0x105806d20>
# ./app/views/profiles/show.html.erb:41:in_app_views_profiles_show_html_erb___917863454_2195331000_0'
# ./spec/controllers/profiles_controller_spec.rb:148
Also, when I try it out in my browser, clicking on the "Delete user" link gets me the following error:
Routing Error
No route matches "/destroy-user-account/2"
Here are the routes that should cover this:
devise_for :users, #:path => '',
:skip => [ :confirmations, :passwords, :registrations ],
:controllers => { :registrations => "registrations" } do
# Routes for ACCOUNT REGISTRATIONS
get "join", :to => "registrations#new", :as => :new_user_registration
post "join", :to => "registrations#create", :as => :user_registration
get "settings/account", :to => "registrations#show", :as => :settings
get "settings/account/edit", :to => "registrations#edit", :as => :edit_settings
put "settings/account", :to => "registrations#update", :as => :update_settings
delete "close-my-account/:id", :to => "registrations#destroy", :as => :close_my_account
delete "destroy-user-account/:id", :to => "registrations#destroy_user_account", :as => :destroy_user_account
Can anyone help with what I'm doing wrong?
In the browser it isn't matching the route because you're sending a GET request but the route only matches a DELETE request. The test fails because the route gives the name destroy_user_account_path instead of destroy_user_account.
Try:
<%= link_to "Delete User", destroy_user_account_path(#profile.user),
:class => "delete",
:confirm => "Are you sure?",
:title => "Delete #{#profile.user.name}"
:method => :delete
%>