Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Allow `erb` and `haml` helpers to take a block #641

Merged
merged 6 commits into from

3 participants

@alexeymuranov

What do you think about this? I have changed only erb and haml helpers because for other template engines i do not know which ones have "layouts".

This is to be able to use nested layouts using erb and haml helpers. Real nested layouts, not in the sense of Rails.

When i want to nest two layouts in my application, i currently can do like this:

render :haml, :outer_layout, :layout => false do
  render :haml, :inner_layout1 do
    haml :page1
  end
end

I want to be able to do simply:

haml :outer_layout, :layout => false do
  haml :inner_layout1 do
    haml :page1
  end
end
@zzak
Collaborator

@alexeymuranov Could you provide a test?

@alexeymuranov

Here are the tests. I only made changes for Haml and ERB, but i imagine there are other template engines that can accept blocks. I was tempted to use meta-programming to define these two helpers in one loop, but i think it would make the documentation harder to generate. Maybe there was a reason meta-programming was not used here before.

@zzak
Collaborator

@alexeymuranov Some of them already support blocks, maybe you can go through and add support for the rest of them that will allow it?

I think it's best to leave it without any meta-programming. This way it's obvious which template helpers are supported, and can easily be removed or deprecated. That is probably the intention. Maybe @rkh can comment further.

@alexeymuranov

@zzak, do you know which ones exactly support blocks? If you do, can you tell me please?

@zzak
Collaborator

@alexeymuranov you will have to check tilt, see tilt/lib/tilt/* and look for #evaluate in each template class.

@rkh
Owner

Could you also add this to liquid, slim, creole, wlang, yajl and rabl and corresponding tests?

test/erb_test.rb
@@ -85,6 +85,24 @@ def is; "IS." end
assert ok?
assert_equal '<outer><inner>hi</inner></outer>', body
end
+
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "<h1>Title</h1>\n<%= yield %>" }
+ template(:an_inner_layout) { "<h2>Subtitle</h2>\n<%= yield %>" }
+ template(:a_page) { "<p>Contents.</p>\n" }
+ get('/') do
+ erb :main_outer_layout, :layout => false do
+ erb :an_inner_layout, :layout => false do
@rkh Owner
rkh added a note

The inner erb call should not need the :layout => false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@alexeymuranov

Do not know how to use layouts in creole, yajl, and rabl, will need to look for examples...

@zzak
Collaborator

@alexeymuranov creole, yajl and rabl all have tests, can you check there? these should all have tests, including the new block support.

@alexeymuranov

I do not see how to make a layout in creole for example. There are no such examples among tests, different layout engines are always used with creole.

@rkh
Owner

Maybe it doesn't work, those were just the ones were the code permits layouts.

@alexeymuranov

It is possible to call yield in rabl: https://github.com/nesquena/rabl/wiki/Using-Layouts, but i have not figured out yet how exactly to use it.

@rkh
Owner

Also, still needs docs in README.md.

@alexeymuranov

I can add docs to README. However, i would need much more time to figure out how to use yield in creole, yajl, and rabl. It seems that the semantics is slightly different from the five HTML templates that i have modified: in those five, yield can be used to insert ready-to-use blocks of HTML.

@rkh
Owner

I guess the current code is fine.

alexeymuranov added some commits
@alexeymuranov alexeymuranov Move "Embedded Templates" subsection in README
Move "Embedded Templates" subsection in English and Russian README to a more appropriate place.
e7d2083
@alexeymuranov alexeymuranov Fix Russian README
In Russian README, "Вложенные шаблоны" -> "Включённые шаблоны"
d03ca72
@alexeymuranov alexeymuranov Tests: 5 template helpers can nest layouts ea0d967
@alexeymuranov alexeymuranov "Embedded Templates" -> "Literal Templates"
In English and Russian README, rename "Embedded Templates" to "Literal Templates".
9087825
@alexeymuranov alexeymuranov Allow 5 template helpers to take a block f47ddf8
@alexeymuranov

I have made several changes in English and Russian README, please tell me if you want them separated to another PR.

In Russian README, i had to change the term "вложенные шаблоны" because in my opinion it was not an appropriate translation of "Inline Templates", and conflicted with "вложенные раскладки" for "nested layouts" that i was adding.

I have also moved "Embedded Templates" subsection and renamed them to "Literal Templates". What do you think?

Those are all in separate commits, but if you do not want to merge them now, i can make a separate pull request.

@alexeymuranov alexeymuranov Add README section on new way of nesting layouts
Add README section "Templates with `yield` and nested layouts" in English and Russian.
2e8d3e6
@rkh rkh merged commit bee7dd4 into from
@alexeymuranov alexeymuranov deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 25, 2013
  1. @alexeymuranov

    Move "Embedded Templates" subsection in README

    alexeymuranov authored
    Move "Embedded Templates" subsection in English and Russian README to a more appropriate place.
  2. @alexeymuranov

    Fix Russian README

    alexeymuranov authored
    In Russian README, "Вложенные шаблоны" -> "Включённые шаблоны"
  3. @alexeymuranov
  4. @alexeymuranov

    "Embedded Templates" -> "Literal Templates"

    alexeymuranov authored
    In English and Russian README, rename "Embedded Templates" to "Literal Templates".
  5. @alexeymuranov
  6. @alexeymuranov

    Add README section on new way of nesting layouts

    alexeymuranov authored
    Add README section "Templates with `yield` and nested layouts" in English and Russian.
This page is out of date. Refresh to see the latest.
View
57 README.md
@@ -401,6 +401,16 @@ Available Options:
</dd>
</dl>
+#### Literal Templates
+
+```ruby
+ get '/' do
+ haml '%div.title Hello World'
+ end
+```
+
+Renders the template string.
+
### Available Template Languages
Some languages have multiple implementations. To specify what implementation
@@ -864,16 +874,6 @@ The `:callback` and `:variable` options can be used to decorate the rendered obj
Since calling ruby methods is not idiomatic in wlang, you almost always want to pass locals
to it. Layouts written in wlang and `yield` are supported, though.
-#### Embedded Templates
-
-```ruby
- get '/' do
- haml '%div.title Hello World'
- end
-```
-
-Renders the embedded template string.
-
### Accessing Variables in Templates
Templates are evaluated within the same context as route handlers. Instance
@@ -898,6 +898,43 @@ Or, specify an explicit Hash of local variables:
This is typically used when rendering templates as partials from within
other templates.
+### Templates with `yield` and nested layouts
+
+A layout is usually just a template that calls `yield`.
+Such a template can by used either through the `:template` option as
+described above, or it can be rendered with a block as follows:
+
+```ruby
+ erb :post, :layout => false do
+ erb :index
+ end
+```
+
+This code is mostly equivalent to `erb :index, :layout => :post`.
+
+Passing blocks to rendering methods is most useful for creating nested
+layouts:
+
+```ruby
+ erb :main_layout, :layout => false do
+ erb :admin_layout do
+ erb :user
+ end
+ end
+```
+
+This can also be done in fewer lines of code with:
+
+```ruby
+ erb :admin_layout, :layout => :main_layout do
+ erb :user
+ end
+```
+
+Currently the following rendering method accept a block: `erb`, `haml`,
+`liquid`, `slim `, `wlang`.
+Also the general `render` method accepts a block.
+
### Inline Templates
Templates may be defined at the end of the source file:
View
65 README.ru.md
@@ -416,6 +416,16 @@ set :views, settings.root + '/templates'
`:'subdir/template'`). Вы должны использовать символы, потому что иначе
шаблонизаторы попросту отображают любые строки, переданные им.
+#### Буквальные шаблоны
+
+```ruby
+get '/' do
+ haml '%div.title Hello World'
+end
+```
+
+Отобразит шаблон, переданный строкой.
+
### Доступные шаблонизаторы
Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
@@ -886,16 +896,6 @@ var resource = {"foo":"bar","baz":"qux"}; present(resource);
исключением `yield`), то вы почти всегда будете передавать в шаблон локальные
переменные.
-### Встроенные шаблоны
-
-```ruby
-get '/' do
- haml '%div.title Hello World'
-end
-```
-
-Отобразит встроенный шаблон, переданный строкой.
-
### Доступ к переменным в шаблонах
Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов.
@@ -920,7 +920,46 @@ end
Это обычный подход, когда шаблоны рендерятся как части других шаблонов.
-### Вложенные шаблоны
+### Шаблоны с `yield` и вложенные раскладки (layout)
+
+Раскладка (layout) обычно представляет собой шаблон, который исполняет
+`yield`.
+Такой шаблон может быть либо использован с помощью опции `:template`,
+как описано выше, либо он может быть дополнен блоком:
+
+```ruby
+ erb :post, :layout => false do
+ erb :index
+ end
+```
+
+Эти инструкции в основном эквивалентны `erb :index, :layout => :post`.
+
+Передача блоков интерпретирующим шаблоны методам наиболее полезна для
+создания вложенных раскладок:
+
+```ruby
+ erb :main_layout, :layout => false do
+ erb :admin_layout do
+ erb :user
+ end
+ end
+```
+
+Это же самое может быть сделано короче:
+
+```ruby
+ erb :admin_layout, :layout => :main_layout do
+ erb :user
+ end
+```
+
+В настоящее время, следующие интерпретирубщие шаблоны методы
+принимают блок:
+`erb`, `haml`, `liquid`, `slim `, `wlang`.
+Общий метод заполнения шаблонов `render` также принимает блок.
+
+### Включённые шаблоны
Шаблоны также могут быть определены в конце исходного файла:
@@ -941,9 +980,9 @@ __END__
%div.title Hello world.
```
-Заметьте: вложенные шаблоны, определенные в исходном файле, который подключила
+Заметьте: включённые шаблоны, определенные в исходном файле, который подключил
Sinatra, будут загружены автоматически. Вызовите `enable :inline_templates`
-напрямую, если используете вложенные шаблоны в других файлах.
+напрямую, если используете включённые шаблоны в других файлах.
### Именованные шаблоны
View
32 lib/sinatra/base.rb
@@ -199,7 +199,7 @@ def http_status; 404 end
# Methods available to routes, before/after filters, and views.
module Helpers
# Set or retrieve the response status code.
- def status(value=nil)
+ def status(value = nil)
response.status = value if value
response.status
end
@@ -253,7 +253,7 @@ def uri(addr = nil, absolute = true, add_script_name = true)
alias to uri
# Halt processing and return the error status provided.
- def error(code, body=nil)
+ def error(code, body = nil)
code, body = 500, code.to_str if code.respond_to? :to_str
response.body = body unless body.nil?
halt code
@@ -265,7 +265,7 @@ def not_found(body = nil)
end
# Set multiple response headers with Hash.
- def headers(hash=nil)
+ def headers(hash = nil)
response.headers.merge! hash if hash
response.headers
end
@@ -630,8 +630,8 @@ def initialize
@default_layout = :layout
end
- def erb(template, options = {}, locals = {})
- render :erb, template, options, locals
+ def erb(template, options = {}, locals = {}, &block)
+ render(:erb, template, options, locals, &block)
end
def erubis(template, options = {}, locals = {})
@@ -640,8 +640,8 @@ def erubis(template, options = {}, locals = {})
render :erubis, template, options, locals
end
- def haml(template, options = {}, locals = {})
- render :haml, template, options, locals
+ def haml(template, options = {}, locals = {}, &block)
+ render(:haml, template, options, locals, &block)
end
def sass(template, options = {}, locals = {})
@@ -659,13 +659,13 @@ def less(template, options = {}, locals = {})
render :less, template, options, locals
end
- def builder(template=nil, options = {}, locals = {}, &block)
+ def builder(template = nil, options = {}, locals = {}, &block)
options[:default_content_type] = :xml
render_ruby(:builder, template, options, locals, &block)
end
- def liquid(template, options = {}, locals = {})
- render :liquid, template, options, locals
+ def liquid(template, options = {}, locals = {}, &block)
+ render(:liquid, template, options, locals, &block)
end
def markdown(template, options = {}, locals = {})
@@ -680,11 +680,11 @@ def rdoc(template, options = {}, locals = {})
render :rdoc, template, options, locals
end
- def radius(template, options ={}, locals = {})
+ def radius(template, options = {}, locals = {})
render :radius, template, options, locals
end
- def markaby(template = nil, options ={}, locals = {}, &block)
+ def markaby(template = nil, options = {}, locals = {}, &block)
render_ruby(:mab, template, options, locals, &block)
end
@@ -698,16 +698,16 @@ def nokogiri(template = nil, options = {}, locals = {}, &block)
render_ruby(:nokogiri, template, options, locals, &block)
end
- def slim(template, options = {}, locals = {})
- render :slim, template, options, locals
+ def slim(template, options = {}, locals = {}, &block)
+ render(:slim, template, options, locals, &block)
end
def creole(template, options = {}, locals = {})
render :creole, template, options, locals
end
- def wlang(template, options = {}, locals = {})
- render :wlang, template, options, locals
+ def wlang(template, options = {}, locals = {}, &block)
+ render(:wlang, template, options, locals, &block)
end
def yajl(template, options = {}, locals = {})
View
18 test/erb_test.rb
@@ -85,6 +85,24 @@ def is; "IS." end
assert ok?
assert_equal '<outer><inner>hi</inner></outer>', body
end
+
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "<h1>Title</h1>\n<%= yield %>" }
+ template(:an_inner_layout) { "<h2>Subtitle</h2>\n<%= yield %>" }
+ template(:a_page) { "<p>Contents.</p>\n" }
+ get('/') do
+ erb :main_outer_layout, :layout => false do
+ erb :an_inner_layout do
+ erb :a_page
+ end
+ end
+ end
+ end
+ get '/'
+ assert ok?
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
+ end
end
View
18 test/haml_test.rb
@@ -84,6 +84,24 @@ def haml_app(&block)
haml_app { haml "= foo", :locals => { :foo => 'bar' }}
assert_equal "bar\n", body
end
+
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "%h1 Title\n= yield" }
+ template(:an_inner_layout) { "%h2 Subtitle\n= yield" }
+ template(:a_page) { "%p Contents." }
+ get('/') do
+ haml :main_outer_layout, :layout => false do
+ haml :an_inner_layout do
+ haml :a_page
+ end
+ end
+ end
+ end
+ get '/'
+ assert ok?
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
+ end
end
rescue LoadError
View
18 test/liquid_test.rb
@@ -52,6 +52,24 @@ def liquid_app(&block)
assert ok?
assert_equal 'foo', body
end
+
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "<h1>Title</h1>\n{{ yield }}" }
+ template(:an_inner_layout) { "<h2>Subtitle</h2>\n{{ yield }}" }
+ template(:a_page) { "<p>Contents.</p>\n" }
+ get('/') do
+ liquid :main_outer_layout, :layout => false do
+ liquid :an_inner_layout do
+ liquid :a_page
+ end
+ end
+ end
+ end
+ get '/'
+ assert ok?
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
+ end
end
rescue LoadError
View
18 test/slim_test.rb
@@ -77,6 +77,24 @@ def slim_app(&block)
assert ok?
assert_body '<x foo="bar"></x>'
end
+
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "h1 Title\n== yield" }
+ template(:an_inner_layout) { "h2 Subtitle\n== yield" }
+ template(:a_page) { "p Contents." }
+ get('/') do
+ slim :main_outer_layout, :layout => false do
+ slim :an_inner_layout do
+ slim :a_page
+ end
+ end
+ end
+ end
+ get '/'
+ assert ok?
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
+ end
end
rescue LoadError
View
17 test/wlang_test.rb
@@ -63,6 +63,23 @@ def who; "world"; end
assert_body "WLang Layout!\nHello World"
end
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
+ mock_app do
+ template(:main_outer_layout) { "<h1>Title</h1>\n>{ yield }" }
+ template(:an_inner_layout) { "<h2>Subtitle</h2>\n>{ yield }" }
+ template(:a_page) { "<p>Contents.</p>\n" }
+ get('/') do
+ wlang :main_outer_layout, :layout => false do
+ wlang :an_inner_layout do
+ wlang :a_page
+ end
+ end
+ end
+ end
+ get '/'
+ assert ok?
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
+ end
end
rescue LoadError
Something went wrong with that request. Please try again.