I am fairly new to Rails, so apologies if it's not an 'instance variable' I am talking about!
I am using Devise for authentication, so can use things like current_user throughout the app. The app I am building has a User model, but also a Keyholder model (who is a sort of moderator for that user), and a Guest (who has read-only access to some things for that user).
What I want to know is - can I set it up so that I can use e.g. access_user when logged in as the keyholder to access the same object as current_user - and if so, where do I put the code in my app? It's quickly becoming very verbose and un-Rails-like having to repeat myself otherwise.
What I am trying to achieve is being able to use 'access_user' instead of current_user, so that regardless of whether it is the user, keyholder or guest logged in, it will use the user object.
For example:
def access_user
if user_signed_in?
access_user = current_user
end
if keyholder_signed_in?
access_user = current_keyholder.user
end
if guest_signed_in?
access_user = current_guest.user
end
end
Thanks!
Class level instance variables could also help you.
def access_user
if user_signed_in?
#access_user = current_user
end
if keyholder_signed_in?
#access_user = current_keyholder.user
end
if guest_signed_in?
#access_user = current_guest.user
end
end
You can just set this method in ApplicationController, and expose it to a helper method.
class ApplicationController
helper_method :access_user
def access_user
#blah blah
end
end
When method in ApplicationController, it's available to all controllers.
When you use helper_method, it is exposed as helper method to be used in View. More about helper_method: http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
Related
I use temporarily Rails as frontend app to communicate with an API.
After the authentication, I set the user_id in a cookie.
I use the her gem to call the User from the API and save it into an instance variable.
The issue is that I do this request on every page I and would like to do it once.
It's like #current_user is reset after each page.
def current_user
#User.find -> Her model
#current_user ||= User.find(cookies.signed[:user_id]) if cookies.signed[:user_id]
end
There no clear solution because your user coming from API. You can try to do something like that:
#remember user attributes without references
session['user'] = #current_user.attributes #remember
#user = OpenStruct(session['user']) #load, allow call #user.name etc, but not #user.posts
#use class variable
class User
include Her::Model
##tmp = {}
def remember
##tmp[id] = self
#call job etc to delete user from tmp to prevent something that reminds "memory leak"
end
def self.local_find(id)
##tmp[id]
end
end
def current_user
#current_user ||= User.local_find(cookies.signed[:user_id]) ||
User.find(cookies.signed[:user_id]) if cookies.signed[:user_id]
end
The main reason not to store(remember) objects in the session(long-term variable) is that if the object structure changes, you will get an exception.
I've got a User model, that has an associated keyholder and guest. To make my code more DRY, I'd like it so that current_user will refer to:
a) If a user is signed in, it will be the normal Devise current_user object.
b) If a keyholder is signed in, it will be that keyholder's user - i.e. current_keyholder.user
c) If a guest is signed in, it will be the guest's user.
I've tried adding helpers into the application controller, however, these don't work as the e.g. current_user isn't being initialized before my code runs.
Basically what I'm trying to achieve is:
def current_user
if user_signed_in?
#current_user ||= warden.authenticate(:scope => :user)
elsif keyholder_signed_in?
#current_user = current_keyholder.user
elsif guest_signed_in?
#current_user = current_guest.user
end
end
The above code gives me a "stack level too deep" error, but will hopefully show what I'm trying to get working. If anyone can help, that would be great, thanks!
I found a workaround - in the application controller, I added a before_filter called access_user, then a method called access_user:
def access_user
if user_signed_in?
#access_user = current_user
elsif keyholder_signed_in?
#access_user = current_keyholder.user
elsif guest_signed_in?
#access_user = current_guest.user
end
end
Which I can then call in the views using e.g. <%= #access_user.id %>
If there is a better way or this is going to cause some security issue, please can somebody let me know?
I have a Rails helper method...
def current_user
#user = User.find(cookies[:user_id]) if cookies[:user_id]
end
And I want to add a non-database property to it...
self.custom_property = true
So I can access it in my view.
<%= current_user.custom_property %>
But, Rails says "Undefined method: custom_property"
Here's the full code:
def current_user
#user = User.find(cookies[:user_id]) if cookies[:user_id]
#user.ribbon_array ||= []
self.is_moderator = #user.ribbon_array.include?(1) ? true : false
end
I'd like to do it like this so in my view I can check current_user.is_moderator. Elsewhere in my code I have an #is_moderator variable that's specific to each page, but this one would be used across the whole app and specific to the current_user as opposed to the user on the profile.
To accomplish this
I'd like to do it like this so in my view I can check
current_user.is_moderator. Elsewhere in my code I have an
#is_moderator variable that's specific to each page, but this one
would be used across the whole app and specific to the current_user as
opposed to the user on the profile.
I recommend you to implement a method on your user model called
def is_moderator_of(page)
# returns true or false here
end
Or better yet, use a authorization gem like cancan
EDIT:
Also, this
#user = User.find(cookies[:user_id]) if cookies[:user_id]
#user.ribbon_array ||= []
will generate "Method not found: ribbon_array for nil" if there is no cookies[:user_id]
Edit
My bad without too much thought. In this case maybe a better solution is to add another helper method. Helpers themselves are not so object oriented, so you should be able to tolerate this kind of solution as well.
def current_user
#current_user = User.find(cookies[:user_id]) if cookies[:user_id]
end
def is_moderator?
ribbon = #current_user.ribbon_array || []
ribbon.include?(1)
end
In your app/models/user.rb file:
class User << ActiveRecord::Base
attr_accessor :custom_property
...
end
Then your view code should work.
Alternatively, you could just set #custom_property = true in the helper, and use #custom_property in the view.
I thought methods such as name and email were default in rails?
In my static pages view, in profile.html.erb I have:
<% if logged_in? %>
<% provide(:title, #user.name) %>
<% else %>
<% provide(:title, 'Profile')%>
<% end %>
I put in my static_page_controller
def profile
#user = User.find_by_remember_token(:remember_token)
end
When I go to the console User.find_by_remember_token("actualtoken").name returns me the appropriate users name, but :remember_token does not. How do I make :remember_token = the logged in users remember token?
In my sessions_helper I have
def log_in(user)
cookies.permanent[:remember_token] = user.remember_token
current_user = user
end
def logged_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end
def log_out
current_user = nil
cookies.delete(:remember_token)
end
private
def user_from_remember_token
remember_token = cookies[:remember_token]
User.find_by_remember_token(remember_token) unless remember_token.nil?
end
end
copying it to my static_pages_helper didn't accomplish anything.
Quick things you should be aware of the rails framework and the ruby language:
A function defined in any of your helpers will be available to all helpers and views (so there is no reason to copy and paste the same functions through different helpers);
You're probably using an authentication gem and I guess it is the Devise gem. If this is right, then you should not be overriding their helpers unless you have a reason to do this;
User.anything will call the static function anything from the User class;
user = User.find_by_anything(the_thing) is a class static helper provided by ActiveModel that will query the database looking for a user that has *anything = the_thing*; this user or nil will be returned;
user.an_attribute will call a function that returns the user specified attribute (which is the same as the column name of this attribute by default);
user.try(:anything) will try to call the function anything from the user and return its value. If user is nil, the returned value will also be nil.
That said, I guess you just wanted to retrieve the current user remember token, which can be accomplished with the following:
user = current_user.try(:remember_token)
EDITED: The question is a bit messy, but I also think the following code will work with your controller:
def profile
#user = User.find_by_remember_token(params[:remember_token])
end
You must access the request's parameters through the params hash.
EDIT: completely replaces my first answer with one hopefully not as stupid :-)
(While there are several ways to implement and manage sessions in Rails, the default uses a cookie in the browser to reference a key stored in memory. Sessions are created by a request from a browser, so while it's certainly possible to use the console to get at an existing session, it's probably not what you want.)
So your method, user_from_remember_token will either return a user or nil. What I don't see in your code is where you're setting the remember_token on the User model. I'll assume it's there, but you may want to have code that tells the user to log in if you don't find them. A common pattern would be
def current_user
#current_user ||= user_from remember_token
unless #current_user
flash[:notice] = "Yo! Log in first."
redirect_to login_path and return
end
end
There's no problem calling a model finder from a separate controller. But why call User.find_by_remember_token(:remember_token) -- you don't have the remember_token yet (right?). Don't you just want to call the current_user method in your sessions helper?
If the method is not visible, you may want to include or require the session helper in your application_controller.rb
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.