Devise - Allow both individual and admin user creation - ruby-on-rails

I have seen the few links on here regarding this topic but as yet have been unable to crack the problem. I want my application to allow users to register but also to register other users. So far my code lucks like so (though at this point I'm not sure I'm even on the right path and have probably included stuff which is not required just to make it work):
routes.rb
resources :devise
devise_for :users, path: 'devise'
devise_scope :user do
get 'users/registrations/admin_new' => 'devise/registrations#admin_new'
post 'users/registrations/admin_create' => 'devise/registrations#admin_create'
end
These are the two methods I have referenced in my route.
controllers/user/registration_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def admin_new
puts "---------------"
#user = User.new
end
def admin_create
puts "---------------"
end
end
My view is as follows but shortened so as not to include all the fields:
views/devise/registrations/admin_new.html.haml
= simple_form_for(User.new, url: users_registrations_admin_create_path(User.new)) do |f|
...
This set up has gotten me to the point that the url:
http://localhost:3000/users/registrations/admin_new
loads the form alright however the puts I inserted does not appear in my console when it loads which is strange. Also i previously had the form as:
= simple_form_for(#user, url: users_registrations_admin_create_path(#user)) do |f|
...
however this resulted in the error:
undefined method `model_name' for nil:NilClass
which is why I changed to User.new instead of #user. When i submit the form it returns the error:
The action 'admin_create' could not be found for Devise::RegistrationsController
I'm at a loss as to why this is. I'm also unsure as to whether the structure of my devise is correct given the controllers are contained within a users folder while the views are contained with a devise folder. Any help on this would be greatly appreciated.

your route is problem.
post 'users/registrations/admin_create' => 'devise/registrations#admin_create'
which means this route to admin_create action in Devise::RegistrationsController
so router find a action in Devise::RegistrationsController but action "admin_create" exists in Users::RegistrationsController,
change
'devise/registrations#admin_create'
=> 'user/registrations#admin_create'
update1
I check your route out.
assigned route path to controller in 'controllers/devise/registrations_controller.rb'
post 'users/registrations/admin_create' => 'devise/registrations#admin_create'
PREFIX users_registrations_admin_create
URL PATTERN /users/registrations/admin_create(.:format)
Controller&Action devise/registrations#admin_create
assigned route path to controller in 'controllers/user/registrations_controller.rb'
post 'users/registrations/admin_create' => 'user/registrations#admin_create'
PREFIX users_registrations_admin_create
URL PATTERN /users/registrations/admin_create(.:format)
Controller&Action user/registrations#admin_create
only 'devise' controller name changed to 'user' in route, then fine.

Related

Rails use nesting and resource path with other model

I have such method controller:
class Admin::CarManufacturersController < ApplicationController
def edit
#man = Manufacturer.find(params[:id])
render :layout => 'admin'
end
def update
#man = Manufacturer.find(params[:id])
if #man.update_attributes(params[:car_manufacturer])
****
else
render :action => :edit, :layout => 'admin'
end
end
end
and i have such route:
namespace :admin do
resources :car_manufacturers do
###
end
end
and such form partial:
= form_for [:admin, #man] do |f|
###
but when i call this form to edit my data i get:
undefined method `admin_manufacturer_path'
but i need admin_car_manufacturer_path i thing it's becouse i use other model name in controller, but i can't change it... how can i use right pass? i try to write admin_car_manufacturer_path in form, but i think this is bad idea. How to solve my problem?
I would think about renaming your controller/your model to match. Both should either be just manufacturer or car manufacturer. Having the same names for a resource's controller and model will spare you problems like the one you're having right now.
In any case, if you just need a quick fix, you can get around this by specifying the as option for your nested routes like this:
namespace :admin do
resources :manufacturers, as: :car_manufacturers do
###
end
end
Source: Rails Routing from the Outside In - Ruby on Rails Guides - 3.6: Naming Routes
That will turn your path names into admin_car_manufacturer_path etc and should allow you to use your form the way you you intended to. But I really recommend renaming your model and controller so that they match.

Rails Routes based on condition

I have three roles: Instuctor, Student, Admin and each have controllers with a "home" view.
so this works fine,
get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"
I want to write a vanity url like below which will route based on the role of the user_id to the correct home page.
get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"
How do I accomplish this?
I'm providing an alternate approach as this SO question comes up near the top when searching for role based routing in Rails.
I recently needed to implement something similar but wanted to avoid having a large number of conditionals in the controller - this was compounded by the fact that each of my user roles required completely different data to be loaded and presented. I opted to move the deciding logic to the routing layer by using a Routing Constraint.
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
#block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && #block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
The most important part of the above code is the matches? method which will determine whether or not the route will match. The method is passed the request object which contains various information about the request being made. In my case, I'm looking up the :user_id stored in the session cookie and using that to find the user making the request.
You can then use this constraint when defining your routes.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
With the above in place, an administrator making a request to /home would be routed the home action of the AdministratorsController, an instructor making a request to /home would be routed to the home action of the InstructorsController, and a student making a request to /home would be routed to the home action of the StudentsController.
More Information
If you're looking for more information, I recently wrote about this approach on my blog.
You can't do this with routes because the routing system does not have the information required to make this decision. All Rails knows at this point of the request is what the parameters are and does not have access to anything in the database.
What you need is a controller method that can load whatever data is required, presumably the user record, and redirects accordingly using redirect_to.
This is a fairly standard thing to do.
Update:
To perform all of this within a single controller action you will need to split up your logic according to role. An example is:
class HomeController < ApplicationController
def home
case
when #user.student?
student_home
when #user.admin?
admin_home
when #user.instructor
instructor_home
else
# Unknown user type? Render error or use a default.
end
end
protected
def instructor_home
# ...
render(:template => 'instructor_home')
end
def student_home
# ...
render(:template => 'student_home')
end
def admin_home
# ...
render(:template => 'admin_home')
end
end

Rails: Route to custom controller action

I have a really hard time understanding routes and I hope someone can help me.
Here's my custom controller
class SettingsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = current_user
end
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
and I have the file settings/edit.html.erb and my link
<li><%= link_to('Settings', edit_settings_path) %></li>
The route
get "settings/edit"
doesn't work for this, because then I get
undefined local variable or method `edit_settings_path' for #<#<Class:0x00000001814ad8>:0x00000002b40a80>
What route do I have to give this? I can't figure it out. If I put "/settings/edit" instead of a path it messes up as soon as I'm on a other resource page because the resource name is put BEFORE settings/edit
Thx
Following should do:
get 'settings/edit' => 'settings#edit', :as => :edit_settings
# you can change put to post as you see fit
put 'settings/edit' => 'settings#update'
If you use /settings/edit directly in link, you shouldn't have problem with other resource name being prepended in path. However, without the leading slash, i.e. settings/edit it might have that issue.
Reason why edit_settings_path is not working might be because you didn't declare a named route. You have to use :as option to define by which method you will be generating this path/url.
If you want to explicitly define the route, you would use something like
get 'settings/edit' => 'settings#edit', :as => edit_settings
This statement defines that when a GET request is received for setting/edit, call the SettingsController#edit method, and that views can reference this link using 'edit_settings_path'.
Take some time to read the Rails guide on routing. It explains routing better than any other reference out there.
Also keep in mind the rake routes task, that lists the details of all the routes defined in your application.

No Method error in UsersController

I began coding in Rails several weeks ago, and I can't figure out why I have this error. I'm using Devise for log-ins and Formtastic for forms. The app was working correctly until I added the Acts_like_tags_on and reset the database.
The error message:
NoMethodError in UsersController#show
undefined method `username' for nil:NilClass
app/controllers/users_controller.rb:19:in `show'
Request
Parameters:
{"id"=>"sign_in"}
This is what I have in the Users Controller:
def show
#user = User.find_by_username(params[:id])
#title = #user.username
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
Any input would be helpful. Thanks!
After editing #user = User.find_by_username(params[:id]) to:
#user = User.find_by_user(params[:id])
The error becomes:
undefined method `find_by_user' for #
The username column does exist in the User table.
#user = User.find_by_username(params[:id])
That line above may be returning nil. If you do User.find_by_username("Superman-is-awesome") and that username does not exist in your database, it's going to return nil.
Then it is trying to do:
#title = #user.username
Which is essentially:
#title = nil.username
Which of course won't work. So could be something wrong with the parameter you are passing in.
Also, make sure your User table have a column called 'username'? Make sure you've run:
rake db:migrate
As well.
If you configured the routes correctly, you should have devise routes BEFORE user resource, like this:
devise_for :users
resources :users, except: "create"
This is actually a routing problem
The problem is that devise expects you to have a route that will turn:
"/users/sign_in" into sessions#new
but your routes file is missing that route, and so the dispatcher is matching against the:
"users/:id" route which then goes to:
users#show with :id => 'sign_in'
(and hence throws an error when it tries to find a user with the id of "sign_in")
You need to read the README doc for devise (google if you don't have it locally) - especially the part that describes how to add the standard set of routes to config/routes.rb Then do whatever it says. :)
Should be right after that.
I found that the user_id was being given the value of 'users'. After commenting the following line out in my routes.rb, the id was no longer given that value.
match '/:id' => 'users#show', :as => :user
I also needed the users_controllers #show to have the following line, since my user path uses the username. removing '_by_username' caused an error on pages that called for the username:
#user = User.find_by_username(params[:id])

Rails route dependent on current user

I'd like to create a rails route for editing a user's profile.
Instead of having to use /users/:id/edit, I'd like to have a url like /edit_profile
Is it possible to create a dynamic route that turns /edit_profile into /users/{user's id}/edit, or should I do thing in a controller or?
You might want to create a separate controller for this task but you could also continue using users_controller and just check whether there is a params[:id] set:
def edit
if params[:id]
#user = User.find(params[:id])
else
#user = current_user
end
end
But you should note that /users normally routes to the index action and not show if you still have the map.resources :users route. But you could set up a differently called singular route for that:
map.resources :users
map.resource :profile, :controller => "users"
This way /users would list all the users, /users/:id would show any user and /profile would show the show the currently logged in users page. To edit you own profile you would call '/profile/edit'.
Since a route and controller serve two different purposes, you will need both.
For the controller, assuming you're storing the user id in a session, you could just have your edit method do something like:
def edit
#user = User.find(session[:user_id])
end
Then have a route that looks something like:
map.edit_profile "edit_profile", :controller => "users", :action => "edit"
This route would give you a named route called edit_profile_path
Tomas Markauskas's answer could work, but here's the answer to your question from the Rails Guide:
get 'edit_profile', to: 'users#edit'
So, when someone goes to www.yoursite.com/edit_profile, it will route to www.yoursite.com/users/edit.
Then, in your controller you can access the user with
#user = User.find(session[:current_user_id])
Assuming you set that session variable when someone logs in. Also, don't forget to check if they're logged in. This will work if your using Resourceful Routing (the Rails default) or not.
Source: http://guides.rubyonrails.org/routing.html
make the route as
get '/users/:id/edit', to: 'users#edit', as: 'edit_profile'
As explained in this link section 'The hard way' :
http://augustl.com/blog/2009/styling_rails_urls/
The url will be
/users/edit_profile
Because the ID is no longer in the URL, we have to change the code a bit.
class User < ActiveRecord::Base
before_create :create_slug
def to_param
slug
end
def create_slug
self.slug = self.title.parameterize
end
end
When a user is created, the URL friendly version of the title is stored in the database, in the slug column.
For better understanding read the link below
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
write it in any home controler.
def set_roots
if current_user
redirect_to dashboard_home_index_path
else
redirect_to home_index_path
end
end
in routes.rb file
root :to => 'home#set_roots'
match "/find_roots" => "home#set_roots"

Resources