I am using this to access session in Model.
http://www.zorched.net/2007/05/29/making-session-data-available-to-models-in-ruby-on-rails/
Can anybody confirm that it will work with Apache + Passenger deployment too?
Or if there are any other alternatives to achieve the same?
Thanks,
Imran
I did not find any code on the internet that works, so I did some research and wrote my own. It works for Rails 3.2.x and probably on some other versions.
Insert this in your ApplicationController
# Set a filter that is invoked on every request
before_filter :_set_current_session
protected
def _set_current_session
# Define an accessor. The session is always in the current controller
# instance in #_request.session. So we need a way to access this in
# our model
accessor = instance_variable_get(:#_request)
# This defines a method session in ActiveRecord::Base. If your model
# inherits from another Base Class (when using MongoMapper or similar),
# insert the class here.
ActiveRecord::Base.send(:define_method, "session", proc {accessor.session})
end
I will not remind you that accessing your session from a model may lead to bad code. Other posts may tell you, that you are stupid. Though there are some valid reasons to access the session from your model, like implementing a Model.save method, that saves to the current users session.
Yes. It is the only efficient way I found to use session data in model. I also used it and never faced any deployment issue with Apache + passenger.
But you need to confirm when you will be playing with session values. On each new request to server, session value gets stored in thread and we can access it in model. If you are applying any logic by using thread value, then also make sure with situation when thread value might be nil also.
Because I got an issue where on development, my every code worked fine but on production, during starting server it caused an issue as initially it considered thread value as nil.
Related
I'm working with ruby on rails 2.5.
I have an object "payment_plan". This object can change with a toggle behavior that changes, and I need to keep this object alive thorough all the session and at the end it should be save par of it in my mongo db. I need to access the latest status of the object always. The controller should be capable to update the object and the view should be able to access the latest state of the object.
Any insights on how to do something like this would be great :)
I have try to create a helper function in the application controller but had problem accessing it from the view.
Also I prefer not to save the state of the object in the db, because it will be too many db calls later.
To access a controller helper function from view, define it as a helper:
class SomeController < ApplicationController
helper def some_helper
end
end
As for storing some data in session - it's ok, rails has nice session store mechanism for session[:my_object_prop] = 1/session[:my_object_prop] (see in official guide)
But keep in mind, that:
by default session is stored in cookies, which are passed in headers from client browser with every request to your server (even to images, if they are on the same domain), so it's only practical to save small amounts of data there.
user can clear their's cookies and data will be lost (this is usually fine)
the opposite of the latter - a user may come to your app with session data from old version of your code
thank you very much, that was very helpfull!
Another question, can I override a session value after setting it up?
for example session[:plan_id]="plan_id_1" and they further on in the run do something like this: session[:plan_id]="plan_id_2"
thanks!!!
I need to get current login user name in model code, but I dont want to add a new additional parameter that will require many changes. So I am thinking whether it works to put the login user name in Thread.current, and then access it in model code.
It works in a simple try, but I have a concern whether it can work properly with unicorn multi workers, for example
- the login request is handled by worker 1, and the 2nd request is handled by worker 2. My basic understanding is that it should be ok because I set it from session into Thread.current in ApplicationController before filter that should be executed in the beginning of each request.
- if a unicorn worker is killed and restarted for whatever reason, is the request will be re-initiated, and still have the session data?
I dont have enough knowledge on unicorn... so probably it is a naive question...
And any other possible issue to use Thread.current?
Thanks in advance for your help!
You don't have to use Threads directly, you can use this gem https://github.com/steveklabnik/request_store
Your User model code can look something like this:
def self.current_user
RequestStore.store[:current_user]
end
def self.current_user=(logged_in_user)
RequestStore.store[:current_user] = logged_in_user
end
And in your controller, after login you can set User.current_user = current_user
I'd like to do the following:
define a before_filter in application.rb that extracts the user's IP address and stores it anywhere, preferably in the session.
define two before filters in all my models as before_create and before_update that add the current user's IP to the object to be stored.
Problem: I cannot access session[] neither env[] in a model. Can anyone help with a standard solution that I don't know yet?
Regards
Jason
Try this. In your user model add a class attribute accessor
cattr_accessor :current_ip
In your application controller add:
before_filter :set_current_ip
protected
def set_current_ip
User.current_ip = request.env['REMOTE_ADDR']
end
Then in your model you should be able to just call User.current_ip
We do something similar to get the current_user object passed through.
You're having trouble doing what you want because Rails is designed not to allow you to have access to session information in your models. It's the classic separation of concerns with MVC. Models are meant to work independently of your other layers, and you'll be thankful they do when you start doing things with Rake or other system tasks where you won't have a session.
The
cattr_accessor :current_ip
is a horrible approach. It's a hack and it should be apparent why. Yes, it may work, but it's the wrong approach to this problem.
Since you're tracking "who" did "what" by their IP, the logical place for this to happen is in the controller layer. There are several approaches you can take, including using CacheSweepers as auditors, as outlined in the Rails Recipes book. CacheSweepers can observe models but also have access to all controller information. Using the ditry attributes in a rails model, you can see exactly what changed.
#user = User.find_by_login "bphogan"
#user.login = "Brian"
#user.save
#user.changed
=> ["login"]
#user.changes
=> {"login"=>["bphogan", "brian"]}
#user.login_was
=> "bphogan"
Combine this with the session info you have and you have a pretty awesome auditor.
Does that help?
If you want to save the IP in the session, you can create a before filter in the applicationController. Like this, for each action, the filter is called and the ip is stored.
authlogic is a plugin to manage users login/sessions etc, it has a built in option to track the users IP
What you really need is a versioning plugin - I suggest having a look at one of the fine solutions at http://ruby-toolbox.com/categories/activerecord_versioning.html
Edit: archived version of that link (was 404 since sometime in 2012): https://web.archive.org/web/20111004161536/http://ruby-toolbox.com:80/categories/activerecord_versioning.html
I have a rails app that has the following content in the config/initializers/session_store.rb file:
ActionController::Base.session = {
:key => '_app_session',
:secret => 'a really long string here',
:expire_after => 2.minutes
}
ActionController::Base.session_store = :active_record_store
So during normal operations we are seeing ActiveRecord objects created in the database for each session. The issue is that we don't always want to create a session for requests - we'd like to be able to turn off session creation for automated requests. We are seeing thousands of session records in the database, one for each automated request. Prior to rails 2.3.3, the following was possible:
class ApplicationController < ActionController::Base
session :off
...
end
but now in rails 2.3.3 "session :off" has been deprecated because sessions are now lazy-loaded - if you don't use them, they are not created. The problem seems to be that the session object is always assigned in the sessionstore.rb file, and therefore always created. If I remove the session assignment block from the config file, then no session records appear after an automated request. My question is, how can I move the configuration assignments to the session hash out of session_store.rb and into the ApplicationController class (or elsewhere) where the session can be conditionally assigned only if a request is not automated? I'm concerned that the session config data may be needed before the controller handler is executed. Where would I put the assignment of the session key values?
Thanks in advance for any help.
The config/initializers are one-time configurations, they usually only ran once, on startup. According to the documentation, the lazy loading will only initialize the session if the "session" object is touched during a request.
Are you certain that you don't touch the session variable at all in your request cycle? (That is, in the filters, actions, helpers or templates). Also, do the session objects have some content that could tip you of as to where they were created?
sorry for the "late" answer but I think is relevant for people landing here.
was struggling with similar issues ever since session :off was removed from Rails.
I decide to "back-port" it for Rails 2.3+ as a plugin https://github.com/kares/session_off
Say I want to store some variable in my controller. I want to initialize it in one action, increment it in another, and read it in yet another. Just declaring this variable with #foo doesn't work because #foo dies after the action that created it is rendered.
I do not want this variable to be stored in a model.
Is there a way to preserve this variable besides storing it in a session?
It seems like I've run into this simple problem a few times, and I want to know the best way to go about solving it.
Not really. Each call to a controller action is stateless. Nothing is available after the controller action finishes. A new controller instance is created for each request, and then discarded at the end of the request.
If you don't want to store it in the session, or database model, you don't have many options if you're wanting that variable to be specific to a particular session.
If it is global across all sessions, you could put it in a ##class_variable rather than an #instance_variable, but that can get messy once you start having multiple Rails processes (each which will have their own copy of it), or if you're running in threadsafe mode, you can end up with nasty concurrency bugs.
I guess you could look at something like memcached, but you'd still need to key that to some user_id or other session marker (unless it's global)
I too am wondering why you are against using session? If you don't like working with session directly in your actions, you could emulate a surviving #foo instance variable with filters. Something like this maybe?
class FooController < ApplicationController
before_filter :load_foo
after_filter :save_foo
private
def load_foo
#foo = session[:foo] || 0
end
def save_foo
session[:foo] = #foo
end
end
Your actions will the be able to manipulate the value through the #count instance variable and this will be automatically persisted to session.
You could make use of the built in Rails.cache mechanism to store the value but as mentioned in the first answer you'd have to key it off something like the user_id. This is a nice way to go since you can back it with different storage mechanisms.
Rails.cache.write(:foo)
# in later action
Rails.cache.read(:foo)
One other thing you could look at is the flash hash, which provides a keep method to make the flash value last more than one subsequent request.
So in action 1 you could create the value:
flash[:foo] = some_value
flash.keep(:foo)
In action 2 you can access it, and call keep again if you want it to stay alive for more subsequent actions.
flash[:foo] #use it for something
flash.keep(:foo) # keep it for another request
It's a bit of a tricky thing to do cleanly within the context of http requests.
If it's a simple count or string, I think the best solution is to store it in the session. That way it will be there if you are using multiple web servers.
Why are you against using a session for this?
Don't worry, sessions won't bite.
Also, the session is probably the best way to do this.