Inherited Templates in Pancake

Update: Fixed some of the code examples to remove a stray block

Pancake uses template inheritance to embed content in a root template. This is similar to Rails layouts, but with inherited templates, you can have different content blocks, each having their own default content. Inspiration for this feature has come from Django and Rango which also use an inheritance concept. There’s really not an easy way to explain in words so lets see what it looks like:

# base.html.haml

!!!
%html
  %body
    %h1 Welcome to base
    - content_block :content do 
      %p Default Base Content

# foo.html.haml

- inherits_from :base
  
- content_block :content do 
  %p Foo Content

If we had a stack that looked like this:

class FooStack < Pancake::Stacks::Short
  add_root(__FILE__)
  
  get "/" do 
    render :base
  end

  get "/foo" do
    render :foo
  end
end

When we visit the “/” url we’d get output like this:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <h1>Welcome to base</h1>
    <p> Default Base Content </p>
  </body>
</html>

When we visit “/foo” we’d get something like this:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <h1>Welcome to base</h1>
    <p> Foo Content </p>
  </body>
</html>

See how the content block for :content in foo was used when we inherited from :base? It’s got “Foo Content” in the inherited one. Templates in a Pancake Short stack can all use inherited templates. By suppling inherited_from with a template name, the current template will inherit from it. You can supply a different template name on each request, and then inherit from a different template each time if you need to.

That’s cool and all. We can inherit from a different template each time. Pancake will take care of finding the right template when you inherit a stack also, so that if a child stack hasn’t defined it, it will look in the parent stack.

Wouldn’t it be cool to be able to append to the parent content rather than just overwrite it though? Well a Pancake Short stack lets us do that too.

# foo.html.haml

- inherits_from :base
  
- content_block :content do 
  %p Foo Content
  = content_block.super

By calling super on the content block, you’re inserting the content of the parent block inside the child. The output of running that template would look like this:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <h1>Welcome to base</h1>
    <p> Foo Content </p>
    <p> Default Base Content </p>
  </body>
</html>

One of the troubles with an inherited approach though, is that if I have 2 stacks, Foo and, Bar inside a master container stack MyMasterStack, all three of these will inherit from different places. I’ve just added in Pancake version 0.1.26 the ability to specify a stack name and template name in inherits from. So you could have something like this:


- inherits_from UiStack, :base

- content_block :content do 
  %p Some content

So we’re inheriting this template from the :base template of the UiStack. Kinda cool, but there’s still a problem. I need prior knowledge that it’s the UiStack that I want to inherit from. That’s ok when it’s your app, but if you write a stack for general consumption you may not be able to make that assumption. Pancake sorts this for you. You can tell Pancake, at a global level, which Stack you’d like to set as the master template stack. By default this is the container stack when using pancake stand alone. If you’re just adding it somewhere else though, you’d start the container stack like this:

MyStack.stackup(:master => true)

This sets the stack as the master which adds the “master” directory as another root for the stack, but also sets the stack to be master of templates. To manually set the stack as master of templates you do this:


  MyStack.before_build_stack do 
    Pancake.master_templates = UiStack
  end

This sets the UiStack as the master for the whole pancake graph, which lets us do this:


- inherits_from :default!

- content_block :content do
  %p content for this view

Using the :default! template name, will tell pancake to inherit from the master template stack, in this case UiStack, with the :base template. You can tell it to use a different one but by default, :base is the one that gets used.

By using this mechanism, a collection of different shared stacks can share the same look and feel by inheriting from the global Pancake master templates stack. When writing plugins and shared stacks, you can just set the stack templates to inherit from :default! and everything should just work.

About these ads

Tags: , ,

4 Responses to “Inherited Templates in Pancake”

  1. Marcin Kulik Says:

    Great stuff. This is the killer feature of Django and I missed it in ruby world.

    I would like to see inheritable templates also in Merb. Maybe I’ll start working on it ;)

  2. Botanicus Says:

    Marcin, I was already thinking about it. I’ll be happy to help with the feature. We was actually already talking about it with Pavel Kunc (merboutpost) and he probably would like to have it for Merb 2, at least as an option, but we haven’t discussed it with other Merb devs so far.

  3. Botanicus Says:

    Thanks to Rango it’s fairly easy to get template inheritance into your favourite framework. You can just use Rango template rendering layer (you don’t need anything else from Rango) to get it working.

    See an examples:
    Merb: http://github.com/botanicus/rango-in-merb (just few lines of code, really easy!)

    Rails Plugin I created yesterday for this: http://github.com/botanicus/rails-template-inheritance, example app http://github.com/botanicus/rango-in-rails

  4. hh Says:

    Hi, Thanks for interesting posts. I’m new to web frameworks – trying to get a handle on which-does-what. I am wondering if Thor’s tasks/action can’t be explicitly leveraged, so inherits_from and content_block would be an alias to Thor’s invoke. By writing tasks you’d get Thor’s name spacing fu, inheritence of class and method options, etc, etc and invoke can be employed quite flexibly. Main benefit would be compounding Thor task know-how into pancake and vice-versa.
    Just a thought.
    HTH?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: