I am using Rails 4.1.rc2. It's the soon going to be launched Rails 4.1 there has been a change with the ways sessions are serialized now commit here and link to guide.
I am no more able to store my object in the session eq
session[:user] = User.new
when i access session[:user] i get a String.
Any way to revert back to old one ? Or store object in Rails 4.1 session
# app/controllers/application_controller.rb
helper_method :session_user
def session_user
#session_user ||= Marshal.load(session[:user])
end
def set_session_user(user)
session[:user] = Marshal.dump(user)
end
# set
set_session_user(User.new)
# reconstitute and perform operation
session_user.awesome = 'rad'
session_user.save!
see http://www.ruby-doc.org/core-2.1.1/Marshal.html
Related
I created a simple authentication for Sinatra, however the session object seems to be cleaning up ALL custom keys. For example, when user logs in:
session[:user_id] = current_user.id
This is effectively stored in the session object for the current request. When a new request occurs the session[:user_id] is not there anymore. Session is active, cookies are enabled. I tried everything I can't figure out what it wrong (here is the all the relevant code: https://gist.github.com/ksiomelo/7656296).
application:
use Rack::Session::Cookie , :secret => "82e042cd6fde2bf1764f777236db799e"
enable :sessions # for flash messages
helpers:
def require_auth
unless session[:user_id]
flash[:error] = "You need to be logged in."
redirect to("/login")
end
end
def current_user
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
end
signin:
authorized_user = User.authenticate(params[:email],params[:password])
if authorized_user
# update session / redirect
session[:user_id] = authorized_user.mongo_id.to_s
session.options[:expire_after] = 2592000 unless params[:remember].nil? # 30 days
# redirect to the wizard
flash[:info] = "Welcome back #{authorized_user.first_name}"
redirect to("/home")
You should likely try to set a provider for session handling, e. g.:
use Rack::Session::Pool, :expire_after => 2592000
Glad to help.
I have a rails 3.1 app running on heroku.
I need to provide the user with the ability to download csv data.
I'm trying to stream the data, but it is all sent in one go.
Which for larger requests will timeout.
There is much talk on the heroku site about streaming and chunking
but as far as I can tell thin collects all the data and sends it in one go.
How do I get it to work?
Do I have to add some middleware? e.g. unicorn
The code streams fine running with mongrel.
I'm pretty sure you just need to add
stream
to the top of your controller.
More info on HTTP streaming can be found on RailsCasts: http://railscasts.com/episodes/266-http-streaming
This question is really old but the issue is still very common because of the 30'' limit in Heroku responses so I will add some code on how I achieved it. Works with Rails 5.2 & 6.1 on Heroku with Puma server.
I'm using #send_stream method (present only in edge rails, future rails 7) so I just copied it + set the Last-Modified header manually. Added all in a rails concern to reuse it.
module Streameable
extend ActiveSupport::Concern
include ActionController::Live
def send_stream(filename:, disposition: 'attachment', type: nil)
response.headers['Content-Type'] =
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
'application/octet-stream'
response.headers['Content-Disposition'] =
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename) # for Rails 5, use content_disposition gem
# extra: needed for streaming correctly
response.headers['Last-Modified'] = Time.now.httpdate
yield response.stream
ensure
response.stream.close
end
end
class ExporterController < ApplicationController
include Streameable
def index
respond_to do |format|
format.html # index.html
format.js # index.js
format.csv do
send_stream(attachment_opts) do |stream|
stream.write "email_address,updated_at\n"
50.times.each do |i|
line = "user_#{i}#acme.com,#{Time.zone.now}\n"
stream.write line
puts line
sleep 1 # force slow response for testing respose > 30''
end
end
end
end
end
private
def attachment_opts
{
filename: "data_#{Time.zone.now.to_i}.csv",
disposition: 'attachment',
type: 'text/csv'
}
end
end
Then, if you use something like curl you will see the output generated second by second.
$ curl -i http://localhost:3000/exporter.csv
An important thing is to write your code to iterate the data with #each, by using the Enumerable module. Oh, a tip with ActiveRecord, use #find_each so the DB fetch is in batches.
I'm using a JRuby(latest, 1.7 RC2)/Sinatra mix on the Trinidad server, for background info.
get "/" do
#user = session[:user] || [3,2]
puts session[:user]
haml :home
end
get "/signed_in" do
"#{session[:loggedin]}"
end
post "/signup" do
user = User.create(:username => params[:username], :password => Digest::SHA1.hexdigest(params[:password]))
session[:user] = user
session[:loggedin] = true
puts session[:user]
end'
What I expect as output is (with comments):
# blank line
#<User:0x4049839a>
#<User:0x4049839a>
But what I really get is:
# blank line
#<User:0x4049839a>
# blank line
And after the post, /signed_in will still have no value to output, when it really should be true.
Why aren't these values being kept? No, I'm not using shotgun, I do have sessions enabled, and I do have a session secret set up.
You cannot keep DataMapper resources in session variables. Instead put the key to your user object in your session variable and use a before helper to set #user = User.first(session[:user]) and use the #user instance variable throughout your application
Also, you do not need the session[:loggedin] entry, just use #user.nil?
It's the first time I'm working with Sinatra and I just can't get sessions to work in my tests. I have enable :sessions in my app.
I tried:
get "/controller/something", {}, "rack.session" => {:session => "Aa"}
or
get "/controller/something", {}, "session" => {:session => "Aa"}
But no session variables are being set in my request. I've looked around the web and tried several suggestions but nothing seems to work. Am I missing something?
Thanks!
Rack doesn't support passing in sessions via the request anymore (Rack >= v1.0). Read this post for more detailed information on that.
The best way to set a session variable in your app is to call an action inside of your application that will set the session variable. For instance, if you have a route inside your app that sets a session variable like this:
post '/set_sess_var/:id'
session[:user_id] = params[:id]
end
Let's pretend there's another route that you actually wanted to test which is using the session variable like this:
get '/get_user_attributes'
User.find(session[:user_id]).attributes
end
Then in your tests, you should first call the route which sets the session, then go onto another route which uses it. Here is rspec notation, since that is what I use for testing:
it "should print out user attributes" do
user_id = 1
post '/set_sess_var/' + user_id
get '/get_user_attributes'
last_response.body.should == User.find(user_id).attributes
end
If you were going to be using the route frequently in your tests, then you could write a method to accomplish this in your test file (if you're using Rspec, then this method could go in your spec_helper.rb or in your controller_spec.rb file):
def set_session_var(user_id)
post '/set_sess_var/' + user_id
end
and then call it in your tests when you needed it to be set:
it "should print out user attributes" do
set_session_var(1)
get '/get_user_attributes'
last_response.body.should == User.find(1).attributes
end
You need to use the keys that will end up in env:
get "/controller/something", {}, "rack.session" => {:session => "Aa"}
I'm looking for a ruby gem (or rails plugin) which abstracts the details of memcached in the same way that ActiveRecord abstracts the details of SQL. I am NOT looking for something to help cache ActiveRecord models in memcached. I'm sure there are approximately 4215 gems that will help with that problem.
Ideally what I'd like is to be able to do something like:
class Apple < MemcachedModel
# whatever else here
end
and then be able to do stuff like:
my_apple = Apple.find('some memcached key')
which would look up the JSON representation of this class in memcached and deserialize it. I'd also maybe be able to do things like:
my_apple.color = "red"
# persist changes back to memcached
my_apple.save
# load any changes from memcached into local model
my_apple.update
It seems like someone must have scratched this itch by now and created something along these lines, but whenever I google for such a gem I just keep turning up thing which help cache AR models using memcached.
You can take a look at my moneta gem, which is an ORM'ish thing for all kinds of key-value-stores. You can see it at: http://github.com/wycats/moneta/tree/master
The basic idea behind moneta is that all KVSs should behave exactly like a subset of normal Ruby hashes. We support:
#[]
#[]=
#delete
#fetch
#key?
#store
#update_key
#clear
The store and update_key methods take an additional options hash which you can use thusly:
cache = Moneta::Memcache.new(:server => "localhost:11211", :namespace => "me")
cache.store("name", "wycats", :expires_in => 2)
cache.update_key("name", :expires_in => 10)
We support a large number of KVSs:
BerkeleyDB
CouchDB
DataMapper (which means any store supported by DM)
Files
LMC
Memcache
In-process memory
MongoDB
Redis
Tokyo Cabinet
Tokyo Tyrant
S3
SDBM
Files using XAttrs
Every store supports expiry, either natively (like in memcached) or using a standard module that emulates memcache-style expiry. The API is always identical and there is a shared spec that all adapters are run against to ensure compliance.
It is also quite easy to add your own adapter, which is why so many exist.
I don't know about any Ruby ActiveRecord-like adapter for Memcached. A similar library would probably be hard to create because Memcached doesn't act as a relational database.
The result is that the library wouldn't be able to implement about the 80% of the features supported by ActiveRecord, so what's the benefit of such an implementation?
You already have everything you need in Rails to work with memcache with a "CRUD" pattern.
Rails.cache.read('key')
Rails.cache.write('key', 'value')
Rails.cache.delete('key')
Rails.cache.increment('key', 5)
Rails.cache.fetch('key') { 'value' }
If you feel more comfortable, you can create a wrapper and proxy these methods with corresponding new/create/update/save/destroy methods. However, you would never be able to go beyond a basic CRUD system just because Memcached is not intended to be a relational database.
It's fairly easy to implement.
require 'ostruct'
require 'active_support/cache'
class StoredStruct < OpenStruct
attr_writer :store
def self.store
#store || superclass.store
end
def self.expand_key(key)
'StoredStruct_' + (superclass == OpenStruct ? '' : "#{self}_") + key.to_s
end
def self.get_unique_id
key = expand_key('unique_id')
store.write(key, 0, :unless_exist => true)
store.increment(key)
end
def self.save(instance)
id = instance.id || get_unique_id
store.write(expand_key(id), instance)
id
end
def self.find(id)
store.read(expand_key(id))
end
attr_reader :id
def attributes
#table
end
def attributes=(hash)
#table = hash
end
def new_record?
self.id.nil?
end
def save
#id = self.class.save(self)
true
end
def reload
instance = self.class.find(self.id)
self.attributes = instance.attributes unless self == instance
self
end
end
Use it like this:
# connect to memcached
StoredStruct.store = ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
class Apple < StoredStruct
end
fruit = Apple.new
fruit.color = "red"
fruit.taste = "delicious"
fruit.id
#=> nil
fruit.save
#=> true
fruit.id
#=> 1
# to load any changes:
fruit.reload
Apple.find(1)
#=> fruit
As Simone Carletti wrote, Memcached isn't a relational database; it can't even list all its keys. As such, any ActiveRecord-like model storing data in Memcached will not contain all the functionality of ActiveRecord. Nonetheless, I think there is some value in having a consistent API for all your models, so if it makes sense to have one of your models store its data in Memcached, you can use this module I created for that purpose:
http://blog.slashpoundbang.com/post/1455548868/memcachemodel-make-any-ruby-object-that-persists-in
You may be looking for Nick Kallen's cache-money.