How to get Liquid template partials (includes) working with Sinatra? - ruby

I am new to ruby and I am trying to get template partials working in my Sinatra + Liquid project.
I have several template partials in my /includes directory.
How can I get all of these templates working as liquid partials so I can use them with liquid include tag?
What I actually have done:
# Sinatra First App
require 'sinatra'
require 'sinatra/config_file'
require 'liquid'
config_file 'config.yml'
# WebRick
set :run, true
set :server, %w[webrick]
# App Paths
set :root, File.dirname(__FILE__)
set :views, File.dirname(__FILE__) + '/views'
set :controlers, File.dirname(__FILE__) + '/controlers'
set :public_folder, Proc.new { File.join(root, "static") }
# Includes Folder
includes = File.dirname(__FILE__) + '/includes'
get '/' do
Liquid::Template.file_system = Liquid::LocalFileSystem.new(includes)
Liquid::Template.parse(includes).render
liquid :index, :locals => { :title => "My Sinatra App"}
end
get '/test' do
"This is the test page."
end
But I am still getting: Liquid error: Illegal template name ''.
Here is my index template:
<html>
{% include 'header' %}
<body>
<h1>{{ title }}</h1>
{{content}}
</body>
</html>
and here is the header part template:
<head>
<title>{{ title }}</title>
</head>
How can I fix it? Thanks for every response.

Of the get '/' route, it seems to me that the first line should be in a configuration block, and the second line shouldn't be needed at all as Tilt/Sinatra should take care of calling render, e.g.
configure do
set :views, File.join(File.dirname(__FILE__),'/includes')
# or just put these in the views dir
end
get '/' do
liquid :index, :locals => { :title => "My Sinatra App"}
end
or, if you want a views directory with sub-directories for partials/includes etc, something like "./views/includes", you could pass the view folder as an option, e.g.
get '/' do
liquid :index, :locals => { :title => "My Sinatra App"}, :views => File.join(File.dirname(__FILE__),'views/includes')
end
or you could try Sinatra Partial (I'm the maintainer).
Since the above didn't work (see comments), I looked at the Sinatra tests for Liquid and played around with the OP's code. I found that the following worked for me:
# ./app.rb
# Sinatra First App
require 'sinatra'
require 'sinatra/config_file'
require 'liquid'
config_file 'config.yml'
configure do
# WebRick
set :run, true
set :server, %w[webrick]
# App Paths
set :root, File.dirname(__FILE__)
set :views, File.dirname(__FILE__) + '/views'
set :controlers, File.dirname(__FILE__) + '/controlers'
set :public_folder, Proc.new { File.join(root, "static") }
Liquid::Template.file_system = Liquid::LocalFileSystem.new(File.join(File.dirname(__FILE__),'views/includes'))
end
get '/' do
liquid :index, :locals => { :title => "My Sinatra App" }
end
get '/test' do
"This is the test page."
end
#./Gemfile
source "https://rubygems.org"
gem "sinatra"
gem "liquid"
gem "sinatra-contrib"
#./views/includes/_header.liquid
<head>
<title>{{ title }}</title>
</head>
#./views/index.liquid
<h1>{{ title }}</h1>
{{content}}
#./views/layout.liquid
<html>
{% include "header" %}
<body>
{{ yield }}
</body>
</html>

Related

Change Sinatra views directory location

I'd like to structure a Sinatra application more like a Rails application with the following structure:
.
├── app
│ ├── models
│ │ └── a_model.rb
│ └── views
│  └── a_view.erb
└── app.rb
According to the documentation, it can be done by overwriting the :views setting:
:views - view template directory
A string specifying the directory where view templates are located. By default, this is assumed to be a directory named “views” within the application’s root directory (see the :root setting). The best way to specify an alternative directory name within the root of the application is to use a deferred value that references the :root setting:
set :views, Proc.new { File.join(root, "templates") }
I've setup :root and :views:
set :root, File.dirname(__FILE__)
set :views, Proc.new { File.join(root, 'app', 'views') }
# Also tried some variations like:
# set :views, 'app/views/'
# set :views, Proc.new { File.join(setting.root, 'app', 'views' }
# set :public_folder, Proc.new { File.join(root, 'app', 'views' }
# ...
class MyApp < Sinatra::Base
get '/' do
erb :a_view
end
end
But I'm always facing the same error message:
No such file or directory # rb_sysopen - /path/to/my/app/views/a_view.erb
Indeed, settings.views is evaluated to /path/to/my/app/views (instead of /path/to/my/app/app/views)
It looks like I cannot control the value of the :views (settings.views) variable. I know I could simply move the views folder to the root location.
Can anyone explain why I'm not able to control theses settings ?
Move settings inside class definition has all the settings are inherited from inside Sinatra::Base
class MyApp < Sinatra::Base
set :root, File.dirname(__FILE__)
set :views, Proc.new { File.join(root, 'app', 'views') }
get '/' do
erb :a_view
end
end
Or
class MyApp < Sinatra::Base
configure do
set :root, File.dirname(__FILE__)
set :views, Proc.new { File.join(root, 'app', 'views') }
end
get '/' do
erb :a_view
end
end

Public Directory Not Being Served With Sinatra

As the title says, I cannot get Heroku to use my public assets.
Locally, when running my app with shotgun it works. But with rackup (what Heroku uses), the css and assets 404.
I've tried a bunch of answers on here (one, two, three) but none have worked.
Here's my directory structure:
My config.ru:
require 'bundler'
Bundler.require
require File.expand_path('../config/environment', __FILE__)
run BikeShareApp
And my controller:
class BikeShareApp < Sinatra::Base
get '/' do
erb :'home/index'
end
get '/stations' do
#stations = Station.all
erb :'stations/index'
end
end
EDIT: This is how I'm referencing my assets by the way
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/overwrite.css" rel="stylesheet">
Found it.
Following this guide and putting this in my config.ru worked:
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
}
]
}

Set Charset to UTF-8 on html pages for Middleman blog site

I have a Middleman blog hosted on Heroku (http://tomgillard.herokuapp.com) and have been trying to optimise it based on google's PageSpeed recommendations. One recommendation is that I provide a character set on the site's HTML pages.
HTML pages contain the html5 <meta charset="utf-8"> in the <head> but this doesn't seem to be enough os I thought I could set it server side.
Here is my config.ru
require 'rack/contrib'
# Modified version of TryStatic, from rack-contrib
# https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/try_static.rb
# Serve static files under a `build` directory:
# - `/` will try to serve your `build/index.html` file
# - `/foo` will try to serve `build/foo` or `build/foo.html` in that order
# - missing files will try to serve build/404.html or a tiny default 404 page
module Rack
class TryStatic
def initialize(app, options)
#app = app
#try = ['', *options.delete(:try)]
#static = ::Rack::Static.new(lambda { [404, {}, []] }, options)
end
def call(env)
orig_path = env['PATH_INFO']
found = nil
#try.each do |path|
resp = #static.call(env.merge!({'PATH_INFO' => orig_path + path}))
break if 404 != resp[0] && found = resp
end
found or #app.call(env.merge!('PATH_INFO' => orig_path))
end
end
end
# Serve GZip files to browsers that support them
use Rack::Deflater
# Custom HTTP Headers
use Rack::ResponseHeaders do |headers|
headers['Charset'] = 'UTF-8'
end
#Custom Cache Expiry
use Rack::StaticCache, :urls => ["/img", "/css", "/js", "/fonts"], :root => "build"
# Attempt to serve static HTML file
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html', 'index.html', '/index.html']
# Serve 404 messages:
run lambda{ |env|
not_found_page = File.expand_path("../build/404.html", __FILE__)
if File.exist?(not_found_page)
[ 404, { 'Content-Type' => 'text/html', 'Charset' => 'UTF-8' }, [File.read(not_found_page)] ]
else
[ 404, { 'Content-Type' => 'text/html', 'Charset' => 'UTF-8' }, ['404 - page not found'] ]
end
}
I thought I could use Rack::ResponseHeaders from rack-contrib but I don't think I'm using it correctly;
# Custom HTTP Headers
use Rack::ResponseHeaders do |headers|
headers['Charset'] = 'UTF-8'
end
Like I said, I've searched high and low; consulted docs (Rack, heroku), SO questions, blog posts, github, you name it.
Any help with this is much appreciated.
Cheers,
Tom
I had a similar problem and wanted to optimize my site. You just need to manually set your Content-Type header.
Here's my complete config.ru which achieves a 98/100 on Google PageSpeed (it complains about not embedding my css in the page):
# encoding: utf-8
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
require 'rack/contrib'
require File.expand_path("../rack_try_static", __FILE__)
use Rack::ResponseHeaders do |headers|
headers['Content-Type'] = 'text/html; charset=utf-8' if headers['Content-Type'] == 'text/html'
end
use Rack::Deflater
use Rack::StaticCache, urls: ["/images", "/stylesheets", "/javascripts", "/fonts"], root: "build"
use ::Rack::TryStatic,
root: "build",
urls: ["/"],
try: [".html", "index.html", "/index.html"]
run lambda { [404, {"Content-Type" => "text/plain"}, ["File not found!"]] }
You'll also need to add rack-contrib to your Gemfile for Rack::StaticCache:
gem 'rack-contrib'
You'll also want my rack_try_static.
Edit: Note the current implementation of Rack::StaticCache removes Last-Modified and Etag headers and will break on assets ending with a - followed by a number. I have PRs open for both of these (83 and 84).
This no longer applies as I have moved my blog from heroku to github pages. Thanks for looking.

Sinatra public assets not available when uploading to Heroku

I've just started working on a simple Sinatra app and when uploading it to Heroku. None of the files in the public folder seem to be available but it works fine locally.
Are there any obvious reasons this might be happening?
Right now the code is really simple:
require 'rubygems'
require 'sinatra'
require 'bundler/setup'
require 'haml'
require 'rdiscount'
set :static, true
set :public_folder, "#{File.dirname(__FILE__)}/public"
get '/' do
haml :landing
end
__END__
## layout
%html
%head
%meta{charset: "utf-8"}/
%meta{content: "width=device-width, initial-scale=1.0", name: "viewport"}/
%meta{content: "", name: "description"}/
%meta{content: "", name: "author"}/
%title TIL
%link{href: "http://yui.yahooapis.com/pure/0.3.0/pure-min.css", rel: "stylesheet"}
%link{rel: "stylesheet", href: "/styles.css"}
%body
= yield
##landing
%section.hero
.container
.pure-g-r
.pure-u-1
.logo
...
.container
%hr/
.pure-g-r
.pure-u-2-3
.padding-box
:markdown
...
.pure-u-1-3
.padding-box
..
%hr/
.pure-g-r
.pure-u-1
.padding-box
:markdown
...
%hr/
.pure-g-r
.pure-u-1
.padding-box
%h2 ...
.pure-u-1-3
.padding-box
%img.img-rounded{src: "GD-thumbnail.png"}/
:markdown
...
.pure-u-1-3
.padding-box
%img.img-rounded{src: "AL-thumbnail.png"}/
:markdown
...
.pure-u-1-3
.padding-box
%img.img-rounded{src: "BP-thumbnail.png"}/
:markdown
...
%hr/
%footer
.row
.col-lg-12
%p
Local file structure is:
TIL (folder)
- app.rb
- Gemfile
- Procfile
- public (folder)
- AL-thumbnail.png
- BP-thumbnail.png
- GD-thumbnail.png
- logo.png
- styles.css
Have a look in your Heroku log file:
heroku logs
If you can see something like
Rack::Flash::SessionUnavailable - Rack::Flash depends on session middleware.:
Then add
gem "rack-flash-session"
to you Gemfile.
Also add 'require 'rack/flash/test'' to you main file.
This will force heroku to load the desired middelware.

Sinatra json rendering not working as expected

I'm having a problem in Sinatra where I can't respond with just a json and I can't find good sinatra docs anywhere, most of things seems outdated.
Anyways, here's the code:
module MemcachedManager
class App < Sinatra::Base
register Sinatra::Contrib
helpers Sinatra::JSON
get '/' do
json({ hello: 'world' })
end
end
end
MemcachedManager::App.run! if __FILE__ == $0
The response that I do get is:
"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>{\"hello\":\"world\"}</p></body></html>\n"
Where it should have been only the json part. Why is it rendering html tags when I didn't ask for it?
Have you seen this blog post?
require 'json'
get '/example.json' do
content_type :json
{ :key1 => 'value1', :key2 => 'value2' }.to_json
end
I would also modify this to:
get '/example.json', :provides => :json do
to stop HTML/XML calls using the route. Since you're using the sinatra-contrib gem, and since Ruby doesn't need all those parens etc, you can also simplify the code you've given as an example to:
require 'sinatra/json'
module MemcachedManager
class App < Sinatra::Base
helpers Sinatra::JSON
get '/', :provides => :json do
json hello: 'world'
end
end
end
MemcachedManager::App.run! if __FILE__ == $0
Try putting
content_type :json
before the json(...) call

Resources