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!!!
Related
I am trying to reduce unnecessary calls to the Shopify API from a controller that inherits from ShopifyApp::AuthenticatedController, for example to get the myshopify_domain:
myshopify_domain = ShopifyAPI::Shop.current.myshopify_domain
Is there some method in ShopifyApp::SessionRepository or somewhere else in the ShopifyApp that I can call to retrieve Shop.current.myshopify_domain without making an actual call to the Shopify API webservice? If not, can I store the myshopify_domain, once retrieved, in the ShopifyApp::SessionRepository?
If you are in the AuthenticatedController, dump the following to the console:
session.to_json
You will see that you can access all sorts of stuff about the current session, such as:
session["shopify_domain"]
session["shop_id"]
I had the same problem with a muli-store app, where I needed to pull data tied to a specific store. ShopifyAPI::Shop.current.myshopify_domain is redundant in that you are slowing down the controller waiting for Shopify's response, and you are tinking down the api bucket limit. The session object is the superior method to avoid all of that, and should be accessible from any controller which inherits the ShopifyApp::Authenticated controller.
Your question is confusing. At the point where you are doing calls to the API, you clearly already know the myshopify_domain, as you cannot do API calls with that the shops name and token.
So now we're past that point, and you are asking how you can somehow have the myshopify_domain be more convenient for you to use? Just make yourself a little helper so that when you open a session, you have access to shop_name or whatever you want.
Shopify always sends you shop name in their requests, so you're covered there as it's a param, and your own interface code and calls will also be setting up the shop name too, so you're really now into some pretty esoteric territory to need anything else.
Seem like you're caught in a classic "the dog chasing its own tail", but why?
The myshopify_domain is usually available in the session parameters:
if !session[:myshopify_domain].nil? && !session[:myshopify_domain].empty?
session[:myshopify_domain]
else
session[:myshopify_domain] = ShopifyAPI::Shop.current.myshopify_domain
end
I'm working on a wizard - a PORO module - for user's information. The goal is to forward the users to said wizard for him to complete his user profile if he attempts to make a booking (or similar action) with an incomplete profile.
After the last step of the wizard I would like to redirect the user back to the action he intended initially. For that I'm saving a session variable named ":user_return_to_from_wizard".
I can't seem to access the session variable from the PORO module. What can I do?
I tried of course
session[:user_return_to_from_wizard]
but that doesn't work. I guess something like
App::Application.session....
You probably can't and should'nt - one of nice things of using PORO is to be able to use them outside of a specific context (HTTP and session in your case). What I would do is collect the information you need from the session and use it to initialize your wizard:
class SomeController < ApplicationController
def index
return_url = session[:user_return_to_from_wizard]
wizard = Wizard.new(return_url: return_url)
...
end
end
This way you can still test/use Wizard independently and don't have to rely on session inside.
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.
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
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.