Normally I wouldn't store objects in a Rails session but I'm using a library that requires this. I've run into a very strange issue where a stored object appears as a String after redirect.
To reproduce I've created a sample Rails 4.1 app
$ rails new session-test
Added a test controller:
class HomeController < ApplicationController
def index
logger.debug "session[:customer]: #{session[:customer]}"
logger.debug "session[:customer].name: #{session[:customer].name}"
end
def from
Struct.new 'Customer', :name, :address
session[:customer] = Struct::Customer.new 'Dave', '123 Main'
redirect_to :action => :index
end
end
setup routes:
Rails.application.routes.draw do
get 'home/index'
get 'home/from'
root 'home#index'
end
Then I fire up Rails
$ bundle exec rails server
and hit localhost:3000/home/from in the browser:
Started GET "/home/from" for 127.0.0.1 at 2014-04-09 21:20:25 -0700
Processing by HomeController#from as HTML
Redirected to http://localhost:3000/home/index
Completed 302 Found in 18ms (ActiveRecord: 0.0ms)
Started GET "/home/index" for 127.0.0.1 at 2014-04-09 21:20:25 -0700
Processing by HomeController#index as HTML
session[:customer]: #<struct Struct::Customer name="Dave", address="123 Main">
Completed 500 Internal Server Error in 2ms
NoMethodError (undefined method `name' for "#<struct Struct::Customer name=\"Dave\", address=\"123 Main\">":String):
app/controllers/home_controller.rb:4:in `index'
I have no idea why this object is getting translated as a String...
It appears to have to do with the session store type of cookie_store because if I change
session_store.rb from
Rails.application.config.session_store :cookie_store, key: '_session-test_session'
to
Rails.application.config.session_store :cache_store
it works!
Any ideas?
You can't store objects in the Rails session. It's a key-value store that only accepts strings because, more often than not, it's packaged up and sent to the client as an encrypted cookie.
It's not a dumping ground for things you might need. Pay attention to how much junk you cram in there because the more you lean on the session, the larger the cookie is that the client will have to sling back to your server for every request.
It's worth observing the headers in your browser's network inspection tool to see how heavy a footprint your requests have.
If you really do need to persist something in there, use a string-friendly encoding format like JSON to be sure you can get the data back out in a usable format.
I'd be very hesitant to use the cache_store
as well, that doesn't get shared across different instances of your application. Ruby objects only exist within the context of a single process, so other requests, which more often than not will hit some random process, will not be able to make use of this as easily.
The default cookie store is the most reliable. The others that share between processes are dependent on additional services being operational (Memcached, Redis, etc.) but most of those dictate a strings-only policy as well.
Fix is to perform serialization and deserialization explicitly, manually.
e.g.
# storing...
session[:customer] = (Struct::Customer.new 'Dave', '123 Main').to_yaml
# retrieving...
customer = YAML.load(session[:customer])
For shopify_app
gem, see file changes in pull request https://github.com/Shopify/shopify_app/pull/90/files and apply accordingly to your existing app.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With