0

I really like how Sass mixins let you wrap a block of code with a customizable wrapper:

=container($class)
  .#{$class}.container
    .container-inner
      @content

+container(header)
  foo: bar

+container(main)
  baz: quux

Resulting CSS:

.header.container .container-inner {
  foo: bar;
}

.main.container .container-inner {
  baz: quux;
}

I'm trying to do the same with Padrino/Middleman partials. The problem is that partials not accept content blocks (whyyy?). :(

I tried to work it around by passing content blocks into content_for, but subsequent uses of content_for would append to content rather than replace it:

Partial partials/container.haml:

.container
  = yield_content :container

Page index.html.haml:

- content_for :container do
  Foo

= partial('partials/container')


- content_for :container do
  Bar

= partial('partials/container')

Resulting HTML:

<div class="container">
  Foo
</div>
<div class="container">
  Foo
  Bar
</div>

As you can see, it duplicates my content which is absolutely unacceptable. Also, this way does not allow to provide arguments that would be used to customize wrapper HTML.

So i thought of creating a helper. It would enable the following usage style which is way more elegant than content_for and also supports passing variables into content block:

= partial_with_content_block('partials/container', class: 'header') do
  Foo

= partial_with_content_block('partials/container', class: 'main') do
  Bar

How do i implement such a helper?

Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133

2 Answers2

1

Something like this should work, but I left the partial out:

def container(options={}, &block)
  raise ArgumentError, "Missing block" unless block_given?
  content = capture_html(&block)
  content_tag(:div, content, options)
end

Call as:

= container(class: 'main') do
  p More content here

But now that I think if it, this could be all handled with default Padrino methods:

= content_tag :div, class: 'main' do
  p More content
Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
zwippie
  • 15,050
  • 3
  • 39
  • 54
  • Good point, I was too quick assuming this was Rails. But it seems that Middleman has a `content_tag` method that you can use. `block_given?` is a plain Ruby method, so I think this will work in Middleman. Edit: except for [`capture`](http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-capture)... – zwippie Nov 08 '13 at 15:09
  • Thank you @zwippie, i had failed to notice the `content_tag` method. My container wrapper consists of two wrapping elements, so your helper example is very helpful. I've edited your answer to be fully Padrino/Middleman compliant. – Andrey Mikhaylov - lolmaus Nov 08 '13 at 23:45
  • You're welcome. I never knew that the Padrino/Middleman view layer took so much goodness of Rails (did they?). Looks interesting. – zwippie Nov 09 '13 at 21:21
1

Middleman partials do accept content blocks, but the syntax is slightly different. ERB is given below, but you should be able to do this in HAML, as well.

<% partial 'my_partial' do %>
    My Content
<% end %>

my_partial.html.erb

<p>
    <%= yield %>
</p>

Result

<p>
    My Content
</p>
Jimothy
  • 9,150
  • 5
  • 30
  • 33
  • I originally said you must use `render_partial`, but that isn't true. `render_partial` is just an alias for `partial`, so either will work. – Jimothy Dec 07 '15 at 21:56