I try to optimise a Rails app with big load that currently hit the databsae on every request. I try now to optimise this by saving some info on the session so I don't need to go the database every time. I'm currently doing something like this.
def set_audience
if current_user
session[:audience] ||= current_user.audience
else
session[:audience] ||= 'general'
end
end
And then calling session[:audience] anywhere on my controller and views. Seems fine except that I'm seeing on the New Relic logs that sometimes the session is not set and therefore the app get a nasty nil.
Im thinking better I should use instance variables, maybe more something like this.
def set_audience
if current_user
session[:audience] ||= current_user.audience
end
#audience = session[:audience]
#audience = 'general' if #audience.empty?
end
And then calling #audience in my app.
Is this correct? I would like to make sure I'm used the preferred approach to this.
I think the standard approach here would be to use a helper method on ApplicationContoller:
class ApplicationController < ActionController::Base
private
def current_audience
#current_audience ||= current_user.audience
end
helper_method :current_audience
end
This will work pretty much exactly like the current_user helper method in your controllers and views. Depending on the specifics of your application, you may want to add some more robust nil handling, but this is the basic idea.
Related
A common pattern is to use current_user in many places, but check whether it is set.
if current_user
#your code
end
But instead of injecting an if check just about every time you want to use current_user, how and where can you wrap the current_user method in a different method ONCE, so that the you won't have to deal with your code breaking due to a nil value for devise's default current_user method?
The current_user method is added to ApplicationController, then I think you can override it in ApplicationController doing somethig like:
# in application_controller.rb
alias_method :devise_current_user, :current_user
def current_user
if ...#your validation
devise_current_user # || User.new # <-- or whatever other non-nil result
end
end
Creating a custom classes that have the methods you are trying to use on current_user throughout your application is one way.
http://littlelines.com/blog/2013/06/22/how-to-guard-against-ruby-nil-errors/
If you do not want to account for nil value possibilities while working ruby, you're going to be swimming upstream, and become very frustrated. I know I've had similar feelings. It can be really annoying to write:
if current_user && current_user.attr == 'val'
But I've developed ways to make that less awkward over time...
--edit to show some of the things I do to avoid this--
I often do some of these things. This isn't necessarily a recommendation, or a best practice, but I find that it makes my code less verbose and more readable sometimes
If I expect an array, but the array might be nil:
my_array ||= []
That way I can safely do things that require array-ness, usually when this is needed in various places in the scope.
my_array.size
and not have things choke.
In a similar way I might do
car ||= Car.new
Then I can treat car like any car object. But I probably wouldn't do this with current_user, though.
I'd like to use pg_audit_log for logging in a rails app. The audit log must not only show the columns that have changed, but also the user who made those changes. The docs don't show how to do this, but after looking through the pg_audit_log source (postgresql_adapter.rb) I see it reads user information from a thread local variable, ala:
current_user = Thread.current[:current_user]
I've considered setting/unsetting this in before and after filters like so:
Thread.current[:current_user] = current_user
(using the current_user helper method in the controller to get the currently logged in user), but that seems dangerous. I'm now spending time trying to understand how the rails request cycle and threads interact, to get a better feel for just how dangerous. In the mean time, I was curious if any SO users currently using pg_audit_log have solved the need to log the user_id and user_unique_name to the log tables each time the user makes a change to a record.
Setting the current user the way you describe is a common way to do it. See, for example, http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
Some example code could look like:
# in your model
class User < ActiveRecord::Base
def self.current
Thread.current[:current_user]
end
def self.current=(user)
Thread.current[:current_user] = user if user.nil? || user.is_a?(User)
end
end
# in your controller
class ApplicationController < ActionController::Base
before_filter :set_current_user
def set_current_user
User.current = user_signed_in? ? current_user : nil
end
end
Relying on the Thread.current hash to provide model-level access to objects managed by the controller is indeed controversial. For example, see the following:
Safety of Thread.current[] usage in rails
It is worrisome that this particular feature is undocumented in the pg_audit_log gem.
Suppose you had not actively explored the gem's source code, and suppose you had independently decided to define Thread.current[:current_user] = something in your own application, for your own purpose. In that case, pg_audit_log would audit that object, without your knowledge.
Granted, the name current_user is so universally accepted to mean the currently logged-on user as defined by authentication routines that it's difficult to imagine this potential bug as a concrete problem, but from a design standpoint? Ouch.
On the other hand, since you know what you are doing, ensuring that Thread.current[:current_user] is set/unset at the beginning/end of each and every response cycle should make the process safe. At least that's what I gather from reading lots of posts on the topic.
Cheers, Giuseppe
My Rails 3.2 project has a devise-generated user and a set of models that all contain data that's specific to that user. I want a logged-in user to be able to access only his own data through the APIs exposed by the controllers.
Now, a brute-force way to enable this would be to change each and every controller from something like:
def index
#stuff = Stuff.all
to
def index
#stuff = Stuff.find_all_by_user_id current_user.id
And I have to repeat this for every single action of every single controller. Is there perhaps a more succinct and DRY way of achieving the same effect? The amount of boilerplate I have to write feels wrong.
Thanks!
Take a look at the CanCan gem.
a) You can have a before callback in application_controller.rb that looks something like
def find_stuff_from_current_user
#stuff = Stuff.find_all_by_user_id current_user.id
end
And than call this in every controller like this:
before_filter :find_stuff_from_current_user
Now you have #stuff variable available in every controller and in every action.
b) Or you can use scoping in stuff model.rb where you say something like:
scope :stuff_from_current_user, where(:user => current_user)
The application I am writing is pretty complex and has different stylesheets/javascripts depending on the action. Right now I have added some methods to the application controller that allows me to build up an array which then is used in the application.html.erb layout. This feels a little sloppy. It would be nice to somehow configure this from a settings file based on action or something. (I guess that might get messy too though)
Any ideas?
def initialize
super()
#application_stylesheets = Array.new
#application_javascripts = Array.new
end
def add_stylesheet(stylesheet)
#application_stylesheets.push(stylesheet)
end
def add_javascript(javascript)
#application_javascripts.push(javascript)
end
Hi I have a rails App which displays always the company name on each page.
Since a logged in user can have multiple companies she belongs to.
User and companies are stored in the db.
I use authlogic for the user management.
Now I do not want to hit the database on every postback or page change
What would be best practise to chache/store the company until the logged in users changes or the user selects a different company? Something like global instance vars for a given user.
I started with this in my application_controller
def current_company
return #current_company if defined?(#current_company)
#current_company = Account.includes(:users).where(:users =>current_company)
end
and I realized that I am still hitting the db...
Is the session the recommended way or what would be best practice for this...
Thanks for the help in advance
||= way:
def current_company
#current_company ||= Account.includes(:users).where(:users =>current_company)
end
memoize way:
def current_company
Account.includes(:users).where(:users =>current_company)
end
memoize :current_company
Differences between this method and normal memoization with ||=
http://apidock.com/rails/ActiveSupport/memoize#447-Differences-between-normal-or-assign-operator
#tadman, you are right but from my point of view depends how complex its the method that you are trying to "cache". For simple cases I prefer ||=
I think this is what you're looking for
https://github.com/nkallen/cache-money