Posts Tagged ‘templates’

Inherited Templates in Pancake

November 28, 2009

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.


Follow

Get every new post delivered to your Inbox.