Rails - helper method in controller available in the model? - ruby-on-rails

There is a helper method in the application_controller:
def current_user
#current_user ||= User.find(session[:user_id])
return #current_user
end
I want to set up before_save callback in a model - so that the current_user.id will be saved when the activerecord is updated.
Basically, whenever the model is changed or modified and saved, I want to ensure the updated_by column is populated for that model/
But I'm not sure how to access the current user in the model?
Can anyone advise?

You can do something like this to access the instance variable declared in controller from model
class User < ActiveRecord::Base
cattr_accessor :current_user
end
class ApplicationController < ActionController::Base
before_filter :current_user
def current_user
#current_user ||= User.find(session[:user_id])
User.current_user = #current_user
end
end
end

It is not possible, current_user is available only in the controller. You should pass the current_user id in a method, for example :
In the controller method :
def action
method_with_user_id(current_user.id)
end
in the model class definition :
def method_with_user_id(user_id)
#things with the user id
end

Helpers methods are not available for models its just for the views.
I think that instead of set up before_save in the model and pass the current_user.id i think there is a relationship between your model and the user, so you should make the relationship, and pass the user object in the create action of that model.
And in your application controller you should have something like this.
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def current_user
#current_user ||= User.find(session[:user_id])
return #current_user
end
end
And in your model controller you have access to that method.
def create
current_user.my_model.create(params_model)
end

As a design choice, its not recommended to call controller helpers from your models. The model should not know about its controllers. That's not Railsy way of doing things.
To get a workaround you can set the instance variable #current_user in a class attribute of your controller:
class ApplicationController < ActionController::Base
before_filter :set_current_user
def set_current_user
#current_user ||= User.find(session[:user_id])
Account.current_user = #current_user
end
end
Then, in your Model:
class User < ActiveRecord::Base
cattr_accessor :current_user
end
Then just access the #current_user with Account.current_user in your Model.

Related

How to make #current_user available in my views?

In my application controller I have a module that I include like:
require 'current_user'
class ApplicationController < ActionController::Base
include CurrentUser
end
So if there is a token cookie set, it looks up the user.
In my views i am doing this:
<% if #current_user.present? %>
...
..
This doesn't work currently in my views because there is no #current_user or current_user in my views.
How can I create a #current_user variable globally for all my view pages to access?
I'm not sure what your module does, but you can just make a method on in ApplicationController:
def current_user
# ... fetch current_user somehow...via your module method, or session:
#current_user ||= session[:user_id] ? User.find(session[:user_id]) : nil
end
Then you can access it in your views via:
<% if current_user.present? %>
If you really want to force it to be #current_user, I guess the easiest way would be to add it as a before_action:
class ApplicationController < ActionController::Base
before_action :set_current_user
private
def set_current_user
#current_user = session[:user_id] ? User.find(session[:user_id]) : nil
end
end
Now you can access it in views via #current_user, but it will be eagerly fetched even if you aren't using it in your views. Using first method will only query for current user if you use it.
In my module I just some ruby metaprogramming to include it in my application_controller class:
module CurrentUser
def self.included(base)
base.send :helper_method, :current_user
end
end
Now current_user works in my views.

Render in JSON in Rails

I've got a controller with a single index action:
chat_room_controller.rb
class ChatRoomController < ApplicationController
before_action :authenticate_user
def index
#current_user = User.find(session[:user_id])
end
end
I need to render my variable #current_user in JSON format. So I need to create a show action or I can simply handle this situation adding:
respond_to do |format|
format.json
end
I need the format JSON of this variable for reference in an AngularJS module so I also need access to it.
If you want to have the current_user method to be available in your rails views, you should create a helper method in your ApplicationController, and then in all controllers, which inherit from it, the view will have the current_user helper:
class ApplicationController
protected
def current_user
#current_user ||= User.find(session[:user_id])
end
helper_method :current_user
end
class ChatRoomController < ApplicationController
def index
render json: current_user
end
end

Code in each controller method

I have a controller and every method of it starts with the following code:
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
return
end
I'm just wondering if it is possible to avoid code duplication in this case ?
Yes, use a before_filter
class YourController < ApplicationController
before_filter :check_user
def check_user
..
end
end
Absolutely.
class MyController < ApplicationController
before_filter :ensure_logged_in
# actions here.
def ensure_logged_in
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
end
end
end
You shouldn't need to worry about the 'return', as rails will bail out of the filter pipeline once the redirect happens.
To avoid duplication you just need to add before_filter in every controller where you want to check user authentication.
class SomeController < ApplicationController
before_filter :authenticate_user
end
then add your user authentication logic in application controller something like this,
class ApplicationController < ActionController::Base
private
def current_user
#current_user ||= UserData.find_by_login(session[:cuser]) if session[:cuser]
end
helper_method :current_user
def authenticate_user
redirect_to({:controller=> 'user_data', :action=> 'login'}, :alert => "Not authorized") if current_user.nil?
end
end
You can use current_user helper method in every controller to get current user.
Try to use before filter. This should be fine

Get value from session on every method of every controller

I have some controller and in each method of each controller I have next code:
#user = session[:user]
Is there a way to avoid putting this code on every method of each controller?
You can add your code in ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :current_user
def current_user
#user = session[:user]
end
end
The anwer of #nash is fine, and here is an alternative providing helper methods you can use in every method/view. This is the way gems like Devise go:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
helper_method :user_signed_in?
private
def current_user
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
end
def user_signed_in?
return 1 if current_user
end
def authenticate_user!
if !current_user
flash[:error] = 'You need to sign in before accessing this page!'
redirect_to signin_services_path
end
end
end

Authlogic, Namespace and private methods in ApplicationController

I am troubleshooting why my ApplicationController's methods don't seem to work in my namespaced admin area and it seems like when I'm in a namespace I cannot access my ApplicationController's private methods, is this right?
If it is like that, what's the best practise to reuse something like Authlogic's example ApplicationController methods in my namespaced controller? I could easily copy and paste the methods to an AdminController or something, and I could also un-private these methods - but this doesn't seem like such a good way to do it.
Here's what the example ApplicationController from Authlogic (and mine) looks like:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user_session, :current_user
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
# and some more methods here...
end
And that's how I inherit from it in my namespace:
class Admin::DashboardController < ApplicationController
layout 'administration'
require_user # fails
def index
end
end
Thanks for your help,
Arne
You should use before_filter in Admin::DashboardController:
class Admin::DashboardController < ApplicationController
layout 'administration'
before_filter :require_user
def index
end
end
I haven't used authlogic, but maybe you need to change
require_user
to
before_filter :require_user

Resources