I have a situation where the user model is used multiple times in the same query in different places. For example: I want to iterate through all the groups the users is a member of, and get a unique list of all his/her friends that are also members of these groups.
Right now the user model gets reloaded every time, which results in:
1) Performance hit
2) Ideally I would use an instance variable to hold the unique list of friends, but I cannot do it since the user model gets reloaded.
It would make a lot of sense for the user model to be global for the duration of the request - any idea how I could achieve that?
Assuming that with "the user model" you actually means the current user instance (the User model is used to reference the model defined bu the class User) you normally use a memoization technique.
For instance, given you have a current_user method in your controller, you can do
def current_user
return #current if defined?(#current)
# In the following line load the current user. It's the first time
# this method is requested. Further calls will return the cached instance.
#current = ...
end
You can set a user like this:
def current_user
#current_user ||= User.find(1)
end
Related
I have an Active Record model method that's basically just a database query, and I'd like to cache the results, ideally as simply as as via a local variable in the model:
my_data = method_already_called ? stored_results : do_query
This made me realise that I don't really understand the object lifecycle of an Active Record model, and all the Rails guides really tell you is about callbacks. Specifically, I can guess that the object will be created when the user wants to retrieve some data associated with that object, but I have no idea when that object is going to be destroyed.
At a practical level, say a user requests some information, which causes an AR object to be created, take some instance data from the DB and manipulate it before presenting it to the user. How long does that object hang around in memory if the user wants to instruct it to do something based upon that information?
Thanks in advance.
EDIT: I'm specifically interested in the behaviour of Rails 5.1 on Ruby 2.4.
In practice, as long as you keep a reference to this instance. In most cases - until a request is finished.
class Model
# most common memoization pattern
def something
#cached_result ||= do_query
end
end
So, when your model will be instantiated (in controller/service/etc), it will be available as long as you can reference it. On the next request, #cached_result will be re-calculated.
If you want to cache something between requests, you can use CacheStore:
class Model
def something
Rails.cache.fetch("cache_key") do
do_query
end
end
end
Do not treat cache as permanent store though. But this will allow you to cache something between requests and for some period of time.
btw, #cached_result will be calculated for each model instance. If you do something like Model.where(field: "value") that returns 2+ instances, each of them will do do_query on the first call.
I am making an application where each user has access to multiple properties and can set assumptions for each property they have access to for modelling cashflows.
So user 1 has access to property 1 and sets assumptions that rent is 10k. user 2 has access to property 1 and sets assumptions that rent is 20k. Now if User 1 wants to calculate the NPV of rents over next 10 years then I need to use 10k and if user 2 wants NPV then need to use 20k.
I was thinking of implementing NPV of rent in Property Model, however then it will require current_user. Any other solution?
I would pass current_user to the model method, when you call it in the controller. That's a common technique.
# controller
def action
Model.calculate_npv(current_user)
end
i need to check whether a Thing's id is in my session variable.
my instinct is to add a method to the Thing model which checks to see whether the Thing's id is in an array stored in the session variable.
class Thing < ActiveRecord::Base
...
def pinned?
session[:pinned].include? self.id
end
end
The functionality I am trying to create is the user selects a bunch of different things for a list, which are then stored in a per-user session and then retrieved at a later date for printing.
Rails seems to prevent session access in the model without some hackery, which I'm keen to avoid. Maybe i'm approaching the problem the wrong way round, or the pinned? function belongs somewhere else?
Thanks.
Maybe pass it to the method as an argument instead of accessing it directly?
def pinned?(things)
things.include? self.id
end
thing.pinned?(session[:pinned])
I am building an app in which the users can have different roles (like seller, buyer, agency, etc). So my plan is use polymorphic association, build a class for each of the roles, and associate each instance with one user account. Each user can only be of one of those types, and after reading on the subject I concluded that this is better than using STI, but correct me if I am wrong.
So the app will have different sign up screens for the main types of user accounts. For instance, in the Seller sign up form, what will happen is that the user will fill in the details required for his user account and the fields specific for the Seller profile.
So this form should create the user object, and then the seller object associated to the former. How do you handle this? My guess is that this form should correspond to the 'new' action of the sellers controller, and in the create action the user account should be created before finally creating the seller.
Is this correct? If so, should I call the User controller create action from the Seller controller, or call directly the User model? If it's the former please provide some example code, because I am not sure about how I should call one controller from another.
EDIT: I also considered using a multipart form, which is probably easier, but before deciding I want to check out this option.
If you're bent on doing it this way, I'd say just call the model from the create method of the Seller controller. What type of relationship do you have between the User model and the Seller model? Because you'd need to do something like this:
def create
user = User.create(params[:user])
seller = Seller.new(params[:seller])
seller.user_id = user.id
seller.save
redirect_to #wherever
end
Here I just assumed you have a belongs_to :user in the Seller model. Still I would advise you to consider a gem like cancan or something to handle roles instead of this approach.
Good luck!
You can use nested form. A user has one role. You can view this railscast: http://railscasts.com/episodes/196-nested-model-form-part-1, it explained how to use nested form. You will be calling User controller when will create User and Role.
I'd take a different approach for this question. Have one roles class. Then create methods in the user class like.
def can_assign_users?
roles.map(&:name).includes('admin')
end
You might have 50 models in a few years otherwise. Plus there a plenty of gems that work like this so you can leverage them.
With Rails 3, How can you set a session variable in a model
session[:cannot_reason] = "no such item"
I'd like to set the above in my user model. Right now I get this error:
undefined local variable or method `session' for #<User:0x00000102eb9c38>
Ideas? Thanks
There's some unnecessary cargo-culting regarding whether or not models should have access to session data. I think this is silly, since session is really just another form of persistant storage (albeit for a much shorter time frame) and, in Rails, it seems ok to have your domain object also be able to persist itself.
That being said, the not very clean way to do it would be to pass the session hash as a parameter into the User method that will operate on it:
class User < ...
def mymethod(session, count)
session[:count] = count
end
end
In your controller, you would then do something like:
def update
# ...
user.mymethod(session, count)
end
Imagining that mymethod is implementing some business logic, this will modify the session hash appropriately. You don't have to pass the session hash back out to the controller because Ruby passes around references to objects (like a Hash)--modifications are made destructively to those referenced objects.
A word of advice: The above example, in my opinion, is smelly. This is because User is an ActiveRecord model which (I'm assuming) persists to a database and we're adding behavior that makes it also persist to a session cookie. To me, this violates SRP and should be avoided.
The way I would implement this would be to extract the session storage logic out to a separate domain object that encapsulates the reason for its existence. Maybe, for example, count isn't just a "count", it's the total of the number of items in the user's temporary cart.
class TemporaryCart
def initialize(session)
#session = session
end
def add_item
# ... other item adding logic
#session[:temporary_cart][:num_items] += 1
end
end
Now your controller would look like this:
def update
# ...
TemporaryCart.new(session).add_item
end
This is much more revealing and opens the door for an obvious way to abstract out session access code if you find yourself using session storage a lot. Also notice that I namespaced the data in the session hash (but didn't show this implementation). I recommend you do this so you don't step on your own toes when adding other data.
In short, you can't. By design, models don't have access to cookies and the session. If you to access items in the session from your model, you'll need to explicitly pass them in from the controller.
The session object is not visible in models. Either pass it as a parameter to a method in your model (IMHO bad) or define a method in your model which returns what you want and then store it in the session (from your controller).
class User < ...
def item_status
return :no_such_item
end
end
In your controller
session[:item_status] = current_user.item_status