I don't understand when and what calls this function:
def current_user
#current_user ||= user_from_remember_token
end
it's from: http://ruby.railstutorial.org/chapters/sign-in-sign-out#code:current_user_working
When it kick in?
A little higher up you'll see this:
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
This means that current_user is available in all your controllers and all your views. So any time you need to know who the current user is for access control, filtering data, assigning a creator to a new object, put their name in the upper right corner of the page, etc. you can ask current_user.
Related
In laravel I am use to having
Auth::user()->id
which I can reference for setting up data-id's or something in views. I am working in a ruby on rails app and cannot for the life of me find an answer to how to achieve this in rails. I found a lot of answers talking about current_user but I cannot get any data in the view.
To be clear what I am try to set up exactly is
Enroll
Here the "current_user.id" would be that users id. With the code above (and any variation of it I can think of) I am getting nothing, no errors but no data either. Do I really have to set this up in every controller method to access it somehow? Does anyone have a solution to this that they can point me towards?
Thanks so much for any help.
If you're going to access any instance variable in a view, you need to define it first (either in a controller or in the view).
Depending on how you have auth set up, you probably have a current_user method somewhere.
It could be defined in ApplicationController (which has functionality shared by all controllers);
class ApplicationController < ActionController::Base
def current_user
User.find_by id: session["current_user_id"]
# or whatever
end
helper_method :current_user
end
The helper_method line makes it accessible in your views, so you can write <%= current_user.id %>.
You could also write some code so that the #current_user instance variable is available in all your views:
class ApplicationController < ActionController::Base
before_action :define_current_user
def define_current_user
#current_user = current_user # call the 'current_user' method defined elsewhere
end
end
Im working with a medium sized Rails application and I do this in every controller:
def create
#object = Model.new(params[:model].merge(editing_user: current_user))
...
end
def update
#object = Model.find(params[:id])
#object.editing_user = current_user
...
end
Setting the editing user over and over again is not DRY. I thought about cleaning this up with an observer but it would need access to the current user. Observers do not have access to the current user, neither should they (Law of Demeter).
Any suggestions how to DRY this up between controllers?
class ApplicationController < ActionController::Base
before_filter :init_request
def init_request
params[:editing_user] = current_user
end
end
I like using decent_exposure to dry up my controllers. It automatically finds or initializes a model instance, based on whether an :id was passed as a param, and it assigns the attributes from params[:model].
To finish drying up your code, you could use the new strategy support (see the end of the readme) to automatically set the editing_user attribute on your model.
You could try an after_filter for this. Perhaps something like so:
class ApplicationController < ActionController::Base
after_filter :set_editing_user
def set_editing_user
#object.update_attribute(:editing_user, current_user) if #object && current_user
end
The difficulty, of course, is that you'll be saving the object twice per call. Generally though creations and updates don't happen so frequently that two database commits is a serious problem, but if you expect to be the next Twitter -- with massive database insertion load -- it could be an issue.
You could also possibly set this in a before_filter, but then you'd have to find or set the object in a previous before_filter. Otherwise #object will always be nil and the before_filter will never fire. You can use the filter ordering methods prepend_before_filter and append_before_filter to ensure the correct sequencing of these filters.
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.
This post - http://www.ruby-forum.com/topic/51782 - seems to suggest a way of setting User.current_user in a before_filter in a controller and accessing User.current_user in models affected by that action. Is this perfectly thread-safe or are there security issues here? Seems like the correct approach would be to always pass in #current_user into any model that needs it, but that gets messy.
That solution is not thread safe and when processing two requests (A and B), the later one will change the earlier one's current user mid request (which would be almost impossible to debug and extremely confusing to the user).
Store the user (or the user id) in the current thread's storage.
class User < ActiveRecord::Base
class << self
def current_user
Thread.current[:current_user]
end
def current_user=(user)
Thread.current[:current_user] = user
end
end
end
before_filter :set_current_user
private
def set_current_user
User.current_user = User.find(session[:user_id])
end
Using a global before_filter in your ApplicationController should be thread-safe (if you are using thread local storage). This is from the excellent declarative_authorization gem's documentation:
If you’d like to use model security,
add a before_filter that sets the user
globally to your
ApplicationController. This is
thread-safe.
before_filter :set_current_user
protected def set_current_user
Authorization.current_user = current_user
end
Update:
The actual implementation of Authorization.current_user looks like this. It uses thread-local storage, which is what makes it thread-safe.
I have an object in ruby on rails for #user which contains username, password, etc
How can I ensure that the values are kept throughout all views?
Thanks
If you set it up as follows:
class ApplicationController < ActionController::Base
before_filter :set_user
protected
def set_user
#user = User.find_by_id(session[:user_id])
end
end
Then in all of controller, since they all inherits from ApplicationController, will have the #user value set.
Note: this will set the #user to nil if the session[:user_id] as not been set for this session.
For more on filters and the :before_filter, check this link out: Module:ActionController::Filters::ClassMethods
I take it you want some sort of user sustem? logged in and tracking all over your system?
AuthenticatedSystem is something that can help you. there is a lot of documentation out their that will tell you exactly how to setup an environment that uses it. I personally use if for several systems I've made.
In your ApplicationController, add your object to the session and create a variable for it. Add a before_filter that calls the method that does that.