Sinatra session not preserved with Rack::FiberPool - ruby

The session is not preserved between requests, though I can't see what I'm doing wrong. Code!
require 'sinatra'
require 'rack/fiber_pool'
class SessionTest < Sinatra::Base
use Rack::FiberPool
enable :sessions
set :session_secret, "foobar"
get '/' do
body { session.inspect } #This is always '{}'!
end
get '/a' do
session['user'] = "bob"
redirect '/'
end
end
run SessionTest.new

Try this instead:
require 'sinatra'
require 'rack/fiber_pool'
class SessionTest < Sinatra::Base
enable :sessions
set :session_secret, "foobar"
get '/' do
body { session.inspect } #This is always '{}'!
end
get '/a' do
session['user'] = "bob"
redirect '/'
end
end
use Rack::FiberPool
run SessionTest.new
Otherwise Sinatra will set up the fiber pool after the session middleware, which doesn't work. This is not a bug but caused by the way Rack::FiberPool works.

Turns out replacing enable :sessions with use Rack::Session::Cookie is enough to make it work.
But why!?

Related

Can I create global variables that work accross routes in Sinatra (Ruby)?

I'm trying to make a ruby class manage most of what's going on in my application, and I intend to manage its params through the erb with embeded Ruby Code. I picture it goes something like this, but it's obviously not working:
require 'sinatra'
require './models/questionaire_manager'
set :bind, '0.0.0.0'
set :port, ENV['PORT']
enable :sessions
set :session_secret, 'SecretString#!$%'
get '/' do
#questionaire=Questionaire_Manager.new 0
erb :index
end
post '/' do
session[:number]=params[:number]
redirect '/quiz'
end
get '/quiz' do
#questionaire.number=session[:number]
#questionaire.genQuestionaire
erb :quiz
end
post '/quiz' do
redirect'/results'
end
get '/results' do
#number=session[:number]
erb :results
end
I guess I should also say I can't get the hang of sessions and session params, and since Sinatra's page has been down for almost a week now, I really cannot check it out.
Try something like this maybe?
require 'sinatra'
require './models/questionaire_manager'
set :bind, '0.0.0.0'
set :port, ENV['PORT']
enable :sessions
set :session_secret, 'SecretString#!$%'
helpers do
def quiz_manager
#questionaire = session[:quiz_manager] ||= Questionaire_Manager.new 0
end
end
get '/' do
# Uncomment the line below if you intend to create a new quiz each time
# session[:quiz_manager] = nil
quiz_manager # Initializes the session variable
erb :index
end
post '/' do
quiz_manager.number = params[:number]
redirect '/quiz'
end
get '/quiz' do
quiz_manager.genQuestionaire
erb :quiz
end
post '/quiz' do
redirect '/results'
end
get '/results' do
#number = quiz_manager.number
erb :results
end
Edit:
To clarify what this is doing -- I've created a helper method called quiz_manager that initializes session[:quiz_manager] if it hasn't already been set - This will persist between routes. I'm also setting the class variable #questionnaire so that you can access it within your views.

Errno::ENOENT at / no such file or directory

I'm currently working on a sinatra app and im having a little problem.
i'm trying to load my index.erb but sinatra cannot find the index.erb.
Here is my app.rb
require 'rubygems'
require 'sinatra'
module Registration
class HelloWorldApp < Sinatra::Base
get '/' do
erb :index
end
end
end
and this is my Code hierarchy.
It keeps on looking in the directory: Sinatra-Intro/app/views/index.erb
but my views is in the: Sinatra-Intro/views/index.erb
You need to configure your application instance, something like this should work:
require 'rubygems'
require 'sinatra'
module Registration
class HelloWorldApp < Sinatra::Base
configure do
set :public_folder , File.expand_path('../public', __FILE__)
set :views , File.expand_path('../views', __FILE__)
set :root , File.dirname(__FILE__)
set :show_exceptions, development?
# Optional: Load from external file
#YAML.load_file('path/to/config.yml').each do |k, v|
# set(k.to_sym, v)
#end
end
get '/' do
erb :index
end
end
end
Then:
bundle exec rackup
You can change the default location with the view setting. Like this:
set :views, Proc.new { File.join(root, "views") }

How to test if some specific rack middleware is being used?

To be more particular, I'm talking about sentry-raven and sinatra here. I saw examples testing sinatra applications, or middlewares. But I didn't see ones testing if some particular middleware is present. Or should I be testing behavior, not configuration (or how should I call it)?
The important thing (I'd say) is the behaviour, but if you wish to check for middleware there are 2 ways I'd suggest after taking a delve into the Sinatra source (there are possibly much easier/better ways):
The env
In the Sinatra source there's a method that uses the env to check if a middleware is already present:
# Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
# if another CommonLogger is already in the middleware chain.
class CommonLogger < Rack::CommonLogger
def call(env)
env['sinatra.commonlogger'] ? #app.call(env) : super
end
You could do the same thing in a route, e.g.
get "/env-keys" do
env.keys.inspect
end
It'll only show you the middleware if it's inserted something in env hash, e.g.
class MyBad
def initialize app, options={}
#app = app
#options = options
end
def call env
#app.call env.merge("mybad" => "I'm sorry!")
end
end
output:
["SERVER_SOFTWARE", "SERVER_NAME", "rack.input", "rack.version", "rack.errors", "rack.multithread", "rack.multiprocess", "rack.run_once", "REQUEST_METHOD", "REQUEST_PATH", "PATH_INFO", "REQUEST_URI", "HTTP_VERSION", "HTTP_HOST", "HTTP_CONNECTION", "HTTP_CACHE_CONTROL", "HTTP_ACCEPT", "HTTP_USER_AGENT", "HTTP_DNT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "GATEWAY_INTERFACE", "SERVER_PORT", "QUERY_STRING", "SERVER_PROTOCOL", "rack.url_scheme", "SCRIPT_NAME", "REMOTE_ADDR", "async.callback", "async.close", "rack.logger", "mybad", "rack.request.query_string", "rack.request.query_hash", "sinatra.route"]
It's near the end of that list.
The middleware method
There's also a method called middleware in Sinatra::Base:
# Middleware used in this class and all superclasses.
def middleware
if superclass.respond_to?(:middleware)
superclass.middleware + #middleware
else
#middleware
end
end
Call it in the class definition of a modular app and you can get the middlewares in an array:
require 'sinatra/base'
class AnExample < Sinatra::Base
use MyBad
warn "self.middleware = #{self.middleware}"
output:
self.middleware = [[MyBad, [], nil]]
There may be a way to get it from Sinatra::Application, but I haven't looked.
With help from ruby-raven guys, we've got this:
ENV['RACK_ENV'] = 'test'
# the app: start
require 'sinatra'
require 'sentry-raven'
Raven.configure(true) do |config|
config.dsn = '...'
end
use Raven::Rack
get '/' do
'Hello, world!'
end
# the app: end
require 'rspec'
require 'rack/test'
Raven.configure do |config|
logger = ::Logger.new(STDOUT)
logger.level = ::Logger::WARN
config.logger = logger
end
describe 'app' do
include Rack::Test::Methods
def app
#app || Sinatra::Application
end
class TestRavenError < StandardError; end
it 'sends errors to sentry' do
#app = Class.new Sinatra::Application do
get '/' do
raise TestRavenError
end
end
allow(Raven.client).to receive(:send).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven.client).to have_received(:send)
end
end
Or if raven sending requests is in the way (when tests fail because of raven sending requests, and not because of the underlying error), one can disable them:
Raven.configure(true) do |config|
config.should_send = Proc.new { false }
end
And mock Raven.send_or_skip instead:
...
allow(Raven).to receive(:send_or_skip).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven).to have_received(:send_or_skip)
...

Modular Sinatra App using Sinatra Reloader?

Hi if I had a 'main' sinatra file with the following code,
require 'sinatra'
require "sinatra/reloader"
class MyApp < Sinatra::Base
configure do
require "./rest/auth.rb"
register Sinatra::Reloader
also_reload '/rest/auth'
end
get '/' do
erb :home
end
end
And I wanted to put my authentication logic inside of /rest/auth.rb, how can I get /rest/auth.rb to reload in development mode? Must I put the configure block and require sinatra/reloader in every one of my controller files? I'd like for my controllers to inherit the settings of my main class. The code inside of auth.rb is as follows:
class MyApp < Sinatra::Base
set(:auth) do |*roles| # <- notice the splat here
condition do
unless logged_in?
session[:success_url] = request.path_info
redirect '/'
end
end
end
def logged_in?
current_user
end
def current_user
if session[:user_id]
u = User.find(:id=>"#{session[:user_id]}")
else
false
end
end
end
I need to restart the server for my changes to take place but I can throw that reload code in auth.rb's configure block though I wouldn't like to. Any ideas?
Try to rewrite like this
require 'sinatra/base'
require "sinatra/reloader"
class MyApp < Sinatra::Base
configure :development do
register Sinatra::Reloader
also_reload './rest/auth'
end
require "./rest/auth.rb"
get '/' do
erb :home
end
end

Sinatra session gets destroyed automatically

I'm not sure why but my session gets wiped out every request.
This is my code
require 'rubygems'
require 'sinatra'
require 'sinatra/base'
require 'haml'
require 'facebook_oauth'
class MyClass < Sinatra::Base
set :logging, true
set :sessions, true
get "/auth/facebook_callback" do
// Do some facebook login which is fine
access_token = facebookClient.authorize(:code => params[:code])
session[:access_token] = access_token.token
session[:user] = facebookClient.me.info['name']
session[:id] = facebookClient.me.info["id"]
#print session by "pp session" I can still see all the sessions
redirect '/'
end
get '/' do
#print all the sessions again. And I can't see anything. The session_id is also different
end
end
To keep sessions consistent you need to set a session secret, e.g.:
set :session_secret, 'super secret'
When it's not set sinatra generates random one on application start and shotgun restarts application before every request.

Resources