Writing a program that spits out HTML is often more difficult than you might imagine. Although programming languages are better at creating text than they used to be (some of us remember character handling in Fortran and standard Pascal), creating and concatenating string constructs is still painful. If there isn't much to do, it isn't too bad, but a whole HTML page is a lot of text manipulation.
With static HTML pages - those that don't change from request to request - you can use nice WYSIWG editors. Even those of us who like raw text editors find it easier to just type in the text and tags rather than fiddle with string concatenation in a programming language.
Of course the issue is with dynamic Web pages - those that take the results of something like database queries and embed them into the HTML. The page looks different with each result, and as a result regular HTML editors aren't up to the job.
The best way to work is to compose the dynamic Web page as you do a static page but put in markers that can be resolved into calls to gather dynamic information. Since the static part of the page acts as a template for the particular response, I call this a Template View.
Martin Fowler
Views in Sinatra are HTML templates that can optionally contain data passed from the application.
There are two ways to work with views in Sinatra: inline templates and external templates.
Véase en GitHub sinatra-up-and-running/tree/master/chapter2/views.
Templates Inline
Templates may be defined at the end of the source file. En este ejemplo trabajamos con varios templates inline en diferentes ficheros:
[~/sinatra/sinatraupandrunning/chapter2/views(master)]$ cat example2-14.rb require 'sinatra/base' class App < Sinatra::Base enable :inline_templates get '/index' do puts "Visiting #{request.url}" erb :index end end require './another' __END__ @@index <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Inline template</title> </head> <body> <h1>Worked!</h1> </body> </html>En este fichero tenemos un segundo template inline:
[~/sinatra/sinatraupandrunning/chapter2/views(master)]$ cat another.rb class App enable :inline_templates get '/' do erb :another end end __END__ @@another <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Separated file</title> </head> <body> <h1>Inside another!</h1> </body> </html>Este es nuestro config.ru:
[~/sinatra/sinatraupandrunning/chapter2/views(master)]$ cat config.ru require './example2-14' run AppPara simplificar las cosas hemos hecho unRakefile
:[~/sinatra/sinatraupandrunning/chapter2/views(master)]$ cat Rakefile task :default => :server desc "run server" task :server do sh "rackup" end desc "make a get / request via curl" task :root do sh "curl -v localhost:9292" end desc "make a get /index request via curl" task :index do sh "curl -v localhost:9292/index" endEl resultado de la ejecución es:
[~/sinatra/sinatra-up-and-running/chapter2/views/inline_templates(master)]$ curl http://localhost:9292/index <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Inline template</title> </head> <body> <h1>Worked!</h1> </body> </html> [~/sinatra/sinatra-up-and-running/chapter2/views/inline_templates(master)]$ curl http://localhost:9292/ <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Separated file</title> </head> <body> <h1>Inside another!</h1> </body> </html>
Named Templates
Templates may also be defined using the top-leveltemplate
method:
template :layout do "%html\n =yield\n" end template :index do '%div.title Hello World!' end get '/' do haml :index endIf a template namedlayout
exists, it will be used each time a template is rendered.You can individually disable layouts by passing
:layout => false
or disable them by default viaset :haml, :layout => false
:
get '/' do haml :index, :layout => !request.xhr? end
Templates Externos
$ ls Rakefile example2-16.rb views config.ru
$ cat example2-16.rb require 'sinatra/base' class App < Sinatra::Base get '/index' do puts "Visiting #{request.url}" erb :index end end
$ cat views/index.erb <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Inline template</title> </head> <body> <h1>Worked!</h1> </body> </html>
$ cat config.ru require './example2-16' run App
$ cat Rakefile task :default => :server desc "run server" task :server do sh "rackup" end desc "make a get / request via curl" task :root do sh "curl -v localhost:9292" end desc "make a get /index request via curl" task :index do sh "curl -v localhost:9292/index" end
$ rake server rackup >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:9292, CTRL+C to stop Visiting http://localhost:9292/index 127.0.0.1 - - [03/Jul/2013 22:30:16] "GET /index HTTP/1.1" 200 157 0.0774
$ rake index curl -v localhost:9292/index * About to connect() to localhost port 9292 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 9292 (#0) > GET /index HTTP/1.1 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8x zlib/1.2.5 > Host: localhost:9292 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/html;charset=utf-8 < Content-Length: 157 < X-XSS-Protection: 1; mode=block < X-Content-Type-Options: nosniff < X-Frame-Options: SAMEORIGIN < Connection: keep-alive < Server: thin 1.5.1 codename Straight Razor < <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Inline template</title> </head> <body> <h1>Worked!</h1> </body> </html> * Connection #0 to host localhost left intact * Closing connection #0
Templates Externos en Subcarpetas
Véase en GitHub sinatra-up-and-running/tree/master/chapter2/views/external_view_files/external_in_subfolders
$ ls Rakefile app.rb config.ru views
$ cat app.rb require 'sinatra/base' class App < Sinatra::Base get '/:user/profile' do |user| @user = user erb '/user/profile'.to_sym end get '/:user/help' do |user| @user = user erb :'/user/help' end end
$ cat views/user/profile.erb <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Profile Template</title> </head> <body> <h1>Profile of <%= @user %></h1> <%= params %> </body> </html>
$ cat views/user/help.erb <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>HELP Template</title> </head> <body> <h1>Help for user <%= @user %></h1> <pre> <%= params %> </pre> </body> </html>
$ cat config.ru require './app' run App
$ cat Rakefile PORT = 9292 task :default => :server desc "run server" task :server do sh "rackup" end desc "make a get /pepe/profile request via curl" task :profile, :name do |t, h| user = h['name'] || 'pepe' sh "curl -v localhost:#{PORT}/#{user}/profile" end desc "make a get /pepe/help request via curl" task :help do sh "curl -v localhost:#{PORT}/pepe/help" end
$ rake server rackup >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:9292, CTRL+C to stop 127.0.0.1 - - [03/Jul/2013 21:40:04] "GET /Pedro/profile HTTP/1.1" 200 227 0.1077
$ rake profile[Pedro] curl -v localhost:9292/Pedro/profile * About to connect() to localhost port 9292 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 9292 (#0) > GET /Pedro/profile HTTP/1.1 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8x zlib/1.2.5 > Host: localhost:9292 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/html;charset=utf-8 < Content-Length: 227 < X-XSS-Protection: 1; mode=block < X-Content-Type-Options: nosniff < X-Frame-Options: SAMEORIGIN < Connection: keep-alive < Server: thin 1.5.1 codename Straight Razor < <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Profile Template</title> </head> <body> <h1>Profile of Pedro</h1> {"splat"=>[], "captures"=>["Pedro"], "user"=>"Pedro"} </body> </html> * Connection #0 to host localhost left intact * Closing connection #0
Variables en las Vistas
Comunicación vía variables de instancia
Los templates se evaluan en el mismo contexto que los manejadores de las rutas. Las variables de instancia son accesibles directamente en los templates.
get '/:id' do @foo = Foo.find(params[:id]) haml '%h1= @foo.name' endVeamos un ejemplo de comunicación via variables de instancia entre el manejador de la ruta y el template:
[~/sinatra/sinatra-views/passing_data_into_views(master)]$ ls Rakefile config.ru via_instance.rb
[~/sinatra/sinatra-views/passing_data_into_views(master)]$ cat via_instance.rb require 'sinatra/base' class App < Sinatra::Base get '/*' do |name| def some_template <<-'HAMLTEMP' %ol - @foo.each do |item| %li %i #{item} HAMLTEMP end puts "*---***#{name}*---****" @foo = name.split('/') haml some_template end end
[~/sinatra/sinatra-views/passing_data_into_views(master)]$ cat config.ru require './via_instance' run App
[~/sinatra/sinatra-views/passing_data_into_views(master)]$ cat Rakefile task :default => :server desc "run server" task :server do sh "rackup" end desc "make a get /juan/leon/hernandez request via curl" task :client do sh "curl -v localhost:9292/juan/leon/hernandez" end
[~/sinatra/sinatraupandrunning/chapter2/views/passing_data_into_views(master)]$ rake server rackup >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:9292, CTRL+C to stop *---***juan/leon/hernandez*---**** 127.0.0.1 - - [05/Jul/2013 17:06:05] "GET /juan/leon/hernandez HTTP/1.1" 200 109 0.3502
[~/sinatra/sinatra-views/passing_data_into_views(master)]$ rake client curl -v localhost:9292/juan/leon/hernandez * About to connect() to localhost port 9292 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 9292 (#0) > GET /juan/leon/hernandez HTTP/1.1 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8x zlib/1.2.5 > Host: localhost:9292 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/html;charset=utf-8 < Content-Length: 109 < X-XSS-Protection: 1; mode=block < X-Content-Type-Options: nosniff < X-Frame-Options: SAMEORIGIN < Connection: keep-alive < Server: thin 1.5.1 codename Straight Razor < <ol> <li> <i>juan</i> </li> <li> <i>leon</i> </li> <li> <i>hernandez</i> </li> </ol> * Connection #0 to host localhost left intact * Closing connection #0
Pasando variables a la vista explícitamente via un hash
También es posible pasar en la llamada un hash especificando las variables locales:
get '/:id' do foo = Foo.find(params[:id]) haml '%h1= bar.name', :locals => { :bar => foo } endThis is typically used when rendering templates as partials from within other templates.Veamos un ejemplo:
$ ls Rakefile config.ru via_hash.rb views
$ cat via_hash.rb require 'sinatra/base' class App < Sinatra::Base get '/*' do |name| def some_template <<-'ERBTEMP' <ul><% name.each do |item| %> <li> <i> <%= item %> </i> </li> <% end %> </ul> ERBTEMP end # method some_template puts "*---***#{name}*---****" erb some_template, :locals => { :name => name.split('/')} end end
$ cat views/layout.erb <!DOCTYPE html> <html> <head> <title>Sinatra</title> </head> <body> <h1>Accesing variables in templates via a parameter hash</h1> <%= yield %> </body> </html>
$ cat config.ru require './via_hash' run App
$ cat Rakefile task :default => :server desc "run server" task :server do sh "rackup" end desc "make a get /juan/leon/hernandez request via curl" task :client do sh "curl -v localhost:9292/juan/leon/hernandez" end
$ rake serverrackup >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:9292, CTRL+C to stop *---***juan/leon/hernandez*---**** 127.0.0.1 - - [05/Jul/2013 17:50:20] "GET /juan/leon/hernandez HTTP/1.1" 200 290 0.0352
$ rake client curl -v localhost:9292/juan/leon/hernandez * About to connect() to localhost port 9292 (#0) * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 9292 (#0) > GET /juan/leon/hernandez HTTP/1.1 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8x zlib/1.2.5 > Host: localhost:9292 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/html;charset=utf-8 < Content-Length: 290 < X-XSS-Protection: 1; mode=block < X-Content-Type-Options: nosniff < X-Frame-Options: SAMEORIGIN < Connection: keep-alive < Server: thin 1.5.1 codename Straight Razor < <!DOCTYPE html> <html> <head> <title>Sinatra</title> </head> <body> <h1>Accesing variables in templates via a parameter hash</h1> <ul> <li> <i> juan </i> </li> <li> <i> leon </i> </li> <li> <i> hernandez </i> </li> </ul> </body> </html> * Connection #0 to host localhost left intact * Closing connection #0
Opciones pasadas a los Métodos de los Templates
Options passed to the render method override options set via
set
.Available Options:
locals
List of locals passed to the document. Handy with partials. Example:
erb "<%= foo %>", :locals => {:foo => "bar"}default_encoding
String encoding to use if uncertain. Defaults to
settings.default_encoding.
views
Views folder to load templates from. Defaults to
settings.views
.
layout
Whether to use a layout (true or false), if it's a Symbol, specifies what template to use. Example:
erb :index, :layout => !request.xhr?
content_type
Content-Type the template produces, default depends on template language.
scope
Scope to render template under.
Defaults to the application instance.
If you change this, instance variables and helper methods will not be available.
layout_engine
Template engine to use for rendering the layout.
Useful for languages that do not support layouts otherwise.
Defaults to the engine used for the template. Example:
set :rdoc, :layout_engine => :erb
layout_options
Special options only used for rendering the layout. Example:
set :rdoc, :layout_options => { :views => 'views/layouts' }
- Templates are assumed to be located directly under the
./views
directory.To use a different views directory:
set :views, settings.root + '/templates'
- One important thing to remember is that you always have to reference templates with symbols, even if they’re in a subdirectory (in this case, use:
:'subdir/template'
or'subdir/template'.to_sym
).You must use a symbol because otherwise rendering methods will render any strings passed to them directly.
Casiano Rodríguez León
2015-01-25