Setting Sinatra template options works locally, broken on Heroku - ruby

My Sinatra web site uses an admittedly ghetto way of switching between two languages, which works perfectly on my local machine, but not on Heroku.
My app.rb is as follows:
require 'sinatra'
get '/' do
set :erb, :views => settings.views + "/en"
erb :index, :layout => false, :views => settings.views
end
get '/tr' do
set :erb, :views => settings.views + "/tr"
redirect to('/home')
end
get '/en' do
set :erb, :views => settings.views + "/en"
redirect to('/home')
end
get '/home' do
erb :home
end
get '/products' do
erb :products
end
...
When I click on the links that go to the /en and /tr urls, I am correctly redirected to the /home link, but the views folder does not change.
My gems are the same versions in both environments:
rack (1.4.1)
rack-protection (1.2.0)
tilt (1.3.3)
sinatra (1.3.3)
bundler (1.3.2)
Link to the site
Help, please.

I suggest replacing your admittedly ghetto way with something more robust like the i18n gem
here is a tutorial
And instead of using the browser's language (in example) you could do something like this in a before filter
before do
case request.path_info
when /^\/tr/ then set :locale, "tr"
else set :locale, "en"
end
end
and in helpers
helpers do
def get_locale
settings.locale
end
# other helpers from example
end
That way you only need one copy of your view templates, reducing the complication of changing all views when you only need to change one element (for example)
If you want to test your sinatra app like it will act on heroku you can throw on a RACK_ENV=production before starting your server. Ex. RACK_ENV=production ruby my_app.rb

Related

Sinatra I18n Fallbacks using Rack::Locale

I'm trying to set up a simple Sinatra app with I18n, following the recommended Sinatra recipe, and using Rack:Locale to determine the language.
My app.rb:
require 'rubygems'
require 'sinatra'
require 'rack/contrib'
require 'i18n'
require 'i18n/backend/fallbacks'
require 'tilt/haml'
use Rack::Locale
configure do
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
I18n.load_path = Dir[File.join(settings.root, 'locales', '*.yml')]
I18n.backend.load_translations
end
helpers do
def t(*args)
I18n.t(*args)
end
end
get '/' do
haml :index
end
My locales/en.yml:
en:
welcome: "Welcome!"
When I run rackup and visit the root path of my Sinatra app, I get the following:
I18n::InvalidLocale at /
"en-US" is not a valid locale
file: i18n.rb location: enforce_available_locales! line: 284
I thought that the I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) would handle this, by not finding en-US and falling back to en (which I have), but apparently not. What am I missing?
Add:
I18n.enforce_available_locales = false

Sinatra app with multiple controllers: how do I tell rspec what the routes are?

I'm working on an existing sinatra app that spans a number of controllers. These are currently routed in the config.ru file like so:
require 'bundler'
Bundler.require(:default)
=begin
# The gem file includes these:
gem 'rack' , '1.5.2', :groups => [:default, :test]
gem 'rack-accept' , '0.4.5'
gem 'rack-mount' , '0.8.3'
gem 'rack-protection' , '1.5.1'
gem 'rack-test' , '0.6.2', :groups => [:test]
=end
use Rack::ContentType
maps = {
'/' => RootController,
'/users' => UsersController,
'/animals' => AnimalsController,
}
maps.each do |path, controller|
map(path){ run controller}
end
This file is launched via config.ru (under Thin). But test files don't run under rackup (or Thin).
How do I load the controllers under rspec?
The problem is that when I put a 'get "/PATH"' or 'post' in my tests, Ruby complains about an
argument mismatch (2 for 0). If the 'get' has no argument, Ruby gives a different argument
mismatch (0 for 1). They're both sort of wrong -- get takes a path, an optional hash, and an optional block.
So something is clearly not wired up correctly.
Some of the code is here:
config.ru: http://pastie.org/8673836
spec_helper.rb at http://pastie.org/8673835
Error message at http://pastie.org/8673793
Simple controller at http://pastie.org/8673785
The names are slightly different in the pasties, but the gist is the same.
How do you wire up the controllers when you don't have the environment
config.ru gives you?
Thanks for any help.
The problem is somewhere in the code base, but rack doesn't make it easy to determine where.
I filed a bug on this at
https://github.com/rack/rack/issues/652

Ruby URI module oddness in modular Sinatra app

I'm having trouble using the Ruby URI module's encode_www_form method in a modular Sinatra app. For some reason, URI is interpreted as being the URI::Parser subclass, and so the method call understandably fails.
I've reduced this to a minimal test case. The Gemfile:
source 'https://rubygems.org'
ruby '1.9.3'
gem 'sinatra'
And app.rb:
require 'sinatra/base'
class Frontend < Sinatra::Base
get '/test/' do
URI.encode_www_form(:a => 1, :b => 2)
end
run! if app_file == $0
end
If I then run ruby app.rb and access /test/ I get:
NoMethodError - undefined method `encode_www_form' for #<URI::Parser:0x007fa9221ca868>:
app.rb:6:in `block in <class:Frontend>'
If I convert it to a classic-style Sinatra app, so that app.rb is like this:
require 'sinatra'
get '/test/' do
URI.encode_www_form(:a => 1, :b => 2)
end
Then call ruby app.rb and access /test/, the page shows "a=1&b=2" as desired.
So what's going wrong in the modular format that means something's up with URI?
The class Sinatra::Base redefines URI on line 856 of https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb, which is why your URI reference gets evaluated as that value.
If you want to avoid this problem, you can change your reference to ::URI.
As of Sinatra 1.4.4, the URI module is no longer overwritten.
I tried to reproduce this in irb. This may sound stupid, but require 'uri' did the trick there.

Are my assets being served from AWS S3

Maybe a really daft question and please dont mark me down for this, but I finally got Heroku to compile its static assets in my S3 bucket with asset_sync.
Now how do i know that the assets are in fact being served from there, I take it theres no magic going on that pulls them in from s3? I have to set the path for each asset prefixed with
https://s3-eu-west-1.amazonaws.com/pathto/asset
Is there a way to set this in sinatra explicitly, I don't have to manually change every asset do I? that would be silly.
The asset_sync docs say to use this in rails
config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
but im not sure how to set this in sinatra
EDIT
require 'bundler/setup'
Bundler.require(:default)
require 'active_support/core_ext'
require './config/env' if File.exists?('config/env.rb')
require './config/config'
require "rubygems"
require 'sinatra'
configure :development do
AssetSync.config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
end
get '/' do
erb :index
end
get '/about' do
erb :about
end
This gives the following error in the console
undefined method `action_controller' for #<AssetSync::Config:0x24d1238> (NoMethodError)
Try putting it in Sinatra's configure block via the Async Built-in initializer, e.g:
configure :production do
AssetSync.config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
end
It's possible you'll have to call AssetSync.sync as well at some point, I'm not sure.
Edit: using a configure block.
If you were using a modular app (if not, it's not any different, just remove the class bits)
class App < Sinatra::Base
configure :development do
set :this, "and that"
enable :something
set :this_only, "gets run in development mode"
end
configure :production do
set :this, "to something else"
disable :something
set :this_only, "gets run in production"
# put your AssetSync stuff in here
end
get "/" do
# …
end
get "/assets" do
# …
end
post "/more-routes" do
# …
end
# etc
end
See the link I added above for more.
action_controller is part of Rails. To prefix the path, the best thing you could do is use a helper:
helpers do
def aws_asset( path )
File.join settings.asset_host, path
end
end
configure :production do
set :asset_host, "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
end
configure :development do
set :asset_host, "/" # this should serve it from the `public_folder`, add subdirs if you need to.
end
Then in a route or a view you can do something like this:
aws_asset "sprite_number_1.jpg"
To use with ERB and sinatra-static-assets's image_tag:
image_tag( aws_asset "sprite_number_1.jpg" )
or combine them (this may not work as the image_tag helper might not be seen in the scope of the helper, it's easier to try it than to think about it):
helpers do
def aws_image( path )
image_tag( File.join settings.asset_host, path )
end
end
# in your view
aws_image( "sprite_number_1.jpg" )
I'm sure there'll be an easier way to do this but this will do for a quick and dirty solution.

Sinatra HAML Heroku in-file templates

I'm following a tutorial from http://ruby.about.com/od/sinatra/a/sinatra7_2.htm however I'm having a few problems running the app within my own environment.
The problem is that the following line:
haml :list, :locals => { :cs => Contact.all }
results in a "No such file or directory - [...]/views/list.haml"
The HAML template is within the file, and terminated by:
__END__
## layout
however ruby seems to be looking in the views/ directory for the Haml files.
Is this tutorial missing a call to force ruby to look inside the file, or this resource suggests that in-file templates are broken for version 1.9.2.
I'm using sinatra version 1.1.2 and ruby 1.8.7.
I can't reproduce with Sinatra 1.1.2 and Ruby 1.9.2.
So something like this (sinatrarb.com) example generates the error?
require 'sinatra'
get '/' do
haml :index
end
__END__
## layout
%html
= yield
## index
%div.title Hello world!!!!!
calling a list template you will also need add it to the end of your file:
require 'sinatra'
get '/' do
haml :list
end
__END__
## layout
%html
= yield
## list
%div.title the LIST

Resources