github
Advanced Search
  • Home
  • Pricing and Signup
  • Explore GitHub
  • Blog
  • Login

hassox / pancake

  • Admin
  • Watch Unwatch
  • Fork
  • Your Fork
  • Pull Request
  • Download Source
    • 246
    • 16
  • Source
  • Commits
  • Network (16)
  • Issues (2)
  • Downloads (1)
  • Wiki (1)
  • Graphs
  • Branch: master

click here to add a description

click here to add a homepage

  • Branches (1)
    • master ✓
  • Tags (1)
    • v0.1.10
Sending Request…
Enable Donations

Pledgie Donations

Once activated, we'll place the following badge in your repository's detail box:
Pledgie_example
This service is courtesy of Pledgie.

Stackem Up! — Read more

  cancel

http://pancakestacks.wordpress.com

  cancel
  • Private
  • Read-Only
  • HTTP Read-Only

This URL has Read+Write access

Updates the version 
Daniel Neighman (author)
Tue Jan 12 13:29:54 -0800 2010
commit  38e44602de8e0db5e069945e8e97bfcd0def4f91
tree    df0b13f70cef576dc38b047264d6b4d5ff938e39
parent  05cd95f30f19d129104f640a30267d7371fa5c40
pancake /
name age
history
message
file .gitignore Sat Sep 19 05:55:54 -0700 2009 Adds the tmp directories fo the generators [hassox]
file LICENSE Sat Aug 01 08:14:24 -0700 2009 Fixes some bugs with my Yard dox [hassox]
file MIDDLEWARE_LIST Sun Jun 07 06:04:47 -0700 2009 Adds mappable middleware to stacks and pancake [hassox]
file README.textile Fri Nov 13 00:09:49 -0800 2009 update to link to the Hashie github repo in the... [hassox]
file Rakefile Tue Jan 12 13:29:54 -0800 2010 Updates the version [Daniel Neighman]
file TODO Fri Dec 11 01:06:51 -0800 2009 Updates for the new Usher. Moves the action ou... [hassox]
file VERSION Tue Jan 12 13:29:54 -0800 2010 Updates the version [Daniel Neighman]
directory bin/ Fri Nov 13 21:09:19 -0800 2009 updates to allow for -v/--version and -h [Jack Dempsey]
directory lib/ Tue Jan 12 01:48:57 -0800 2010 Updates the stack to route to a destination tha... [Daniel Neighman]
file pancake.gemspec Tue Jan 12 13:29:54 -0800 2010 Updates the version [Daniel Neighman]
directory spec/ Fri Dec 11 01:06:51 -0800 2009 Updates for the new Usher. Moves the action ou... [hassox]
README.textile

Pancake

Modern web applications often need to be constructed by interacting with numerous independent service/applications, each optimised for a specific task. However, making each of these components interact and work cohesively is difficult and time consuming. Pancake helps you construct clean, modular, re-usable code to create your web masterpieces.

Pancake is primarily a tool for making rack applications. Rack has come up in the Ruby web world as the framework that matters when developing web applications. All the major frameworks use it, although many of the application frameworks and their middlewares are not really re-usable away from their specific implementations yet.

Pancake addresses this by making Rack the fundamental building block of an application. It provides very useful helpers on top of Rack that assist in constructing Rack stacks as mixins. Almost all key aspects of web frameworks are covered in Pancake as mixins to help you create your own re-usable Rack Stacks without worrying about the really low level plumbing.

This README is a very high level overview only and doesn’t really cover a great deal of Pancake unfortunately.

Stacks

While Rack provides the low level framework for building web applications on, Pancake provides a stack as a place to start your application. A stack provides a whole bunch of behavior for you including a router and middleware stack. The stack will accept any valid Rack application as the endpoint. If you’re not familiar with what makes a valid Rack application it basically comes down to an object that receives the “call” method with exactly one argument, the environment hash, and returns an array with exactly 3 elements. The status, headers and body.

The general form of a stack is as follows:


-----------------------------------
|                                 |
|           Router                |
|                                 |
|---------------------------------|
|           Middleware            |
|---------------------------------|
|           Middleware            |
|---------------------------------|
|           Middleware            |
|---------------------------------|
|                                 |
|           Application           |
|            Endpoint             |
|                                 |
-----------------------------------

Each stack has its own router, middleware stack and application endpoint. Stacks can be combined and also put inside Pancake.

Combined into a Pancake application it may look like this:


-------------------------------------------------------------------------------
|                                 Pancake Middleware                          |
|-----------------------------------------------------------------------------|
|                                 Pancake Middleware                          |
|-----------------------------------------------------------------------------|
|                                 Pancake Middleware                          |
|-----------------------------------------------------------------------------|
|                                                                             |
|  -----------------------------------   -----------------------------------  |
|  |                                 |   |                                 |  |
|  |           Router                |   |           Router                |  |
|  |                                 |   |                                 |  |
|  |---------------------------------|   |---------------------------------|  |
|  |           Middleware            |   |           Middleware            |  |
|  |---------------------------------|   |---------------------------------|  |
|  |           Middleware            |   |           Middleware            |  |
|  |---------------------------------|   |---------------------------------|  |
|  |           Middleware            |   |           Middleware            |  |
|  |---------------------------------|   |---------------------------------|  |
|  |                                 |   |                                 |  |
|  |           Application           |   |           Application           |  |
|  |            Endpoint             |   |            Endpoint             |  |
|  |                                 |   |                                 |  |
|  -----------------------------------   -----------------------------------  |
|                                                                             |
-------------------------------------------------------------------------------



Stacks are designed to work together. It’s trivially easy to mount one stack inside another, or just mount any valid rack application inside your stack. They’re also designed to be gemmed and used as libraries or full applications.

MyStack.router do |r|
  r.mount(OtherStack, "/stack/mount/point")
end

A stack doesn’t have to mount other stacks. Pancake stacks are full applications in their own right and can be used standalone as Rack endpoints, or as middleware.

Rails 3 is shipping with a pluggable router which can be the awesome Usher router. If you use Usher in your Rails app, you’ll be able to mount Pancake stacks directly. For now, you can mount them as Metals.

TODO: Demonstrate in a gist mounting pancake in a rails application

All stacks are namespaced. Pancake makes heavy use of namespacing to help construct applications concisely.

Middleware In Stacks

Middleware is a specialised kind of rack application that can either respond to a request, or pass it on to another application.

There’s two main places for middleware in Pancake. Pancake level, and stack level. If you attach middleware to Pancake itself, all requests will pass through those middlewares. If you attach it at the stack level, only that stack will have that middleware active for its requests.

When you use Pancake middleware management, you can name middleware, set middleware to come before or after other middleware, or be included or not based on a stack type label(s). Lets have a look at that.

class MyStack < Pancake::Stack
  stack(:session).use(Rack::Sessions::Cookie)
  stack(:auth, :after => :session).use(Warden::Manager, #...)

  stack(:bugz, :labels => [:development]).use(Rack::Bug)

  use(AlwaysUseThisMiddleware)
end

What can a stack do

Pancake provides a whole bunch of functionality as mixins.

  • BootLoaders
  • Router (Usher Based. Please read.)
  • Sophisticated Middlware Configuration
  • Stack/Custom Configuration
  • Path Management with multiple roots (makes working with gem stacks transparent)
  • Content Type negotiation
  • Inheritable Stacks
  • Inheritable Templating System (see below)

(See here for examples )

Stacks bundle these behaviors together into a cohesive unit.

The true power of a Pancake stack is that it is fully inheritable.

Stack Inheritance

When you inherit a stack, you’re really inheriting a full application. All the features above are inherited along with the stack class.

The easiest way to explain stack inheritance is perhaps with an example.

Let’s take the example of a basic application skeleton. Many times I’ve seen GitHub repos with pre-generated applications with some basic functionality. This is then cloned as a start point for further app development. It’s basically the same as a generator but a little more flexible. What if rather than generate an application skeleton with the basic functionality, you could construct a stack complete with router, default configuration, base templates, css and javascript, even mounted applications, and gem it up? Then when you want to start an application you just require the gem and inherit the stack class. This is what Pancake stacks provide.

class MyStack < SomeOtherStack; end # A full application 

Namespacing

Pancake makes heavy use of namespacing. Namespacing is a good idea to begin with, but inside mounted applications it’s a must. Embracing the constraint of namespacing provides many benefits. Avoiding unexpected name clashes, of course, is the big one.

Inheritable Inner Classes

There is a fairly unique issue when inheriting a full application. Model data may clash.

Normally when you inherit a class in Ruby, when you reference an inner class in the child, it’s actually a reference to the inner class in the parent. Let’s see why this is a problem:

class BlogStack < Pancake::Stack
  class Post < ActiveRecord::Base
    # post code
  end
end

class BobsBlog  < BlogStack; end
class RosesBlog < BlogStack; end

BobsBlog::Post == RosesBlog::Post == BlogStack::Post

Now, let’s mount those inside some master stack:

class MyMasterStack < Pancake::Stack
  router.mount(RobsBlog,      "/rob")
  router.mount(RosesBlog,     "/rose")
end

Now, Bob creates a post at his blog. Then goes away and creates the model (BlogStack::Post).

Now someone visits Rose’s blog. Because both Bob’s and Rose’s blogs reference the same BlogStack::User model, Bob’s post is fetched when we do a RosesBlog::Post.all

It’s efficient for Ruby to reference the parent class’s inner class like this, but it’s not always what we want.

Awesomely, some of the ORM’s include STI (Single Table Inheritance) that we can make use of. When using Pancake, we can set an inner class to inherit along with the parent. Lets have another look at that example:

class BlogStack < Pancake::Stack
  inheritable_inner_classes :Post

  class Post < ActiveRecord::Base
    # post code
  end
end

class BobsBlog  < BlogStack; end
class RosesBlog < BlogStack; end

class RosesOtherBlog  < RosesBlog; end

# Results in

class BobsBlog::Post        < BlogStack::Post; end
class RosesBlog::Post       < BlogStack::Post; end
class RosesOtherBlog::Post  < RosesBlog::Post; end

In this case STI will be activated and each stack will use its own STI version of the Post model, segregating the data between the stacks.

If your Data layer doesn’t support STI, there are on_inherit hooks for all classes provided by Pancake to make any changes you need to.

Mounting Applications

You can mount any valid Rack application inside Pancake. See Rack Spec for guidance on what a valid Rack application is.

Stacks look inside their root(s) for a “mounts” directory. Where they in-turn look for a sub-directory containing the file “pancake_init.rb”. When you want to mount an application you just include that file (and any support files) and the Pancake stack will load it.

my_stack
-- mounts
  -- another_app
    -- pancake_init.rb # Called to initialize the mounted app

Usually pancake_init.rb would just require the files for your application. But there’s nothing stopping you from defining the entire application in there if you wanted.

Inheritable Templates

Templates in Pancake are similar to Django templates. They define named content_blocks that can supply a default, but that can also be inherited from, and have their content combined with some other content or replaced. Unlike Django, templates are built on known Ruby templating languages like ERB and Haml. The templating system in Pancake is built on top of the great Tilt project.

This is best demonstrated with an example:

# base.html.erb
<html>
  <head><title>Hey There</title></head>
  <body>
  <h1>My Page</h1>

  <ul class='nav'>
    <% content_block :navigation do -%>
      <li><a href="/">Home</a></li>
    <% end -%>
  </ul>

  <hr/>

  <% content_block :content do- %>
    <div class='emtpy'>Nothing Found</div>
  <% end -%>

  <% content_block :footer do -%>
    Footer Text
  <% end -%>
</html>

# index.html.erb

<% inherits_from :base %>

<% content_block :navigation do -%>
  <%= content_block.super %>
  <li><a href="/index">Home</a></li>
<% end -%>

<% content_block :content do -%>
  # Stuff for the content
<% end -%>

When you render :index in your stack, the index template will be activated, rendering the :base template but replacing the content_block, :content, with new data. And appending to the :navigation block.

Templates can be inherited as many times as required, so you could inherit from the :index template to build on its content.

When specifying which template to inherit from, you can dynamically specify which template you’d like to use if it makes sense to do so.

At the moment only ERB, Erubis and Haml are supported with inheritable templates. Other Tilt templates are able to be rendered, but the inheritance features are not supported.

Short Stack

As part of the development of Pancake, the initial milestone was to develop a stack based on the awesome Sinatra framework. This was done as a way to develop Pancake to a simple level, not as a way to replace Sinatra. Pancake is very happy to mount Sinatra applications inside and make use of them.

A short stack is a resource focused. It uses the HTTP verbs and routes to access the code that defines a “resource”

Short stacks make use of content negotiation and views in addition to the normal stack behavior.

Get Started

Install

$ gem install pancake

Generate your stack

To start with a short stack use pancakes built in generator:

$ pancake-gen short my_stack

This will generate the basic stack for you. It’s generated as a gem and based on Jeweler so you can gem it up super easy.

class MyStack < Pancake::Stacks::Short
  add_root(__FILE__)

  router.mount(UserStack, "/users")

  provides :html, :xml, :json

  get "(/)",  :_name => :home do
    vars.some_models = SomeModel.all
    render :welcome
  end

  get "/new", :_name => :new do
    vars.some_model = SomeModel.new(params.some_model)
    render :new
  end

  post "(/)" do
    vars.some_model = SomeModel.new(params.some_model)
    if vars.some_model.save
      redirect url(:home)
    else
      render :new
    end
  end
end

The Vars

In the example above you can see the “vars” method. This method provides a place for variables on a per-request basis for later use. In this case, the data is stored so that we can access it in the views. It could just as easily be used to share information between middlewares. This is a Hashie::Mash which is like a Hash with indifferent access, that can access keys via method calls. The vars method is also aliased as “v”.

This is a way to separate the concerns of Controller and View. These contexts are supposed to be separate. Breaking the encapsulation of instance variables does not need to be done in a short stack.

Templates

Templates are searched for in (stack root)/views/.. and rendered.

Templates are separate in each sub-application and will not clash. They’re able to be looked up in parent stacks, however. Meaning, if you inherit from AStack, all of AStacks views will be used unless you replace them in the child stacks views directory.

URL generation

When you’re in a stack, you can call url(named_url) to generate a url. This will generate the url, taking into account the mount path for the stack.

You can generate urls for other stacks also by using the global Pancake.url helper.

Pancake.url(SomeStack, :named_url)

There’s a whole bunch more to Pancake than I can go over here. But hopefully I’ve been able to provide a taste of what it is and why I think it’s pretty awesome in the upcoming Rack landscape.

Community

IRC: #pancake
Google Group
Twitter: #pancakestacks (hashtag)
Blog / News

Bugs

If you find a bug please report it at our tails account, create a fork, and publish your fix in a topic branch.

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don’t break it in a
    future version unintentionally.
  • Commit, do not mess with rakefile, version, or history.
    (if you want to have your own version, that is fine but
    bump version in a commit by itself I can ignore when I pull)
  • send me a pull request. Bonus points for topic branches.

Copyright

Copyright © 2009 Daniel Neighman. See LICENSE for details.

Blog | Support | Training | Contact | API | Status | Twitter | Help | Security
© 2010 GitHub Inc. All rights reserved. | Terms of Service | Privacy Policy
Powered by the Dedicated Servers and
Cloud Computing of Rackspace Hosting®
Dedicated Server