STI routing error - ruby-on-rails-3

Newbie working on his first rails application after studying Hartl's Rails Tutorial book & video cast.
I'm using a STI model where:
class User < ActiveRecord::Base
class Kid < User
class Parent < User
User has the basic elements: name, email, etc.
The problem I'm having is with routing. I continue to work in determining which model will ultimately work best in this situation (STI or polymorphic). I've started with STI and I think I can get this to work if I can nail down the routing issues.
My problem is my edit is looking for an "update" action in the users controller when I want it to route to kidupdate I've been reading many SO posts about STI routing but I can't seem to figure out why this won't route correctly.
Rspec test. It the error comes from the "click_button"
describe "with valid information" do
let(:new_first) { "New First" }
let(:new_last) { "New Last" }
let(:new_email) { "new#example.com" }
before do
fill_in "First Name", with: new_first
fill_in "Last Name", with: new_last
fill_in "Email", with: new_email
select "Kid", from: "Are you a Kid or Parent"
fill_in "Password", with: kid.password
fill_in "Confirmation", with: kid.password
click_button "Save changes"
end
Rspec error:
KidPages edit with valid information
Failure/Error: click_button "Save changes"
AbstractController::ActionNotFound:
The action 'update' could not be found for UsersController
# (eval):2:in `click_button'
# ./spec/requests/kids_pages_spec.rb:32:in `block (4 levels) in <top (required)>'
Routes:
root / static_pages#home
help /help(.:format) static_pages#help
contact /contact(.:format) static_pages#contact
signup /signup(.:format) users#new
signin /signin(.:format) sessions#new
signout DELETE /signout(.:format) sessions#destroy
kidshow /kids/:id(.:format) users#kidshow
kidupate PUT /kids/:id(.:format) users#kidupdate
kidedit /kids/:id/edit(.:format) users#kidedit
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
routes.rb
root to: 'static_pages#home'
match '/help', to: 'static_pages#help'
match '/contact', to: 'static_pages#contact'
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match 'kids/:id', to: 'users#kidshow', :as => 'kidshow'
match 'kids/:id', to: 'users#kidupdate', :via => 'put', :as => 'kidupdate'
match 'kids/:id/edit', to: 'users#kidedit', :as => 'kidedit'
resources :users
resources :sessions, only: [:new, :create, :destroy]
I've been struggling with these concepts and this problem for weeks and I appreciate the help.

You can organize your controller better to avoid this issue altogether. See below
routes.rb
resources :kids
resources :parents
This would directly give you named path:
edit_kid_path(kid_id)
edit_parent_path(parent_id)
kids_controller.rb
class KidsController < ApplicationController
def update
end
end
parents_controller.rb
class ParentsController < ApplicationController
def update
end
end
If you want to share controller behavior, You can potentially do
class KidsController < UsersController
end
class ParentsController < UsersController
end
and put the common actions in users_controller.rb and override them in the child controllers.
The decision on what model relationships to use should be independent of how you structure your controllers. Controllers and routing is one area of consideration. Modeling data is entirely separately consideration. And declaring resources are just shortcuts for some matching routes.
If you are not going to use users#update etc, declaring resources :users is unnecessary as you are not using any of the routes that declaration provides.

Related

Devise custom user root is not working

After a user signs in, I want them to be redirected to /users/2 or users/44, like a normal resource. (That way an admin can easily view any user's profile page just by knowing their id.)
I have no idea what is going on in my routes file. When a user logs in, I can see in the logs that it authenticates properly, and that my application_controller's after_sign_in_path_for is called.
But this is the error that I am getting when I try to redirect_to user_path(current_user).
No route matches {:action=>"show", :controller=>"users", :user_id=>11}
My config/routes.rb
devise_for :users, :path_names => {:sign_in => 'signin', :sign_out => 'signout', :sign_up => 'signup' }
devise_scope :user do
get "signin", :to => "devise/sessions#new"
get "signout", :to => "devise/sessions#destroy", via: :delete
get "signup", :to => "devise/registrations#new"
end
resources :users do
member do
get 'settings', to: 'users#edit'
end
end
My application_controller.rb
class ApplicationController < ActionController::Base
...
def after_sign_in_path_for(resource)
redirect_to user_path(:user_id=>current_user.id)
end
end
Shouldn't the resource :users block take care of creating a user show path? I do have a show method in my users_controller.rb. Please help, I've been trying to get this working for 2 days now.
My problem was that I was redirecting in the after_sign_in_path(resource) method instead of just returning the path:
def after_sign_in_path_for(resource)
return user_path(:user_id=>current_user.id)
end
is the correct way to do this.

rails3: action being routed to the wrong controller. Possible bug?

I have two models (user & profile) with a 1-to-1 association.
class Profile < ActiveRecord::Base
attr_accessible :language_tokens, :tele, :languages, :serviceDesc, :service
belongs_to :user
...
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
The profile is built using:
#profile = #user.build_profile(session[:form_params])
my profile edit form is just a standard form which should be routed to my profile update action upon submission.
<%= form_for #profile do |f| %>
<%= render 'shared/host_information', :f => f%>
<%= render 'shared/service_information', :f => f%>
<%= f.submit 'done', class: "bnt"%>
<% end %>
HTML form:
<form id="edit_profile_6" class="edit_profile" method="post" action="/profiles/6" accept-charset="UTF-8">
However, for some bizarre reason, the form is being submitted to user update instead of the expected profile update. I put a debug point in user update and you can see that the params confirm that the target controller should indeed be profiles_controller.
/railsapp/loco_app/app/controllers/users_controller.rb:82
#user = User.find_by_id(params[:id])
(rdb:15) p params
{"utf8"=>"✓", "_method"=>"put", "authenticity_token"=>"qXx5EWwIWASiARKVFE7eDd43gHnBSmK538YAXeesDyI=", "profile"=>{"country_id"=>"8", "city_id"=>"22", "tele"=>"", "language_tokens"=>"18,14", "service"=>"9", },
"commit"=>"done", "action"=>"update", "controller"=>"profiles", "id"=>"6"}
(rdb:15)
here are the relevant routes:
profiles POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile PUT /profiles/:id(.:format) profiles#update
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
What's the best way to debug this problem? Is this some weird side-effect of a 1-to-1 association or is there a bug here? I'm using rails 3.2.2.
*UPDATE*
So this definitely looks like a bug. I regenerated my controller (same name) and copied over the same code and now it's working without any other changes. I'm still interested in knowing whether this is a known issue and how to debug problems like this in the future.

Rails 3 nested resources short name?

I'm in the process of upgrading a Rails 2.3 app to Rails 3. In the Rails 2.3 router, it was possible to set a :name_prefix of nil on nested resources to get a shorter name. The actual URL would still be fully qualified, but the code could use a shorter name. E.g.,:
map.resources :sites do |site|
site.resources :groups, :as => :groups, :controller => :url_groups, :name_prefix => nil, :member => { :clone => :post } do |group|
group.resources :tests, :as => :tests, :controller => :test_runs, :name_prefix => nil, :collection => { :latest => :get }
end
end
would allow one to use latest_tests_path. I can't figure out how to do the same thing with Rails 3, so I'm stuck with latest_site_group_tests_path. If that's the way it needs to be, I can just go through the code and change every instance of it. But I wanted to make sure I didn't miss anything first. And for better or worse, I do need to maintain the URL structure, so shallow routes don't seem to be the answer.
Good news is that Rails 3 still has the ability to setup arbitrary/abbreviated url helpers. Rather than a parameter to the resources method, you can create short-hand url helpers with the match declaration in routes.rb.
Say we have routes setup like this (noting that you need to maintain the 3 levels of nesting):
resources :sites do
resources :groups, :controller => :url_groups do
member do
post :clone
end
resources :test_runs do
collection do
get :latest
end
end
end
end
We get all the standard url helpers (rake routes):
clone_site_group POST /sites/:site_id/groups/:id/clone(.:format) {:action=>"clone", :controller=>"url_groups"}
latest_site_group_test_runs GET /sites/:site_id/groups/:group_id/test_runs/latest(.:format) {:action=>"latest", :controller=>"test_runs"}
site_group_test_runs GET /sites/:site_id/groups/:group_id/test_runs(.:format) {:action=>"index", :controller=>"test_runs"}
(etc)
But to create something shorter than latest_site_group_test_runs_path(site,group), add a match declaration to routes.rb like this:
match 'sites/:site_id/groups/:id/test_runs/latest' => 'test_runs#latest', :as => :latest_tests
Now you can use latest_tests_path(site,group) or latest_tests_url(site,group) to generate the fully nested path.
If your aim is brevity, you could also use implicit polymorphic paths (as long as you have all your models aligned with resource paths).
For example, given #site #1 and #group #1, all of the following will now generate the same path '/sites/1/groups/1/test_runs/latest':
= link_to "latest tests", latest_site_group_test_runs_path(#site,#group) # std helper
= link_to "latest tests", latest_tests_path(#site,#group) # match helper
= link_to "latest tests", [:latest,#site,#group,:test_runs] # implicit polymorphic path
Hope that helps! Seems like you should be able to get the flexibility you need for the app migration.
NB: I glossed over the lurking issue of having a model called "Test" since that's off topic;-) There are a few model names that are a neverending source of pain because of namespace and keyword conflicts. My other favourite is when I really wanted to have a mode called "Case" (since that matched the problem domain best. Bad idea, rapidly reversed!)
There's the :shallow option (see the documentation), but I'm not sure it fits your use case:
resources :sites, :shallow => true
resources :groups do
resources :tests
end
end
It has the disadvantage of creating a bunch of additional routes:
group_tests GET /groups/:group_id/tests(.:format) {:action=>"index", :controller=>"tests"}
POST /groups/:group_id/tests(.:format) {:action=>"create", :controller=>"tests"}
new_group_test GET /groups/:group_id/tests/creer(.:format) {:action=>"new", :controller=>"tests"}
edit_test GET /tests/:id/modifier(.:format) {:action=>"edit", :controller=>"tests"}
test GET /tests/:id(.:format) {:action=>"show", :controller=>"tests"}
PUT /tests/:id(.:format) {:action=>"update", :controller=>"tests"}
DELETE /tests/:id(.:format) {:action=>"destroy", :controller=>"tests"}
site_groups GET /sites/:site_id/groups(.:format) {:action=>"index", :controller=>"groups"}
POST /sites/:site_id/groups(.:format) {:action=>"create", :controller=>"groups"}
new_site_group GET /sites/:site_id/groups/creer(.:format) {:action=>"new", :controller=>"groups"}
edit_group GET /groups/:id/modifier(.:format) {:action=>"edit", :controller=>"groups"}
group GET /groups/:id(.:format) {:action=>"show", :controller=>"groups"}
PUT /groups/:id(.:format) {:action=>"update", :controller=>"groups"}
DELETE /groups/:id(.:format) {:action=>"destroy", :controller=>"groups"}
sites GET /sites(.:format) {:action=>"index", :controller=>"sites"}
POST /sites(.:format) {:action=>"create", :controller=>"sites"}
new_site GET /sites/creer(.:format) {:action=>"new", :controller=>"sites"}
edit_site GET /sites/:id/modifier(.:format) {:action=>"edit", :controller=>"sites"}
site GET /sites/:id(.:format) {:action=>"show", :controller=>"sites"}
PUT /sites/:id(.:format) {:action=>"update", :controller=>"sites"}
DELETE /sites/:id(.:format) {:action=>"destroy", :controller=>"sites"}
Besides, the shallow nesting only applies to the following routes: :show, :edit, :update, :destroy.
I wanted something like this too, clearly it feels redundant to type the prefixes in named routes all the time for resources that only exist as nested. I was able to get what I wanted using scope, I think this is what you sought too:
resources :sites
scope :path => '/sites/:site_id' do
resources :groups, :controller => :url_groups do
post :clone, :on => :member
end
end
scope :path => '/sites/:site_id/groups/:group_id' do
resources :tests, :controller => :test_runs do
get :latest, :on => :collection
end
end
The rake routes output:
sites GET /sites(.:format) sites#index
POST /sites(.:format) sites#create
new_site GET /sites/new(.:format) sites#new
edit_site GET /sites/:id/edit(.:format) sites#edit
site GET /sites/:id(.:format) sites#show
PUT /sites/:id(.:format) sites#update
DELETE /sites/:id(.:format) sites#destroy
clone_group POST /sites/:site_id/groups/:id/clone(.:format) url_groups#clone
groups GET /sites/:site_id/groups(.:format) url_groups#index
POST /sites/:site_id/groups(.:format) url_groups#create
new_group GET /sites/:site_id/groups/new(.:format) url_groups#new
edit_group GET /sites/:site_id/groups/:id/edit(.:format) url_groups#edit
group GET /sites/:site_id/groups/:id(.:format) url_groups#show
PUT /sites/:site_id/groups/:id(.:format) url_groups#update
DELETE /sites/:site_id/groups/:id(.:format) url_groups#destroy
latest_tests GET /sites/:site_id/groups/:group_id/tests/latest(.:format) test_runs#latest
tests GET /sites/:site_id/groups/:group_id/tests(.:format) test_runs#index
POST /sites/:site_id/groups/:group_id/tests(.:format) test_runs#create
new_test GET /sites/:site_id/groups/:group_id/tests/new(.:format) test_runs#new
edit_test GET /sites/:site_id/groups/:group_id/tests/:id/edit(.:format) test_runs#edit
test GET /sites/:site_id/groups/:group_id/tests/:id(.:format) test_runs#show
PUT /sites/:site_id/groups/:group_id/tests/:id(.:format) test_runs#update
DELETE /sites/:site_id/groups/:group_id/tests/:id(.:format) test_runs#destroy
Update: Duh, I left the static segments of the scope paths out of my example. Fixed.

How do I remove the Devise route to sign up?

I'm using Devise in a Rails 3 app, but in this case, a user must be created by an existing user, who determines what permissions he/she will have.
Because of this, I want:
To remove the route for users to sign up.
To still allow users to edit their profiles (change email address and password) after they have signed up
How can I do this?
Currently, I'm effectively removing this route by placing the following before devise_for :users:
match 'users/sign_up' => redirect('/404.html')
That works, but I imagine there's a better way, right?
Update
As Benoit Garret said, the best solution in my case is to skip creating the registrations routes en masse and just create the ones I actually want.
To do that, I first ran rake routes, then used the output to re-create the ones I wanted. The end result was this:
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
Note that:
I still have :registerable in my User model
devise/registrations handles updating email and password
Updating other user attributes - permissions, etc - is handled by a different controller
Actual answer:
Remove the route for the default Devise paths; i.e.:
devise_for :users, path_names: {
sign_up: ''
}
you can do this in your model
# typical devise setup in User.rb
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
change it to:
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable
notice that the symbol :registerable was removed
That's it, nothing else is required. All routes and links to registration page are magically removed too.
I tried to do this as well, but a thread on the devise google group dissuaded me from searching for a really clean solution.
I'll quote José Valim (the Devise maintainer) :
There isn't a straight-forward option. You can either provide a patch
or use :skip => :registerable and add only the routes you want.
The original question was :
Is there any good way to remove a specific route (the delete route)
from Rails?
I had similar issue tried to remove devise_invitable paths for create and new :
before:
devise_for :users
rake routes
accept_user_invitation GET /users/invitation/accept(.:format) devise/invitations#edit
user_invitation POST /users/invitation(.:format) devise/invitations#create
new_user_invitation GET /users/invitation/new(.:format) devise/invitations#new
PUT /users/invitation(.:format) devise/invitations#update
after
devise_for :users , :skip => 'invitation'
devise_scope :user do
get "/users/invitation/accept", :to => "devise/invitations#edit", :as => 'accept_user_invitation'
put "/users/invitation", :to => "devise/invitations#update", :as => nil
end
rake routes
accept_user_invitation GET /users/invitation/accept(.:format) devise/invitations#edit
PUT /users/invitation(.:format) devise/invitations#update
note 1 devise scope https://github.com/plataformatec/devise#configuring-routes
note 2 I'm applying it on devise_invitable but it will work with any devise *able feature
Important note: see that devise_scope is on user not users ? that's correct, watch out for this ! It can cause lot of pain giving you this problem:
Started GET "/users/invitation/accept?invitation_token=xxxxxxx" for 127.0.0.1
Processing by Devise::InvitationsController#edit as HTML
Parameters: {"invitation_token"=>"6Fy5CgFHtjWfjsCyr3hG"}
[Devise] Could not find devise mapping for path "/users/invitation/accept? invitation_token=6Fy5CgFHtjWfjsCyr3hG".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
match "/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]
I found another post similar to this one and wanted to share an answer #chrisnicola gave. In the post they were attempting to only block user signup's during production.
You could also modify the registrations controller. You can use something like this:
In "app/controllers/registrations_controller.rb"
class RegistrationsController < Devise::RegistrationsController
def new
flash[:info] = 'Registrations are not open.'
redirect_to root_path
end
def create
flash[:info] = 'Registrations are not open.'
redirect_to root_path
end
end
This will override devise's controller and use the above methods instead. They added flash messages incase that someone somehow made it to the sign_up page. You should also be able to change the redirect to any path you like.
Also in "config/routes.rb" you can add this:
devise_for :users, :controllers => { :registrations => "registrations" }
Leaving it like this will allow you to use the standard devise edit your profile. If you wish you can still override the edit profile option by including
def update
end
in the "app/controllers/registrations_controller.rb"
This is an old question - but I recently had solve the same issue and came up with a solution which is far more elegant than:
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
And it gives the default names for the named routes (like cancel_user_registration) without being excessively verbose.
devise_for :users, skip: [:registrations]
# Recreates the Devise registrations routes
# They act on a singular user (the signed in user)
# Add the actions you want in 'only:'
resource :users,
only: [:edit, :update, :destroy],
controller: 'devise/registrations',
as: :user_registration do
get 'cancel'
end
rake routes output with the default devise modules:
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
user_registration PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
You can override the "devise_scope" by placing it before the "devise_for".
devise_scope :user do
get "/users/sign_up", :to => "sites#index"
end
devise_for :users
Not sure if this is the best way but its my solution currently, as it just redirects back to the sign in page.
I liked #max's answer, but when trying to use it I ran into an error due to devise_mapping being nil.
I modified his solution slightly to one that seems to address the issue. It required wrapping the call to resource inside devise_scope.
devise_for :users, skip: [:registrations]
devise_scope :user do
resource :users,
only: [:edit, :update, :destroy],
controller: 'devise/registrations',
as: :user_registration do
get 'cancel'
end
end
Note that devise_scope expects the singular :user whereas resource expects the plural :users.
Do This in routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
devise_scope :user do
get "/sign_in", :to => "devise/sessions#new"
get "/sign_up", :to => "devise/registrations#new"
end
you will get an error now while you come to sign in page, to fix it.
Do this change in: app/views/devise/shared/_links.erb
<% if request.path != "/sign_in" %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>
<% end %>
I've found this to work well without messing with routes or adding application controller methods. My approach is to override the devise method. Add this to app/controllers/devise/registrations_controller.rb
I've omitted the other methods for brevity.
class Devise::RegistrationsController < DeviseController
...
# GET /resource/sign_up
def new
redirect_to root_path
end
....
end
Also to remove illusion that this path is still reachable from other views you might also want to remove this code from app/views/devise/shared/_links.erb
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>
For others in my case.
With devise (3.5.2).
I successfully removed the routes to signup, but kept the ones to edit the profile, with the following code.
#routes.rb
devise_for :users, skip: [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put '/users(.:format)' => 'devise/registrations#update', as: 'user_registration'
patch '/users(.:format)' => 'devise/registrations#update'
end
Here's the slightly different route I went. It makes it so you don't have to override the devise/shared/_links.html.erb view.
In app/models/user.rb:
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable
In config/routes.rb:
devise_for :users
devise_scope :user do
put 'users' => 'devise/registrations#update', as: 'user_registration'
get 'users/edit' => 'devise/registrations#edit', as: 'edit_user_registration'
delete 'users' => 'devise/registrations#destroy', as: 'registration'
end
Before:
$ rake routes | grep devise
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
user_registration POST /users(.:format) devise/registrations#create
new_user_registration GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
After:
$ rake routes | grep devise
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
user_registration PUT /users(.:format) devise/registrations#update
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
registration DELETE /users(.:format) devise/registrations#destroy
Instead of searching for a hard solution. I used the below approaches.
Delete the sign_up form from page (path devise/registrations/new.html.erb) and replace it with custom info.
Redirect the incoming traffic to some other page. Like below in routes.rb
get "/users/sign_up", to: redirect('/')
post "/users/sign_up", to: redirect('/')
Make sure to write it before devise_for :users
I had the same issue and I found it a bit bad practise to redirect users from the registration page. So my solution is basically is not using :registrable at all.
What I did was to create a similar page like edit user details which looked like:
<%= form_tag(update_user_update_path, method: :post) do %>  
<br>
<%= label_tag(:currPassword, 'Current password:') %> <%= password_field_tag(:currPassword) %> <br>
<%= label_tag(:newPassword, 'New password:') %> <%= password_field_tag(:newPassword) %> <br>
<%= label_tag(:newPasswordConfirm, 'Confirm new password:') %> <%= password_field_tag(:newPasswordConfirm) %> <br>
<%= submit_tag('Update') %>
<% end %>
So this form submits into a new post end point that updates the password, which looks like:
def update
currPass = params['currPassword']
newPass1 = params['newPassword']
newPass2 = params['newPasswordConfirm']
currentUserParams = Hash.new()
currentUserParams[:current_password] = currPass
currentUserParams[:password] = newPass1
currentUserParams[:password_confirmation] = newPass2
#result = current_user.update_with_password(currentUserParams)
end
Later on you can use the #result in your view to tell the user whether the password is updated or not.
By changing the routes there are a whole bunch of other problems that come with that. The easiest method I have found is to do the following.
ApplicationController < ActionController::Base
before_action :dont_allow_user_self_registration
private
def dont_allow_user_self_registration
if ['devise/registrations','devise_invitable/registrations'].include?(params[:controller]) && ['new','create'].include?(params[:action])
redirect_to root_path
end
end
end
You could modify the devise gem itself. First, run this command to find the installed location of using:
gem which devise
Let's suppose the path is:
/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise
Then go to
/usr/local/lib/ruby/gems/1.9.1/gems/devise-1.4.2/lib/devise/lib/devise/rails and edit routes.rb in that directory. There is a method called def devise_registration(mapping, controllers) which you can modify to get rid of the new action. You can also completely remove the mappings for devise_registration

update instead of show being called in REST rails form submission

I have a form that a user submits. The form is for a user. I have the following in my routes.rb file:
resources :users, :except => [:new]
root :to => 'pages#home'
match '/contact', :to => 'pages#contact'
match '/about', :to => 'pages#about'
match '/help', :to => 'pages#help'
In the users_controller.rb:
def create
#title = "Home"
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to user_path(#user), :flash => {:success => "Your friend's details have been saved.The surprise has begin"} }
format.js
else
format.html { render :template => 'pages/home'}
format.js
end
end
end
This form is being submitted with a regular POST request and not Ajax.
I want to know why the Update template is being requested after the form is successfully submitted instead of the Show.
The output from rake routes is as follows:
users GET /users(.:format) {:action=>"index", :controller=>"users"}
POST /users(.:format) {:action=>"create", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
root /(.:format) {:controller=>"pages", :action=>"home"}
contact /contact(.:format) {:controller=>"pages", :action=>"contact"}
about /about(.:format) {:controller=>"pages", :action=>"about"}
help /help(.:format) {:controller=>"pages", :action=>"help"}
Thanks in advance.
What is the code for the controller and view of this form? Is there an existing user object when you generate the form or do you create a new user object (ie. #user = User.new in your controller)? If there is an existing user then the form will submit to update.
Your route and controller code look good, i.e. it should be sending you to the show method in your users_controller. I'd put some debbugging code in there, i.e. logger.debug("Inside Show") to see if the show method was being called.
You could also include the output of rake routes in your edited question, it might be informative to show where the user_path helper is going to send the redirect.
ian.