Rails Devise model won't use custom model controller? - ruby-on-rails

In a Rails (version 4.0.2) have a simple model I generated with the Devise gem (version 3.0.4) called User. I want to customize the controller. So I made a file "user_controller":
class UserController < Devise::SessionsController
def create
super
puts "this works"
end
end
Here are my routes:
SecretApp::Application.routes.draw do
root 'static_pages#home'
match '/about', to: 'static_pages#about', via: 'get'
match '/profile', :to => "profile#show", via:'get'
devise_for :users, :controllers => {:registrations => "registrations", :sessions => "user"}
end
I'm assuming when overriding a Devise controller, you have to call super in each method unless you are completely overriding the method, correct? In any case, when I create a new User, the console never prints "this works", so I'm pretty sure the controller is not being used. Why?
I did however manage to override my registrations controller for the User model, which confuses me because I'm using the same method to override the User controller (besides the inherited super class and the controller name). I'm sure of this because I have puts "this is ok" in the the new method and it does print that to the console.

I agree with Zach. A controller that inherits from Devise::SessionsController should definitely not be called UserController. At most, that controller should be inheriting from Devise::RegistrationsController. Also, Rails' conventions dictate that you name your controller in the plural form, UsersController. Regardless, this is why your code is not working:
When you call super, the create action inside Devise::SessionsController is reached. Notice how, in the last line of that action, the respond_with method is called. This method is the one responsible for rendering a template, generating a response. Once this line is reached, your code will not run.
You have thus 2 options:
Call your custom code before you call super:
def create
puts "this works"
super
end
Or take advantage of the fact that Devise's create action has this line:
yield resource if block_given?
You can then call your custom code like this:
def create
super do |resource|
puts "this works"
end
end
This is all mentioned in Devise's readme.
In the second option, Devise is using two awesome Ruby features: blocks, and yielding. You can learn more about blocks and yield with RubyMonk, I highly recommend it!

Related

Skip authentication for specific member action in ActiveAdmin

I'm trying to skip the authentication for a custom ActiveAdmin member action that I've created. Here's what I've been trying, but it still brings me to the login page.
ActiveAdmin.register Foo, as: "Foos" do
controller do
skip_before_action :authenticate_admin_user!, only: :bar
end
member_action :bar, method: :get do
# render something
end
end
Versions:
Rails: 4.1.1
ActiveAdmin: 1.0.0.pre1
Have a look at this gist.
Put the self.filters and self.before_filters methods into controller do block.
Add binding.pry after those methods and restart application server - this should stops on binding.
Type filters(:before) and you will see a list of callbacks.
Find the callback responsible for authentication, e.g. authenticate_active_admin_user.
Add skip_before_filter :authenticate_active_admin_user to controller do block.
Have a break :)

Devise Registration Form

I have two devise forms used in my Ruby on rails site...how can i set different route paths for both devise form..i tried to override the after_sign_up_path..but both the forms gets redirected to same path...
I want to set different paths for each form.
Registrations Controller
class Registrations Controller < Devise::Registrations Controller
protected
def after_sign_up_path_for(resource)
'root_path'
end
end
this method calls when signup is success, so set your after signed up path here
def after_sign_up_path_for(resource)
if resource.invitation_type == "first" (please replace with actual invitation type here)
user1_path(replace with actual path)
elsif resource.invitation_type == "second" (please replace with actual invitation type here)
user2_path(replace with actual path)
else
root_path
end
end
Hope this helps!
Inside the application controller, put the code for after_sign_up_path_for path
#put these code inside applications_controller
def after_sign_up_path_for
if #{your_specific_condition}
else
root_path
end
end
You must be aware that if you want to override your registrations controller, you'll have to include the new controller in your routes:
#config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}

When overriding the registration controller from devise, is it possible to access the newly created user on the create action?

I'm trying to create a folder right after a user registers, so I override the create action on the registration controller (devise) but I don't know how to access the newly created user in order to create the folder with it's name to upload files later.
So far I've got this:
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
create_folder
end
def update
super
end
def create_folder
path = Pathname.new(':rails_root/tmp/')
directory_name = ":current_user"
Dir.mkdir(path, directory_name) unless File.exists?(directory_name)
end
end
routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
I followed this to override the registration controller.
Should I leave it there or move it to the create action? Instead of using a method
is that the right way to access the current user?
Maybe instead of registration it's better to do it on sign in?
I'd appreciate any help I can get.
You should really be accomplishing this functionality in an after_filter to your action, rather than in the action itself:
class RegistrationsController < Devise::RegistrationsController
after_filter :create_folder, :only => :create
protected
def create_folder
path = Pathname.new(Rails.root.to_s + '/tmp/') #=> Note 1
directory_name = resource.id.to_s #=> Note 2
Dir.mkdir(path + directory_name) #=> Note 3
end
end
Notes:
Your syntax for retrieving the project root is incorrect. :rails_root can't be interpolated from within single quotes - and it doesn't exist, anyways. Try Rails.root.to_s instead.
":current_user" is simply a string that will attempt to name the directory :current_user, which is neither dynamic nor valid. Because your Devise controller has access the the current resource (which in this case, represents the current_user), retrieve the resource.id and use it instead.
Concatenate the path and directory_name together with +, not ,. There's no need for the unless conditional, since you're presumably only creating the new folder upon creation of a new user, and each user has a unique id.

Rails.application.routes.recognize_path with devise authenticated route

I have a devise enabled route as:
#config/routes.rb
authenticated :user {
root :to => 'home#signed_in'
}
root :to => 'home#index
and controller:
#app/controllers/home_controller.rb
class HomeController < ApplicationController
def signed_in
Rails.application.routes.recognize_path '/'
end
end
which raises:
NoMethodError: undefined method `authenticate?' for nil:NilClass
...lib/devise/rails/routes.rb:286:in `block in authenticated'
I need such thing to render different templates in destroy action based on request.referer controller name. How can get 'authenticated' controller/action name for such URL?
It seems using Rails.application.routes.recognize_path does not work well with custom routes constraints like devise #authenticated method.
See https://github.com/plataformatec/devise/issues/3747
recognize_path won't work as is with constraints that require request related objects (like the warden instance that Devise expects to be in the request env Hash). If you are doing this inside a controller code you might be able to pass the warden object from request.env to the recognize_path call, or try to pass down the object down the the class that is recognizing the path.
In the other hand, as recognize_path isn't a documented public API, I strongly suggest you to not use it and save pieces of the params Hash instead of the raw URL on your application.
One solution to your problem could be to save the controller/action names in session and access them when you need.
authenticated :user do
root :to => 'home#signed_in'
end
unauthenticated :user do
root :to => 'home#index'
end
You can use this: https://gist.github.com/NullVoxPopuli/8c8af217b7404336c72a
class RouteRecognizer
include Singleton
ROUTE_LIST = Rails.application.routes.routes.collect{|r| r.path.spec.to_s}
REGEX_ROUT_LIST = ROUTE_LIST.map{|r|
Regexp.new(r.gsub(/\:(.*)id/, "(\d+)").gsub("(.:format)", ""))
}
def self.is_route?(path)
REGEX_ROUT_LIST.each do |regex|
return true if !!(path =~ regex)
end
false
end
end
and then call with
RouteRecognizer.is_route?('http://whatever')

Devise gem in Rails: generate user_root_path

Trying to redirect users to their associated 'home' page after successful login w/out nil'ing out stored_location_for(resource_or_scope)...which is giving me some endless redirect loops (pretty sure I've set it up wrong).
Regardless, I'm looking for a better approach...
Devise's docs state: After
signing in a user, confirming the
account or updating the password,
Devise will look for a scoped root
path to redirect. Example: For a
:user resource, it will use
user_root_path if it exists,
otherwise default root_path will be
used. This means that you need to set
the root inside your routes: root :to => "home"
I'm sorta a newbie...how does one go about generating this home_root_path for each user?
rDocs also mention:
-- (Object) after_sign_in_path_for(resource_or_scope)
The default url to be used after
signing in. This is used by all Devise
controllers and you can overwrite it
in your ApplicationController to
provide a custom hook for a custom
resource.
By default, it first tries to find a resource_root_path, otherwise
it uses the root path. For a user
scope, you can define the default url
in the following way:
map.user_root '/users', :controller => 'users' # creates user_root_path
map.namespace :user do |user|
user.root :controller => 'users' # creates user_root_path
end
but these just gives me undefined local variable or methodmap' for #ActionDispatch::Routing::Mapper:…` errors.
If you would like to redirect using a route in answer to your question below:
how does one go about generating this home_root_path for each user?
This will work if you place it in your config/routes file. It will redirect a user to articles#index after (for example) a successful confirmation.
# Rails 4+
get 'user_root' => 'articles#index', as: :user_root
# Rails 3
match 'user_root' => 'articles#index', as: :user_root
See Devise: Controllers Filters and Helpers
You could try something like this:
application_controller.rb:
def after_sign_in_path_for(resource_or_scope)
# return home_page_path for user using current_user method
end
Dug around a bit to figure out the same thing. #polarblau's answer is correct,
def after_sign_in_path_for(resource_or_scope)
user_info_path(current_user)
end
where user_info_path is the path to the page you wish to display.
Also, I would allow this to fall back to super just in case, although I'm not entirely sure if that is necessary...
def after_sign_in_path_for(resource)
if resource.is_a(User)
user_info_path(resource)
else
super
end
end
I spent several hours trying to get the same functionality, and this is the code that ended up working for me:
def after_sign_in_path_for(resource)
current_user
end
If I ever tried current_user_path, I always got undefined local variable or method current_user_path errors.
Also, I'm using Rails 3.2.8 and Devise 2.1.2.
Hope that helps.
Based on #SnapShot answer, this worked for me. I'm using multiple devise models, trying to redirect back to the users profile edit page.
get 'user_root', to: redirect('/users/edit'), as: :user_root
ROR 7 answer
get '/users/home' => 'application#test', as: :user_root

Resources