Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object stored in Rails session becomes a String?

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?

like image 628
Zack Chandler Avatar asked Apr 10 '14 04:04

Zack Chandler


2 Answers

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.

like image 69
tadman Avatar answered Sep 28 '22 09:09

tadman


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.

like image 27
choonkeat Avatar answered Sep 28 '22 10:09

choonkeat