Template functions with HAML in Sinatra - ruby

I'd like to be able to create template functions in Sinatra HAML templates that themselves contain haml. Is there any way to do this or something similar? It'd be cool if it could work with markdown too.
foo.haml
def foo(x)
%h2 something
%p something about #{x}
%h1 Herp de derp
= foo("mary")
= foo("us")
Cheers!

Actually, you can do something like this:
# app.rb
require 'sinatra'
require 'haml'
helpers do
def foo(name)
haml = <<-HAML
#hello_block
Hello, #{name}
HAML
engine = Haml::Engine.new(haml)
engine.render
end
end
get '/' do
haml :index
end
# index.haml
= foo 'World'

Function is close, what you really need is what's known as a partial. These are predefined templates that you can place inside other views. For instance, you may have a comment partial to display a comment's author, timestamp, content, etc. You can then render this partial for each of the comments on a particular post.
Essentially, you'll end up with the following
# _foo.haml.erb
%h2 somthing
%p= x
# index.haml.erb
%h1 Herp de derp
= render :partial => "foo", :locals => { :x => "mary" }
= render :partial => "foo", :locals => { :x => "us" }

Related

Ruby: How to pass a variable from server to index?

something = "0"
get "/" do
erb :index, :locals => something
end
When I do this and go to localhost, it says undefined method `keys' for "0":String. I am using sinatra. How do I pass a variable from server to index?
You're half way there, you just need to make something an object.
get "/" do
erb :index, :locals => {:something => 0}
end
and then just use it in the your index:
Something: <%= something %>

Sinatra method to set layout

I want to write a method in sinatra to set the layout, something like
def admin_layout
set :layout, 'admin/layout'
end
I know I can do things like set :erb, layout: :'main/layout' or specify the layout for each action like
get 'admin/login' do
erb :'admin/login', layout: :'admin/layout'
end
but I'm wondering if there is a way to just abstract that into a method so I don't need to set the layout for each route. I am making an app with a main site and then an admin site, but the admin part is very lightweight, just logging in and being able to edit posts so I am not trying to get too crazy but my current file structure is like this:
db/
models/
public/
views/
admin/
main/
app.rb
config.ru
Define a layout argument:
def admin_layout
{:layout, 'admin/layout'}
end
Then you can use this method as a param like
get 'admin/login' do
## do ......
erb :'admin/login', admin_layout
end
Or if you want to judge which layout it will use, change the admin_layout function like:
def admin_layout
if request.path.start_with?('/admin')
{:layout, 'admin/layout'}
else
{:layout, 'layout'}
end
end
I know I can do things like: set :erb, layout: :'main/layout'
No you can't do that--or at least erb will ignore it because it's not an erb specific option. If you could do that, wouldn't that do what you are asking for here:
but I'm wondering if there is a way to just abstract that into a
method so I don't need to set the layout for each route.
The method already exists--template():
#Templates:
application_layout = <<END_OF_HAML
%html
%head
%title My App
%body
=yield
END_OF_HAML
template :layout do #Creates a template named :layout
application_layout
end
template :page1 do
'%div.greet Hello World!' #=> <div class="greet">Hello World!</div>
end
template :page2 do
'%div#first_name John' #=> <div id="first_name">John</div>
end
#Routes: -------------------
get '/page1' do
haml :page1 #If there is a template named :layout, then the :page1 template will be inserted into the :layout template automatically.
end
get '/page2' do
haml :page2 #If there is a template named :layout, then the :page1 template will be inserted into the :layout template automatically.
end
Then you can override the default layout, like this:
admin_layout = <<END_OF_HAML
%html
%head
%title Admin Only
%body
=yield
END_OF_HAML
template :special_layout do #Creates a template named :special_layout
admin_layout
end
get '/page3' do
haml :special_layout, :layout => false do #Don't use default layout, i.e the :layout template, instead use :special_layout template
haml :page3
end
end
See "Named Templates" here: http://www.sinatrarb.com/intro.html#Named%20Templates

Passing parameters to erb view

I'm trying to pass parameters to an erb view using Ruby and Sinatra.
For example, I can do:
get '/hello/:name' do
"Hello #{params[:name]}!"
end
How do I pass :name to the view?
get '/hello/:name' do
erb :hello
end
And how do I read the parameters inside view/hello.erb?
Thanks!
just pass the :locals to the erb() in your routes:
get '/hello/:name' do
erb :hello, :locals => {:name => params[:name]}
end
and then just use it in the views/hello.erb:
Hello <%= name %>
(tested on sinatra 1.2.6)
Not sure if this is the best way, but it worked:
get '/hello/:name' do
#name = params[:name]
erb :hello
end
Then, I can access :name in hello.erb using the variable #name
get '/hello/:name' do
"Hello #{params[:name]}!"
end
You cannot do this in routes.
You want to set the params in the controller.
app/controllers/some_controller.rb
def index
params[:name] = "Codeglot"
params[:name] = "iPhone"
params[:name] = "Mac Book"
end
app/views/index.html.erb
<%= params[:name] %>
<%= params[:phone] %>
<%= params[:computer] %>

Erb template not rendering when used with ensure exception handling

I have come across a issue when writing some sinatra code I have the follow block of code
begin
# do stuff here
rescue SomeException::Class => ex
flash.now[:err] = "some error " + ex.message
ensure
erb :content, :layout => :mainlayout
end
The problem I have is the erb output is only partially rendered, mainlayout.erb is rendered, however, content.erb doesn't get included. I have used the same erb line in other parts of the application and they work fine.
The following actually works and is a work around that I am currently using
begin
# do stuff here
erb :content, :layout => :mainlayout
rescue SomeException::Class => ex
flash.now[:err] = "some error " + ex.message
erb :content, :layout => :mainlayout
end
Any ideas to why this isn't completing when under ensure? I would like to use it as its more elegant.
try to use return erb :content, :layout => :mainlayout read more about ensure here - http://blog.leshill.org/blog/2009/11/17/ensure-with-explicit-return.html

Sinatra render a ruby file

How to render a ruby file (which in my case returns a pdf document) for example:
get "/pdf" do
#locals = {some_locals_hash}
headers({'Content-Type' => 'application/pdf',
'Content-Description' => 'File Transfer',
'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => "attachment;filename=\"test.pdf\"",
'Expires' => '0',
'Pragma' => 'public'})
ruby :test, :layout => false, :locals => #locals
end
I know Tilt does not have ruby template. For now I put all content in a *.haml file like:
-# PDF file description
:ruby
pdf = Prawn::Document.new(
... docs settings)
... docs content
end
= pdf.render()
and I render it with haml :template ...etc...
Truth is, I only need this for syntax highlighting, my editor does not properly highlight embedded ruby code in haml files :(. So if it's to complicated don't bother...
I managed with a Tilt template
module Tilt
class RubyTemplate < Template
def prepare
end
def evaluate(scope, locals, &block)
super(scope, locals, &block)
end
def precompiled_template(locals)
data.to_str
end
end
register 'rb', RubyTemplate
end
and with a helper method
helpers do
def ruby(*args)
render(:rb, *args)
end
end
I't sure this is the best way, but at least is working :)

Resources