On a rails app setup with Devise, i am trying to provide users with a form to change passwords.
i have followed the solution 3 from the Devise wiki:https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-edit-their-password
and accordingly have in a user controller
class UsersController < Devise::RegistrationsController
def update_password
#user = current_user
if #user.update(user_params)
# Sign in the user by passing validation in case their password changed
bypass_sign_in(#user)
redirect_to root_path
else
render "edit"
end
end
end
routes.rb
devise_for :users,
path: "", path_names: {
sign_in: "login",
sign_out: "logout",
sign_up: "register",
edit: "settings"
},
controllers: {
registrations: "users",
sessions: "users/sessions"
}
resources :users do
patch 'update_password'
end
Rake routes gives me :
user_update_password_path POST (/:locale)/users/:user_id/update_password(.:format)
users#update_password {:locale=>/fr|en|de/}
the link to access the menu is the following:
<%= link_to user_update_password_path(current_user) %>
in browser, that links directs me to :
http://localhost:3000/en/users/1/update_password
but I receive a Routing error
No route matches [GET] "/en/users/1/update_password"
When I wrap the
resources :users do
resources :wishlists
collection do
patch 'update_password'
end
end
the link_to send to
http://localhost:3000/1/password
Which results in the error
undefined local variable or method `user_update_password_path' for
<#:0x00007f86cfe48f10> Did you mean? user_password_path
however, rails routes shows:
update_password_users PATCH (/:locale)/users/update_password(.:format) users#update_password {:locale=>/fr|en|de/}
but a link_to
update_password_users_path
results in an error
Could not find devise mapping for path "/en/users/update_password".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller" end
2) You are testing a Devise controller bypassing the router. If so,
you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
What have I missed ?
First of all in the solution 3 it says resource not resources. Watch the differences carefully between yours and the below one -
resource :user, only: [:edit] do
collection do
patch 'update_password'
end
end
Second the like should direct to edit_user_path not update_password_user as that is the patch route.
Third, you have to add a edit action to your controller as the wiki suggests. And also a form for the action.
before_action :authenticate_user!
def edit
#user = current_user
end
and in app/views/users/edit.html.erb
<%= form_for(#user, :url => { :action => "update_password" } ) do |f| %>
<div class="field">
<%= f.label :password, "Password" %><br />
<%= f.password_field :password, :autocomplete => "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="action_container">
<%= f.submit %>
</div>
<% end %>
You missed lots of things. Try reading the wiki again.
Related
I'm new to ROR. I'm trying to create a page for parking permit application. I encountered this problem
I couldn't find the problem. Or maybe i missed something. Any help is appreciated.
This is my permit_controller.rb
class PermitController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permit = Permit.all
end
def new
#permit = Permit.new
end
def create
#permit = Permit.new(user_params)
if #permit.save
redirect_to root_path
else
flash[:success] = "Welcome to your profile!"
end
end
def destroy
end
def show
#permit = Permit.find(params[:id])
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permit = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration,:permitstart,:permitend)
end
end
This is my permit/new.html.erb
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permit) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :"Vehicle Type" %>
<%= f.text_field :vehicle_type, class: 'form-control' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, class: 'form-control' %>
<%= f.label :department %>
<%= f.text_field :department, class: 'form-control' %>
<%= f.label :"Car Plate" %>
<%= f.text_field :carplate, class: 'form-control' %>
<%= f.submit "Confirm", class: "btn btn-primary" %>
<% end %>
</div>
</div>
This is my route.rb
Rails.application.routes.draw do
resources :users
resources :permit
get 'permit/destroy'
get 'permit/show'
root 'static_pages#home'
get 'homepage/index'
post 'permit' => 'permit#create'
get 'permitapplication' => 'permit#new'
get 'adminlogin' => 'admin_controller#index'
get 'contact'=> 'static_pages#contact'
get 'about' => 'static_pages#about'
get 'signup' => 'users#new'
get 'help' => 'static_pages#help'
post 'users' => 'users#create'
get 'login' => 'sessions#new' #Page for a new session
post 'login' => 'sessions#create' #Create a new session
delete 'logout'=>'sessions#destroy' #Delete a session
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Instead resources :permit use resources :permits.
The biggest issue here is you don't have permit/new in your routes.rb file.
As has already been suggested, it might be better for you to leverage rails
with a resources call
in routes.rb
resources :permits
and remove lines
get 'permit/destroy'
get 'permit/show'
etc.
I'll attempt to consolidate our various answers and comments.
To solve your current problem, in config/routes.rb, change resources :permit to resources :permits. This exposes all seven RESTful routes for use in your application. (This also makes obsolete the custom permit routes, unless you're explicitly calling them from within their respective forms.) Information about RESTful routes/resources here: http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
Why is this valuable?
It's how your application knows to use controller actions in combination with views (and therefore forms). Say you have an edit action in your permits controller and app/views/permits/edit.html.erb has a form. Within this page's edit form, you need only have form_for #permit and Rails does all the rest. It knows you're using this particular route. I recommend you read about routing within Rails.
Please keep in mind Ruby on Rails has been carefully crafted to make things easy for you, the developer.
I'm building my second-ever basic Ruby on Rails application and having fun doing it, but have gotten stuck at precisely the same place that gave me trouble (and was never solved) on my last effort: the PUT or PATCH request.
My application has two models: entries and users. A logged-in user should be able to edit only those entries that were originally created by that user.
CONTROLLER
class EntriesController < ApplicationController
# authenticate user (Devise)
before_action :authenticate_user!, :except => [:index, :show]
# set entry upon page load
before_action :set_entry, :only => [:show, :edit, :update, :destroy]
# GET request - display all entries
def index
#all_entries = Entry.all
end
# GET request - display an individual entry
def show
# nothing required here because entry identified with before_action :set_entry on line 2 above
end
# GET request - access form to create a new entry
def new
#entry = Entry.new
#user = User.find(current_user[:id])
end
# GET request - access form to update an existing entry
def edit
if #entry[:user_id] != current_user[:id]
redirect_to root_path
else
redirect_to edit_entry_path
end
end
# POST request - make a new entry/save new data into db
def create
user = current_user[:id]
Entry.create({
entry_title: params[:entry][:entry_title],
book_title: params[:entry][:book_title],
text: params[:entry][:text],
img_url: params[:entry][:img_url],
tag: params[:entry][:tag],
created_at: params[:entry][:created_at],
user_id: user
})
redirect_to entries_path
end
# PUT request - save changes to an existing entry
def update
if #entry.update(entry_params)
redirect_to entry_path
else
render :new
end
end
# DELETE request - delete an existing entry from db
def destroy
#entry.destroy
redirect_to entries_path
end
private
def set_entry
#entry = Entry.find(params[:id])
end
def entry_params
params.require(:entry).permit(:email, :text, :tag)
end
end
VIEW (show.html.erb - shows a single entry and includes links allowing the logged-in user who originally authored the entry to edit or delete it)
<h3>Selected Entry</h3>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6">
<div>Entry title: <%= #entry.entry_title %></div>
<div>Book title: <%= #entry.book_title %></div>
<div>Text: <%= #entry.text %></div>
</div>
<div class="col-md-4">
<div><%= #entry.created_at.strftime("%b %d, %Y") %></div>
<div>Submitted by: <i><%= #entry.user.email %></i></div>
<div>File under: <i><%= #entry.tag %></i></div>
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
</div>
</div>
ROUTES.RB - At first my routes were the commented-out lines, but then I had a thought that was either madness or sudden realization - should only the GET routes lead with "get"? So that's the non-commented-out attempt you see. Somehow the app works (except for the issue at hand) both ways.
In researching I've come across routes defined using a much more elaborate syntax than that I'm using here. I've been unable to figure out whether a given way of doing things is different convention, outdated, or just inadequate to the task.
Rails.application.routes.draw do
devise_for :users
resources :entries
# root 'entries#index'
# get '/entries' => 'entries#index'
# get '/users' => 'users#index'
# get '/entries/:id' => 'entries#show'
# get '/entries/:id' => 'entries#update'
# get '/entries/new' => 'entries#new'
# get '/entries/:id/edit' => 'entries#edit'
# get '/users/:id' => 'users#show'
# get '/about' => 'pages#index'
root 'entries#index'
get '/entries' => 'entries#index'
get '/entries/new' => 'entries#new'
post '/entries' => 'entries#create'
get '/entries/:id' => 'entries#show'
get '/entries/:id/edit' => 'entries#edit'
put '/entries/:id' => 'entries#update'
delete '/entries/:id' => 'entries#destroy'
get '/users' => 'users#index'
get '/users/:id' => 'users#show'
get '/about' => 'pages#index'
end
Thanks in advance for any insight. If additional context is needed I'm happy to provide.
Edited to add:
PARTIAL (_form.html.erb)
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6" id="form-container">
<%= form_for #entry do |form| %>
<br>
<%= form.text_field :entry_title, :size => 59, :placeholder => "Entry Title"%>
<br><br>
<%= form.text_field :book_title, :size => 59, :placeholder => "Book Title"%>
<br><br>
<%= form.text_field :img_url, :size => 59, :placeholder => "Image URL"%>
<br><br>
<%= form.text_area :text, :placeholder => "Text" %>
<br><br>
<%= form.text_field :tag, :placeholder => "Tag" %>
<br><br>
<%= form.submit %>
<% end %>
</div>
<div class="col-md-4"></div>
</div>
To edit a record you
first, should use a GET request to get the edit form
second, should submit that form using a PUT/PATCH request
To get to the edit form you should link to the edit path for your entry
<%= link_to 'Edit', edit_entry_path(#entry) %>
The Rails form helpers will automatically set the form to submit with the proper method, PUT OR PATCH.
:method in link_to helpers refers to HTML verb (get, post, etc), while controllers methods naming convention is action.
link_to
You need something as
<%= link_to 'Edit', #entry, :method => 'put' %>
or
<%= link_to 'Edit', #entry, :action => 'update' %>
At a glance you are trying to post with the edit link. Remember new/edit are get methods to render form, so just just delete method part in your links. Like from
<%= link_to 'Edit', #entry, :method => 'update' %>
to
<%= link_to 'Edit', edit_entry_path(#entry) %>
I'm building my second-ever basic Ruby on Rails application
Congrats! You need at least 3 more before it all starts to make sense
To add to the existing answers, you'll be best looking at the resources directive to clean the routes up:
#config/routes.rb
root 'entries#index'
devise_for :users
resources :entries
resources :pages, only: [:index], path_names: { index: "about" }
resources :users, only: [:index,:show]
--
A logged-in user should be able to edit only those entries that were originally created by that user.
This is known as authorization.
Authentication = is user logged in?
Authorization = can user do this?
Although people confuse Devise with being able to handle authorization, it only handles authentication. Whilst you have a simple implementation of this in your controller, you should check out either the CanCanCan or Pundit gems:
#Gemfile
gem "cancancan"
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, Post, user_id: user.id
end
end
#app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def edit
authorize! :edit, #entry
end
end
--
Finally, to answer your question directly, you're calling the update method (which doesn't exist) to access the edit view:
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
You should read up about http verbs - this is what the "method" option invokes with the link. As mentioned above, you don't need to set the method for edit as it uses GET. Update uses put/patch, which I can explain later.
A much better way to achieve what you want would be the following:
<%= link_to "Edit", edit_entry_path(#entry) if can? :edit, #entry %>
<%= link_to "Delete", #entry, method: :delete, if can? :destroy, #entry %>
The above uses the CanCanCan authorization method can?
I am very new to rails and am attempting to get some practice by building a profile page. I have implemented devise and once a user has signed in they can click on a link that displays a profile with information about them. Right now it is just displaying their name, date of birth, and gender. I would like for it to also display an image of them. So I have bundled the paperclip gem and read through some tutorials to try and get it configured. I have gotten the code in place, so that a user can click on 'choose file' and select an image. However when I click 'update' it provides me with an error. I'd like for the image to display on the same show page of their profile.
profiles_controller.rb
class Users::ProfilesController < ApplicationController
def edit
#profile = Profile.create(params[:photo])
end
def create
end
private
def profile_params
params.require(:profile).permit(:photo)
end
end
profiles/edit.html.erb
<h2>Edit Account Profile </h2>
<%= form_for(#profile, url: profiles_edit_path(#profile), html: { :multipart => true }) do |f| %>
<li>
<%= f.label :photo, "Photo" %>
<%= f.file_field :photo %>
</li>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<%= link_to "Back", :back %>
registrations/show.html.erb
<%= image_tag #profile.photo.url %>
<br>
<%= #user.email %>
<br>
<%= #user.first_name %>
<%= #user.last_name %>
<br>
<%= #user.date_of_birth %>
<br>
<%= #user.is_female %>
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def show
#user = User.find(params[:id])
end
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :is_female, :date_of_birth, :email, :password, :password_confirmation)
end
def account_update_params
params.require(:user).permit(:first_name, :last_name, :is_female, :date_of_birth, :email, :password, :password_confirmation, :current_password)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :registrations => 'users/registrations', :profiles => 'users/profiles' }
devise_scope :user do
get 'profiles/edit' => 'users/profiles#edit'
post 'profiles/edit' => 'users/profiles#edit'
get 'users/:id' => 'users/registrations#show', as: :user
end
root to: "home#index"
end
Error after clicking update:
Routing Error
No route matches [PATCH] "/profiles/edit.21"
I appreciate any assistance, and please let me know if any further information is needed.
Try:
patch 'profiles/:id' => 'users/profiles#update'
and form helper should like this:
<%= form_for(#profile, html: { :multipart => true }) do |f| %>
Hope it is helpful for you!!
try adding this route to devise_scope to pass that error
patch 'profiles/edit' => 'users/profiles#edit'
btw, in ProfilesController
creating new Profile should be in create action
in edit action, you should load existed profile and render edit view
Update:
form in edit view will submit data to action update, we need get profile again and update data for it
your routes is not correct, when edit a profile, we should submit to a url look like users/profiles/4/update
you can declare routes as below
devise_for :users, :controllers => { :registrations => 'users/registrations' }
namespace :users do
resources :profiles
end
In update action, you retrieve that profile p = Profile.find(params[:id]) and update it p.photo = params[:photo]; p.save or anything you want
I'm super new to Ruby on Rails. I'm trying to make an authentication system using Authlogic (following this tutorial). The error that I'm getting is right after I submit the login form:
No route matches "/user_sessions/%23%3CUserSession:0x103486aa8%3E"
Surprisingly the URL of the page right after the form is submitted which also brings up the error is:
http://localhost:3000/user_sessions/%23%3CUserSession:0x103486aa8%3E
I have no idea what I have done wrong and where that weird UserSession code thing is coming from!!!
This is how my login form looks like:
<% form_for #user_session do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :username %><br />
<%= f.text_field :username%>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
Here is my UserSession class:
class UserSession < Authlogic::Session::Base
def to_key
new_record? ? nil : [ self.send(self.class.primary_key) ]
end
end
and the create action of my UserSessionController:
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
redirect_back_or_default root_path
else
render :action => :new
end
end
"redirect_back_or_default" method in ApplicationController:
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
And lastly everything related to user_sessions in routes.rb:
resources :user_sessions
match 'login' => "user_sessions#destroy", :as => :login
match 'logout' => "user_sessions#destroy", :as => :logout
These are the codes that I thought could be involved in getting that error. If I should add some more code to make it more clear please let me know.
Ok, first, you have a bad route:
match '/login', :to => 'user_sessions#new', :as => 'login'
note the new instead of destroy
also, the to_key is not needed in later versions - I'm using rails 3 and don't have it in my UserSession Model.
Definitely need to change your route to not match login to destroy.
Here's the route setting I have... (from "Agile Web Development with Rails" example).
controller :user_sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
I am new with rails and i am using "devise" gem for authentication purposes.
At first i add a new user through default sign up page (E.g./users/sign_up)
Then, i made "sign_up" page only available to signed_in users by following instructions from
Devise before filter that prevents access to "new_user_registration_path" unless user is signed-in
Now, after sign in process when i try open sign up page it always directs me to root_path! How can i access sign up page?
My "roots.rb" file as follows:
Example::Application.routes.draw do
devise_for :users, :controllers => { :registrations => 'registrations'}
resources :companies
resources :orders
resources :customers
root :to => "welcome#index"
end
Thank you all!
I have other decision. Bitterzoet said
As you can see in the devise source if you navigate to the sign_up it executes the before_filter require_no_authentication and this redirects to the root path which you can find here.
You don't need override registration_controller, you can change only your custom registration_controller that echo original registration_controller.
class Admin::RegistrationsController < Devise::RegistrationsController
layout 'admin'
prepend_before_filter :require_no_authentication, :only => []
prepend_before_filter :authenticate_scope!
end
If you are getting redirected it probably means you are not properly authenticated when you navigate to that page, seeing as it requires a valid user session.
Please post your Registrations controller file as well.
If you are getting redirected it probably means you are not properly authenticated when you navigate to that page, seeing as it requires a valid user session.
Please post your Registrations controller file as well.
Addition:
As you can see in the devise source if you navigate to the sign_up it executes the before_filter require_no_authentication and this redirects to the root path which you can find here.
I think you will have to explicitly override the registrations_controller that I linked first if you really want to override this behaviour :-)
The way I handled the scenario of being able to create New Users if you are signed in was by generating a User controller and having new and create action methods that would save to the User model. (This was stripped from a current app I'm working on so hopefully I didn't miss anything)
user_controller.rb
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully created User."
redirect_to root_path
else
render :action => 'new'
end
end
views/user/new.html.erb
<%= form_for #user, :url => user_index_path do |f| %>
<p><%= f.label :email %>
<%= f.text_field :email %></p>
<p><%= f.label :password %>
<%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %></p>
<p><%= f.submit "Save" %></p>
<% end %>
config/routes.rb (Rails 3)
resources :user, :controller => "user"
Link to the New User page
<%= link_to 'New User', new_user_path %>