So I have an Admin namespace, and a sessions controller within it.
There was also a admin/sessions_helper.rb made when I generated the controller, so I've been putting some methods in that, however I don't seem to be able to call them. Is this down to the namespace?
Error
undefined local variable or method `sign_out' for #<Admin::SessionsController:0x007fe2b2f55680>
def destroy
sign_out
redirect_to admin_path
end
end
sessions_controller.rb
class Admin::SessionsController < AdminController
...
def destroy
sign_out
redirect_to admin_path
end
end
admin/sessions_helper.rb
module Admin::SessionsHelper
...
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
end
Note that I'm not including the helper anywhere, because in the docs it says that all helpers are included by default.
Helpers are not available in the controller. Helpers are designed to be mixed into the view context.
There are workarounds to make the helper available to both the view and the controller, but it should actually make sense. The sign_out method you defined would not make sense at all in the view.
I suggest you to define it in the AdminController (that looks to be the base controller all admin controller inherits from).
module AdminController
...
private
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
end
Be sure to flag it as private or Rails could potentially use it as a controller action.
A nice way to call helper methods from a controller without including the module is with view_context:
class Admin::SessionsController < AdminController
def destroy
view_context.sign_out
redirect_to admin_path
end
end
But I agree with #simone-carletti, this doesn't feel like a helper method.
Related
I'm learning rails.
I'm build a simple test application, with a simple authentication scheme.
I'm using a user.role field to group the users.
In my Application Helper i have:
module ApplicationHelper
def current_user
if session[:user_id]
#current_user ||= User.find(session[:user_id])
else
#current_user = nil
end
end
def user_identity
current_user.role if current_user
end
end
Now, in my app, i can use current_user in all controllers as expected, but instead user_identity is not visible.
why?
The application_helper is used mainly to access methods in views - I don't believe it's available in a controller.
The reason your 'current_user' method appears to work is that I'm assuming you're using Devise - when you call 'current_user' it is using the Engine's method rather than your own.
To solve this, write out a new module:
module MyHelper
def current_user
if session[:user_id]
#current_user ||= User.find(session[:user_id])
else
#current_user = nil
end
end
def user_identity
current_user.role if current_user
end
end
And in the controller you're using:
class MyController < ApplicationController
include MyHelper
bla bla bla...
end
Any methods defined in MyHelper will now be available in MyController, as we've included the module in the controller
Helper modules are mixed into the view context (the implicit self in your views) - not controllers.
So you can call it from the controller with:
class ThingsController < ApplicationController
# ...
def index
view_context.user_identity
end
end
Or you can include the helper with the helper method:
class ThingsController < ApplicationController
helper :my_helper
def index
user_identity
end
end
But if you're writing a set of authentication methods I wouldn't use a helper in the first place. Helpers are supposed to be aids for the view.
Instead I would create a concern (also a module) and include it in ApplicationController:
# app/controllers/concerns/authenticable.rb
module Authenticable
extend ActiveSupport::Concern
def current_user
#current_user ||= session[:user_id] ? User.find(session[:user_id]) : nil
end
def user_identity
current_user.try(:role)
end
end
class ApplicationController < ActionController::Base
include Authenticable
end
Since the view can access any of the controllers methods this adds the methods to both contexts.
How do I guarantee that users only access the routes on my web app if they are logged in? I already have Users and Session models and users are able to create accounts. But how do I make sure that if they are not logged in they are always redirected to the login/sign up page, but if they are they have access to all the routes?
EDIT: So this is what my Application Controller looks like right now:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
So if there isn't a current user, I want to allow access only to the my Pages controller and its actions (which are basically home, signup, login, etc.). If there is a user, on the other hand, I want that user to be able to access all the routes in my route file.
class SomeController < ApplicationController
def show
if current_user.nil?
redirect_to '/path/to/login'
end
end
end
could probably give a more detailed answer if you paste in some code otherwise we all are just guess what your methods are called.
If you are using devise it comes with the built in helper method authenticate_user! which should be placed in your application controller.
If you are not using devise you can define you own method (for this example I will copy devise) authenticate_user! in application controller and call the before action
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
hide_action :current_user
private
def authenticate_user!
redirect_to :root if current_user.nil?
end
end
I want to create a method like current_user for devise's current resources.
Suppose I have two resources like User and Admin and devise is associated with both. So as usual it dynamically creates it's default methods like current_user and current_admin.
It creates it by defining like this in file lib/devise/controllers/helpers.rb:
def current_#{mapping}
How can I add a new method like this to it's dynamic methods.
I want to implement it with devise methods, so that when devise initializes then my method is also initialize with same mapping name.
Copy this code into your application controller. and your problem will solved.
Devise.mappings.each do |mapping, obj|
define_method "current_#{mapping}_email" do
eval("current_#{mapping}.email if current_#{mapping}")
end
helper_method "current_#{mapping}_email"
On view page/controller used according to your resource name
like if you have resource_name as user then its 'current_user_email'
or if have admin then 'current_admin_email'
This works!
I added a custom method current_user_email as follows!
Add a file devise_ext.rb in config/initializers:
And in that file, add your custom methods in this way:
module Devise
module Controllers
# Those helpers are convenience methods added to ApplicationController.
module Helpers
def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!(opts={})
opts[:scope] = :#{mapping}
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
def #{mapping}_signed_in?
!!current_#{mapping}
end
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
def #{mapping}_session
current_#{mapping} && warden.session(:#{mapping})
end
def current_#{mapping}_email
#current_#{mapping}.email if #current_#{mapping}
end
METHODS
ActiveSupport.on_load(:action_controller) do
helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session", "current_#{mapping}_email"
end
end
end
end
end
P.S. I don't think this is the best way to do this. But it works. I tried this in one of my applications! You can use this code till I find a better way to do this :)
I defined a helper class as below
module SessionsHelper
def current_user
#current_user= User.find_by_fbid(session[:fbid])
end
def sign_in(user)
session[:fbid] = user.fbid
#current_user = user
end
def signed_in?
!current_user.nil?
end
end
I included the Helper Class in my Application Controller
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
The sign in method gets called from Session Controller
class SessionsController < ApplicationController
def create
user = User.find_or_create_by_fbid(params[:user][:fbid])
user.update_attributes(params[:user])
sign_in(user)
redirect_to user_path(user)
end
end
However I am not able to access 'current_user' variable from users#show view.
<% if signed_in? %>
<p>
<b>Current User:</b>
<%= current_user.name %>
</p>
<% end %>
It says : undefined method `name' for nil:NilClass
Can anyone please advise ?
The method current_user does not get called at all from index.
Putting include SessionsHelper in your controller includes those module methods in the controller, so they are accessible in your controller methods. You want the helper methods available in your views, so you need to use helper SessionsHelper in your application controller.
That being said, I do agree with Jits that the methods you have in SessionsHelper really do belong in the controller instead of in a helper.
Generally you should have methods like current_user defined in your application_controller and then make them available as helpers in the views. This way the controllers have access to them (and trust me, you will most likely need access to things like that). Example:
def current_user
..
end
helper :current_user
What helped me:
Define methods to use in the controller in helper files
Define methods to use in the view in the relevant model file
Example
Suppose you had this in user_helper.rb
def something
2 + 2
end
simply move that code into
models/user.rb
and it will be accessible in the view without any further effort.
I'm trying to set the current user into a variable to display "Logged in as Joe" on every page. Not really sure where to begin...
Any quick tips? Specifically, what file should something like this go in...
My current user can be defined as (I think): User.find_by_id(session[:user_id])
TY :)
You might want to use something like Authlogic or Devise to handle this rather than rolling your own auth system, especially when you aren't very familiar with the design patterns common in Rails applications.
That said, if you want to do what you're asking in the question, you should probably define a method in your ApplicationController like so:
def current_user
#current_user ||= User.limit(1).where('id = ?', session[:user_id])
end
You inherit from your ApplicationController on all of your regular controllers, so they all have access to the current_user method. Also, you might want access to the method as a helper in your views. Rails takes care of you with that too (also in your ApplicationController):
helper_method :current_user
def current_user ...
Note: If you use the find_by_x methods they will raise an ActiveRecord::RecordNotFound error if nothing is returned. You probably don't want that, but you might want something to prevent non-users from accessing user only resources, and again, Rails has you covered:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
before_filter :require_user
private
def current_user
#current_user ||= User.limit(1).where('id = ?', session[:user_id])
end
def require_user
unless current_user
flash[:notice] = "You must be logged in to access this page"
redirect_to new_session_url
return false
end
end
end
Cheers!
It belongs in your controllers.
All your controllers inheirit from Application Controller for exactly this reason. Create a method in your Application Controller that returns whatever you need and then you can access it in any of your other controllers.