Sinatra method to set layout - ruby

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

Related

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.

Ruby/Sinatra: Passing a URL variable to an .erb template

I'm using Padrino, and I want to take parameters out of URL and use them in an .erb template.
In my app setup I have:
get '/testpage/:id' do
userID = params[:id]
render 'test/index'
end
In my test/ folder I have index.html.erb which is successfully rendered, for a url like http://localhost:9000/testpage/hello123.
However, I've tried printing the params[:userID] on the page with:
<%= #userID %>
The rest of the page renders fine but hello123 isn't anywhere to be found. When I try <%= userID %> I get undefined local variable or method `userID' for #<stuff>
What am I missing here?
Just a guess, because I've never used Padrino, but if it works like Rails this may help you:
get '/testpage/:id' do
#userID = params[:id]
render 'test/index'
end
In sinatra, it's just like this (see "Views/Templates" section):
get '/testpage/:id' do |id|
erb :index, :locals => {:id => id}
end
The template is located in views/index.erb by default. It could be change.

How can I use views and layouts with Ruby and ERB (not Rails)?

How can I use views and layouts with Ruby and ERB (not Rails)?
Today i'm using this code to render my view:
def render(template_path, context = self)
template = File.read(template_path)
ERB.new(template).result(context.get_binding)
end
This works very well, but how can I implement the same function, but to render the template inside a layout? I want to call render_with_layout(template_path, context = self), and so that it will have a default layout.
Since you tagged it with Sinatra I assume that you us Sinatra.
By default you view is rendered in your default layout called layout.erb
get "/" do
erb :index
end
This renders your view index with the default layout.
If you need multiple layouts you can specify them.
get "/foo" do
erb :index, :layout => :nameofyourlayoutfile
end
* If you don't use Sinatra you may want to borrow the code from there.
If you use the Tilt gem (which I think is what Sinatra uses) you could do something like
template_layout = Tilt::ERBTemplate.new(layout)
template_layout.render {
Tilt::ERBTemplate.new(template).render(context.get_binding)
}
If you are using Sinatra so it has a good docimentation and one of the topics it's nested layouts (see Sinatra README)
Also good idea to use special default layout file (layout.haml or layout.erb in your view directory) This file will be always use to render others. This is example for layout.haml:
!!!5
%html
%head
##<LOADING CSS AND JS, TILE, DESC., KEYWORDS>
%body
=yield ## THE OTHER LAYOUTS WILL BE DISPALYED HERE
%footer
# FOOTER CONTENT
Thanks for all the answers!
I solved it finally by doing this, I hope someone else also can find this code useful:
def render_with_layout(template_path, context = self)
template = File.read(template_path)
render_layout do
ERB.new(template).result(context.get_binding)
end
end
def render_layout
layout = File.read('views/layouts/app.html.erb')
ERB.new(layout).result(binding)
end
And I call it like this:
def index
#books = Book.all
body = render_with_layout('views/books/index.html.erb')
[200, {}, [body]]
end
Then it will render my view, with the hardcoded (so far) layout..

Template functions with HAML in Sinatra

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" }

Call Sinatra erb from another class

I need to render a Sinatra erb template inside a class in my controller. I'm having issues calling this though. I've looked in the Sinatra rdocs and have come up with this:
Sinatra::Templates.erb :template_to_render
When I do this, I get the following error:
undefined method `erb' for Sinatra::Templates:Module
Is there a way to call this from another class?
To imitate rendering behavior of Sinatra controller in some other class (not controller) you can create module like this:
module ErbRender
include Sinatra::Templates
include Sinatra::Helpers
include Sinatra::ContentFor
def settings
#settings ||= begin
settings = Sinatra::Application.settings
settings.root = "#{ROOT}/app"
settings
end
end
def template_cache
#template_cache ||= Tilt::Cache.new
end
end
Here you may need to tune settings.root
Usage example:
class ArticleIndexingPostBody
include ErbRender
def get_body
erb :'amp/articles/show', layout: :'amp/layout'
end
end
This will properly render templates with layouts including content_for
why you don't require 'erb' and after use only erb
## You'll need to require erb in your app
require 'erb'
get '/' do
erb :index
end
You could have your class return the template name and render it in the main app.
Of course that's not exactly an answer (I don't have enough rep to add a comment with this account) and you're probably doing just that by now anyway...

Resources