NameError in using custom layout option in sinatra app - ruby

To use custom layout file in my app I'm using this following code,
set :views, File.dirname(__FILE__) + "/../views"
set :public_folder, File.dirname(__FILE__) + "/../public"
get '/' do
if !Db.empty? then
haml :home, {:layout => :nosetup-layout}
elsif request.ip == "127.0.0.1" then
haml :setup, {:layout => :nosetup-layout}
else
haml :nosetup, {:layout => :nosetup-layout}
end
end
there seems to be a problem with the layout option
I get the following error
undefined local variable or method `layout' for #<TabPlayer::Server:0x000000024509c8>
So, where did I went wrong?

Rename your layout to nosetup_layout (using an underscore).
Then just call:
haml :nosetup, {:layout => :nosetup_layout}

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.

How to custom layout directory in sinatra

I have a Sinatra app with multiple layouts, one for admin panel, and one for public. I want to isolate them into their own subdirectory in views:
views/
views/layout.erb (for public)
views/auth/
views/auth/layout.erb (for admin)
but I got error. config.ru :
require 'sinatra'
get "/" do
erb :layout
end
get "/auth" do
erb :layout => :'auth/layout'
end
First, the second "erb" call is wrong. Change it to:
erb :layout, :layout_options => { :views => 'views/auth' }
If you want use Sinatra's "Classic Style", the code you showed should not be in config.ru. Move the code from config.ru to another file, e.g. app.rb. Correcting the second "erb" call, app.rb will contain:
# app.rb
require 'sinatra'
get "/" do
erb :layout
end
get "/auth" do
erb :layout, :layout_options => { :views => 'views/auth' }
end
Run it like this: ruby app.rb. It will by default start a local server on port 4567.
Now the URLs http://localhost:4567 and http://localhost:4567/auth should work.
Remember, you should not render a layout.erb file directly. It is used to wrap your views. Check Sinatra: Getting Started for more examples.

Why doesn't the sinatra-redirect-with-flash gem work with shotgun?

I want to show flash messages using sinatra-redirect-with-flash gem.
Here's my ruby code:
require 'sinatra'
require 'sinatra/base'
require 'sinatra/flash'
require 'sinatra/redirect_with_flash'
require 'data_mapper'
require 'haml'
require 'builder'
# ...
class App < Sinatra::Base
enable :sessions
register Sinatra::Flash
helpers Sinatra::RedirectWithFlash
use Rack::MethodOverride
get '/' do
#notes = Note.all :order => :id.desc
#title = 'All TODOs'
if #notes.empty?
flash.now[:error] = 'No TODOs found. Add your first below.'
end
haml :home
end
post '/' do
n = Note.new
n.content = params[:content]
n.created_at = Time.now
n.updated_at = Time.now
if n.save
redirect '/', :notice => 'TODO saved successfully.'
else
redirect '/', :error => 'Failed to save TODO.'
end
end
# ...
end
And views/layout.haml is:
!!! 5
%html{:lang => "en"}
%head
%meta{:charset => "utf8"}
%body
%header
%hgroup
%h1
%a{:href => "/"}= SITE_TITLE
%h2= SITE_DESCRIPTION
#main
=styled_flash
=yield
After adding a TODO successfully, I expected to see the flash message 'TODO saved successfully.' on the home page. But no flash messages are shown after redirection when I run my app using shotgun. Flash messages are shown well when I run ruby app.rb or rackup.
How can I solve this problem?
Another problem is also happening when I run the app using shotgun. In get '/' method, if I use flash[:error] instead of flash.now[:error], the flash message doesn't show up on the page.
I am shadowning this tutorial, but I made some differences:
erb -> haml
Classic Sinatra app -> Subclassing Sinatra::Base
rack-flash -> sinatra-flash
You can browse whole codes here.
Thanks for any answers/comments.
The shotgun gem reloads Sinatra after every request. The README says:
Each time a request is received, it forks, loads the application in
the child process, processes the request, and exits the child process. The
result is clean, application-wide reloading of all source files and templates on
each request.
As a result, you will need some sort of mechanism to preserve state between requests that doesn't rely on data stored in each child process.

Is it possible to to rewrite the base URL in Sinatra?

Is it possible to to rewrite the base URL?
E.g. instead of www.host.com/ to use www.host.com/blah/ as
a base url and so:
get '/' do
...
end
would work for www.host.com/blah/
I could append to all my routes '/blah/..' but any gems etc.
would fail to work as well.
This can be done in Rails easily and I would like to have it in Sinatra as well.
I use a rack middleware for this rack-rewrite and I am quite happy with it :)
use Rack::Rewrite do
rewrite %r{^/\w{2}/utils}, '/utils'
rewrite %r{^/\w{2}/ctrl}, '/ctrl'
rewrite %r{^/\w{2}/}, '/'
end
EDIT:
Not sure if I understand your problem, but here are a config.ru file
# encoding: utf-8
require './config/trst_conf'
require 'rack-flash'
require 'rack/rewrite'
use Rack::Session::Cookie, :secret => 'zsdgryst34kkufklfSwsqwess'
use Rack::Flash
use Rack::Rewrite do
rewrite %r{^/\w{2}/auth}, '/auth'
rewrite %r{^/\w{2}/utils}, '/utils'
rewrite %r{^/\w{2}/srv}, '/srv'
rewrite %r{^/\w{2}/}, '/'
end
map '/auth' do
run TrstAuth.new
end
map '/utils' do
run TrstUtils.new
end
map '/srv' do
map '/tsk' do
run TrstSysTsk.new
end
map '/' do
run TrstSys.new
end
end
map '/' do
run TrstPub.new
end
and an example Sinatra::Base subclass
# encoding: utf-8
class TrstAuth < Sinatra::Base
# Render stylesheets
get '/stylesheets/:name.css' do
content_type 'text/css', :charset => 'utf-8'
sass :"stylesheets/#{params[:name]}", Compass.sass_engine_options
end
# Render login screen
get '/login' do
haml :"/trst_auth/login", :layout => request.xhr? ? false : :'layouts/trst_pub'
end
# Authentication
post '/login' do
if user = TrstUser.authenticate(params[:login_name], params[:password])
session[:user] = user.id
session[:tasks] = user.daily_tasks
flash[:msg] = {:msg => {:txt => I18n.t('trst_auth.login_msg'), :class => "info"}}.to_json
redirect "#{lang_path}/srv"
else
flash[:msg] = {:msg => {:txt => I18n.t('trst_auth.login_err'), :class => "error"}}.to_json
redirect "#{lang_path}/"
end
end
# Logout
get '/logout' do
session[:user] = nil
session[:daily_tasks] = nil
flash[:msg] = {:msg => {:txt => I18n.t('trst_auth.logout_msg'), :class => "info"}}.to_json
redirect "#{lang_path}/"
end
end
maybe this helps :) full source on github.
In a before block you can edit env['PATH_INFO]`; Sinatra will then use the edited value for routing.
For your example, something like this might work...
before do
env['PATH_INFO'].sub!(/^\/blah/, '')
end
I agree with the other answers that using a middleware component is a more robust solution but if you want something concise and simple, that works inside the Sinatra app instead of via config.ru, then munging the Rack environment is not bad.
You could have a look at https://github.com/josh/rack-mount, maybe that one can help you out?

Ruby Rack - mounting a simple web server that reads index.html as default

I'm trying to get some information from this tutorial: http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder
basically I want to have a file config.ru that tell rack to read the current directory so I can access all the files just like a simple apache server and also read the default root with the index.html file...is there any way to do it?
my current config.ru looks like this:
run Rack::Directory.new('')
#this would read the directory but it doesn't set the root to index.html
map '/' do
file = File.read('index.html')
run Proc.new {|env| [200, {'Content-Type' => 'text/html'}, file] }
end
#using this reads the index.html mapped as the root but ignores the other files in the directory
So I don't know how to proceed from here...
I've also tried this following the tutorials example but thin doesn't starts properly.
builder = Rack::Builder.new do
run Rack::Directory.new('')
map '/' do
file = File.read('index.html')
run Proc.new {|env| [200, {'Content-Type' => 'text/html'}, file] }
end
end
Rack::Handler::Thin.run builder, :port => 3000
Thanks in advance
I think that you are missing the the rackup command. Here is how it is used:
rackup config.ru
This is going to run your rack app on port 9292 using webrick. You can read "rackup --help" for more info how you can change these defaults.
About the app that you want to create. Here is how I think it should look like:
# This is the root of our app
#root = File.expand_path(File.dirname(__FILE__))
run Proc.new { |env|
# Extract the requested path from the request
path = Rack::Utils.unescape(env['PATH_INFO'])
index_file = #root + "#{path}/index.html"
if File.exists?(index_file)
# Return the index
[200, {'Content-Type' => 'text/html'}, File.read(index_file)]
# NOTE: using Ruby >= 1.9, third argument needs to respond to :each
# [200, {'Content-Type' => 'text/html'}, [File.read(index_file)]]
else
# Pass the request to the directory app
Rack::Directory.new(#root).call(env)
end
}
I ended up on this page looking for a one liner...
If all you want is to serve the current directory for a few one-off tasks, this is all you need:
ruby -run -e httpd . -p 5000
Details on how it works: http://www.benjaminoakes.com/2013/09/13/ruby-simple-http-server-minimalist-rake/
You can do this using Rack::Static
map "/foo" do
use Rack::Static,
:urls => [""], :root => File.expand_path('bar'), :index => 'index.html'
run lambda {|*|}
end
For me, using Ruby 2.0 and Rack 1.5.2, sinm solution worked for serving the index page (both as default page for root and loaded explicitly), but for other files I obtained errors similar to the following:
Rack::Lint::LintError: Status must be >=100 seen as integer
I combined sinm solution with this SO answer and the snippet found on Heroku documentation to obtain the desired behavior (assuming that the entire site is contained in a folder called public):
use Rack::Static,
:urls => ["/images", "/js", "/css"],
:root => "public",
:index => 'index.html'
run Rack::File.new("public")
My example for doing the exact same below:
module Rack
class DirectoryIndex
def initialize(app)
#app = app
end
def call(env)
index_path = ::File.join($documentRoot, Rack::Request.new(env).path.split('/'), 'index.html')
if ::File.exists?(index_path)
return [200, {"Content-Type" => "text/html"}, [::File.read(index_path)]]
else
#app.call(env)
end
end
end
end
require 'rack_directory_index.rb'
$documentRoot = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'build'))
Capybara.app = Rack::Builder.new do |builder|
puts "Creating static rack server serving #{$documentRoot}"
use Rack::DirectoryIndex
run Rack::Directory.new($documentRoot)
end
Capybara.configure do |config|
config.run_server = true
end
The solution is mostly a copy and paste from different answers but it works fine. You can find it as a gist here aswell, good luck

Resources