How to call methods in ApplicationController from other controllers? - ruby-on-rails

I have protected function that gets and sets the current_user.
class ApplicationController < ActionController::Base
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user.nil? ? user : user.id
end
end
However, when I sign in, the current_user= method isn't called.
class UsersController < ApplicationController
def sign_in
if (#mailbox = Mailbox.find_by(email: params[:email], password: params[:password]))
current_user= #mailbox.user
redirect_to mailboxes_path
else
redirect_to :root
end
end
end
Also, the value of current_user is nil in other classes.
How do I make sure that the methods defined in ApplicationController are called?

The current_user method is an instance method, not a class method. Therefore, to access it, you need to invoke the method on the instance of the controller, which is self:
self.current_user = #mailbox.user
For further illustration, compare the following:
# This is a class method
def self.foo
return 'bar'
end
# This is an instance method
def foo
return 'bar'
end
The former method is on the class, while the latter is on an instance of the class. Therefore, they are respectively invoked in the following way:
# Invoking the class method
foo
#=> bar
# Invoking the instance method
self.foo
#=> bar

You're not calling your method by doing
current_user = #mailbox.user
Instead, you're setting a local variable called current_user. If you want to call your controller method you need to say:
self.current_user = #mailbox.user

Use helper_method:
helper_method :current_user, :current_user=
and remove current_user method from private.

Related

Rails 5 in applicationHelper an helper is visible, another not

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.

Calling a method of a Rails Engine from Main App

I have a Rails engine and have the following in the {Rails_engine}/lib/role_authorization/session_user.rb
module RoleAuthorization
class SessionUser
mattr_accessor :current_user, :role
def current_user(user)
current_user = user
end
def roles(role)
role = roles
end
end
end
I am calling the following methods from the main_application
{main_app}/errors/application_errors.rb
class ApplicationErrors
def self.access_denied error, controller
RoleAuthorization::SessionUser.current_user(current_user)
end
However i am getting an undefined method current_user here . Does anybody have any idea what i am doing here
RoleAuthorization::SessionUser.current_user
Is undefined because you defined current_user as an instance method on the SessionUser class. Without knowing exactly how you're using this class in the rest of the system these following suggestions could work but break something else:
RoleAuthorization::SessionUser.new.current_user(current_user)
or add self to the method declaration in SessionUser:
module RoleAuthorization
class SessionUser
class << self
attr_accessor :current_user, :role
def current_user(user = nil)
self.current_user ||= user
end
end
end
end
Notice I changed mattr_accessor to attr_accessor. This is because the former is for Modules only and you're calling it within a class. Then I open up the singleton class with class << self so I can define current_user as a class method and use attr_accessor to access class instance variables. Assigning user to self.current_user will now make it available for access via: RoleAuthorization::SessionUser.current_user

Set a current_profile for a user

A user can has many profiles. Each profile has its own properties. And the user can change from one profile to another anytime, anywhere.
So I want to set a method or variable available in controllers and views where I can set the user's current_profile (like devise's current_user helper).
We've tried using a ApplicationController private method and a ApplicationHelper method, but it doesn't work when the user's nickname it's not available (is set through a URL param).
This is the AppController method
...
private
def set_profile
if params[:username]
#current_profile ||= Profile.where(username: params[:username]).entries.first
else
nil
end
end
And this is the AppHelper method
def current_profile
unless #current_profile.nil?
#current_profile
end
end
Any idea?
Create a lib (for organization purposes) that extends ActionController::Base and define "set_profile" and "current_profile" there as a helper method, then require it and call it on ApplicationController.
application_controller.rb
require 'auth'
before_filter :set_profile # sets the profile on every request, if any
lib/auth.rb
class ActionController::Base
helper_method :set_profile, :current_profile
protected
def set_profile
if params[:username]
session[:user_profile] = params[:username]
...
end
end
def current_profile
#current_profile ||= if session[:user_profile]
...
else
...
end
end
end
That way you can call current_profile anywhere in your code (view and controllers).
If you have a relation where User has_many :profiles you can add a current:boolean column on profiles. Then:
def set_profile
if params[:profile_id]
#current_profile = Profile.find(params[:profile_id])
#current_profile.current = true
else
nil
end
end
# helper_method
def current_profile
#current_profile
end
#current_profile as memeber variable of ApplicationController is not visible in your helper. You should create accessor method in Appcontroller like:
def current_profile
#current_profile
end
or via
attr_accessor :current_profile
And in helper (make sure that accessor in controller is not private):
def current_profile
controller.current_profile
end
But you also free to define this as helper only, without involving controller at all:
def current_profile
if params[:username]
#current_profile ||= Profile.where(username: params[:username]).first
end
end
This will automagically cache you database query in #current_profile and automagically return nil if there is no param specified. So no need for extra else clause and extra set_... method

Rails - pass ##var to a view

Rails 3.2.3
I need to pass a class variable to a view. For some reason I'm unable to do this.
class HomeController < ApplicationController
##user_id = '1343454'
def index
#.......................
end
def about
#.......................
end
end
/view/home/about.html.erb
<% ....
##user_id is not visible
... %>
What's the easiest way to do it?
Please do not use ##.
In your application controller you can define:
def current_user
#current_user.id = '1343454'
end
helper_method :current_user
The helper method will make the current_user available in any controller or view in your application.
I believe, '1343454' is just an example. Usually we have something like:
#current_user ||= User.find(session[:user_id]) if session[:user_id]

Rails - session request is nil error

I have a method that I want to execute some search logic, and then save a Search object that has the searched string and user id of the person who did the search.
The search/save logic seems to be working fine otherwise, but when I try to get the current user (using a method from the application controller) it throws a runtime error that has to do with the session:
ActionController::Metal#session delegated to #_request.session, but #_request is nil: #<SearchController:0x1038e32e0 #action_has_layout=true, #view_context_class=nil, #_status=200, #_headers={"Content-Type"=>"text/html"}>
Here's the method in the search controller:
class SearchController < ApplicationController
...
def factualsearch(search)
if search
searchquery = Search.new
# this causes the error
if current_user
searchquery.user = current_user
end
searchquery.search_string = search
searchquery.save
...
end
#results
end
end
Here's the current_user method I'm trying to call from my application controller:
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
end
helper_method :current_user
Here's the pages controller where I'm calling the method:
class PagesController < ApplicationController
...
def search
searchcontrol = SearchController.new
#results = searchcontrol.factualsearch(params[:search])
end
...
end
Not exactly a direct way to fix this problem, but I was able to get around it by have the function accept the user in addition to the search query, rather than trying to call the current_user method from inside it. The class that calls the action can access the current_user method just fine.

Resources