BuildingBlocks

RDocs | Screencast Part 1

BuildingBlocks is an intricate way of rendering blocks of code, while combining some of the best features of content blocks and partials, and adding several new features that go above and beyond what a simple content_for with yield or a render :partial is capable of doing.

  1. Automatically determines whether you’re rendering a content block (i.e. a block of code that you have defined using similar syntax to content_for) or a partial, with exactly the same syntax

  2. Pass parameters into your blocks of code, regardless of whether your block of code is a content block or a partial (something content_for with yield is incapable of doing)

  3. Use “before” and “after” hooks to designate code that should be rendered before and after a block of code that is to be rendered

  4. Define your blocks of code as either a global partial, a controller-specific partial, or an inline block of code

  5. Provides four different ways to define your block of code: as a global partial, a controller-specific partial, or an inline block of code, all with the same syntax usage.

  6. Provides a very powerful way of defining templates to build reusable UI components with minimal code, as was done with table-for

Author’s Note

To fully appreciate how powerful BuildingBlocks can be, i highly recommend checking out the project table-for first. This is a gem that was built using BuildingBlocks with very few lines of code, and illustrates how easily the templating feature of BuildingBlocks can be used to build extremely useful reusable UI components.

Installation

In Rails 3, add this to your Gemfile.

gem "building-blocks"

Defining and using blocks

The syntax for defining and using blocks is similar to how content_for and yield are used. At its simplest form:

<% blocks.define :my_block do %>
  My code to render
<% end %>

<!-- Elsewhere, you can render the block as follows -->
<%= blocks.render :my_block %>

<!-- Will render: My code to render -->

Passing parameters to blocks

Parameters may also be passed into defined blocks:

<% blocks.define :my_block do |options| %>
  The user of this block passed in "<%= options[:my_parameter] %>" as :my_parameter.
<% end %>

<!-- Elsewhere, you can render the block as follows -->
<%= blocks.render :my_block, :my_parameter => "The value I'm passing in"  %>

<!-- Will render: The user of this block passed in "The value I'm passing in" as :my_parameter. -->

<!-- If the anticipated parameters are not passed: -->
<%= blocks.render :my_block %>

<!-- Will render: The user of this block passed in "" as :my_parameter. -->

The parameters are not required, but unexpected results might occur if the “necessary” parameters are not passed in.

Passing non-hash parameters into defined blocks:

<% blocks.define :my_block do |first_parameter, second_parameter, options| %>
  First parameter: <%= first_parameter %>, Second parameter: <%= second_parameter %>, Third parameter: <%= options[:third_parameter] %>
<% end %>

<!-- Elsewhere, you can render the block as follows -->
<%= blocks.render :my_block, "Value 1", 2, :third_parameter => "Value 3" %>

<!-- Will render: First parameter: Value 1, Second parameter: 2, Third parameter: Value 3 -->

Providing default values for parameters to blocks

In the last example, the parameter the block was expecting was not passed in. For this reason, it is possible to specify default values for the parameters when you define a block. If parameters are passed in when the block is rendered, the values passed in override the default values.

<% blocks.define :my_block, :parameter1 => "Parameter 1", :parameter2 => "Parameter 2" do |options| %>
  The values specified are :parameter1 = "<%= options[:parameter1] %>", :parameter2 = "<%= options[:parameter2] %>"
<% end %>

<!-- Elsewhere, you can render the block as follows (specifying zero, one, or both of the parameters the block uses) -->
<%= blocks.render :my_block %><br />
<%= blocks.render :my_block, :parameter1 => "New Parameter 1" %><br />
<%= blocks.render :my_block, :parameter2 => "New Parameter 2" %><br />
<%= blocks.render :my_block, :parameter1 => "New Parameter 1", :parameter2 => "New Parameter 2" %>

<!--
Will render:
The values specified are :parameter1 = "Parameter 1", :parameter2 = "Parameter 2"
The values specified are :parameter1 = "New Parameter 1", :parameter2 = "Parameter 2"
The values specified are :parameter1 = "Parameter 1", :parameter2 = "New Parameter 2"
The values specified are :parameter1 = "New Parameter 1", :parameter2 = "New Parameter 2"
-->

Providing a default definition for a block

What happens if you attempt to render a block that hasn’t been “define”d? Nothing will get rendered.

However, you may want to provide a default definition for a block to render if such a block was never “define”d. You can do this as follows:

<%= blocks.render :my_block, :my_parameter_1 => "Parameter 1" do %>
  This is my default definition of :my_block.
<% end %>

In this case, BuildingBlocks will see if any block by the name :my_block has ever been defined. When it doesn’t find one, it will simply render the default definition and you will see:

This is my default definition of :my_block.

If however, you have defined :my_block elsewhere, it would have used that definition:

<% blocks.define :my_block do |options| %>
  Some other definition of :my_block with :my_parameter_1 set to "<%= options[:my_parameter_1] %>"
<% end %>

<!-- Elsewhere, you can render the block as follows -->
<%= blocks.render :my_block, :my_parameter_1 => "Parameter 1" do %>
  This is my default definition of :my_block.
<% end %>

<!-- Will render: Some other definition of :my_block with :my_parameter_1 set to "Parameter 1" -->
<!-- (since the block was defined, i.e. the default definition is not needed) -->

Using “before” and “after filters”

“Before” and “After” hooks render code before and after the code produced by a “blocks.render” call. A practical example of this would be adding view-specific javascript and stylesheet includes to a global layout file.

In your application.html layout file, you might use this as follows:

<html>
  <head>
    <%= blocks.render :includes do %>
      <%= blocks.render :stylesheets do %>
        <%= stylesheet_link_tag "application", :media => "all" %>
      <% end %>
      <%= blocks.render :javscripts do %>
        <%= javascript_include_tag "application" %>
      <% end %>
      <%= csrf_meta_tags %>
    <% end %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Then, in a specific view that is rendered using this layout, you can add stylesheets before or after the list of stylesheets includes, before or after the list of javascript includes, or before or after the entire list of stylesheet and javascript includes. For example, index.html.erb might add in more stylesheets and javascripts:

<% blocks.before :includes do %>
  <%= stylesheet_link_tag "before_includes" %>
<% end %>

<% blocks.after :includes do %>
  <%= stylesheet_link_tag "after_includes" %>
<% end %>

<% blocks.before :stylesheets do %>
  <%= stylesheet_link_tag "before_stylesheets" %>
<% end %>

<% blocks.after :stylesheets do %>
  <%= stylesheet_link_tag "after_stylesheets" %>
<% end %>

<% blocks.before :javascripts do %>
  <%= javascript_include_tag "before_javascripts" %>
<% end %>

<% blocks.after :javascripts do %>
  <%= javascript_include_tag "after_javascripts" %>
<% end %>

<!--
When index.html.erb is rendered, it will output:
<html>
  <head>
    <link href="/stylesheets/first_overall_stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
    <link href="/stylesheets/stylesheet_before_jquery.css" media="screen" rel="stylesheet" type="text/css" />
    <link href="/stylesheets/jquery.css" media="screen" rel="stylesheet" type="text/css" />
    <link href="/stylesheets/stylesheet_after_jquery.css" media="screen" rel="stylesheet" type="text/css" />
    <script src="/javascripts/javascript_before_jquery.js" type="text/javascript"></script>
    <script src="/javascripts/jquery.js" type="text/javascript"></script>
    <script src="/javascripts/javascript_after_jquery.js" type="text/javascript"></script>
    <link href="/stylesheets/last_overall_stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
  </head>
  <body>
  </body>
</html>
 -->

(An alternative syntax to “blocks.before” and “blocks.after” would be, respectively, “blocks.prepend” and “blocks.append”)

Blocks as Partials

Using exactly the same syntax for “using” blocks, one can put the code to be rendered in it’s own separate file (in a partial). When “blocks.render :some_block” is called, the system will first look for a block defined inline (i.e. one that has been defined using “blocks.define :some_block”). Failing to find that, it will look for a partial by the same name in your current controller’s view directory. Failing to find that partial, it will look for a partial in the global blocks’ directory (by default, /app/views/blocks). Any parameters passed in as a hash will be initialized in the partial as local variables. And failing to find that, it will see if a default implementation has been provided for the block and render it if one has been specified.

As an example, consider the following code, running in a view for PagesController:

<%= blocks.render :wizard, :step => @step %>

<!-- 1) Check and see if there was a block defined called "wizard" somewhere prior to its render... No? then... -->
<!-- 2) Check and see if there is a controller-specific partial /app/views/pages/wizard.html.erb. No? Then... -->
<!-- 3) Check and see if there is a global partial /app/views/blocks/wizard.html.erb. No? Then... -->
<!-- 4) Check and see if there is a default definition provided for "wizard", i.e. specified in the "blocks.render" call... No? Then render nothing -->

Let’s look at each example individually, written in the order that BuildingBlocks attempts to render them:

1) Inline definition of a block:

<% blocks.define :wizard do |options| %>
  Inline Block Step#<%= options[:step] %>.
<% end %>

<!-- Elsewhere, you can render the block as follows -->
<%= blocks.render :wizard, :step => @step %>

2) Controller-specific partial:

<%= blocks.render :wizard, :step => @step %>

<!-- In /app/views/pages/_wizard.html.erb: -->
Controller-specific Block Step# <%= step %>.

3) Global partial:

<%= blocks.render :wizard, :step => @step %>

<!-- In /app/views/blocks/_wizard.html.erb: -->
Global Block Step#<%= step %>.

4) Default implementation of a block:

<%= blocks.render :wizard, :step => @step do |options| do %>
  Default Implementation Block Step#<%= options %>.
<% end %>

Overall Render Order

Putting all the pieces together from the previous examples, here is what BuildingBlock is doing when a block is rendered:

Templating

The most advanced feature of BuildingBlocks is the ability to utilize it for templating and creating your own DTD specifications for the components you write.

As an example, consider table-for, a gem that was written with minimal codes that provides its user with a very nice, easy-to-use table builder. A sample usage might look something like:

<%= table_for @users, :table_html => {:style => "border: 1px solid black"},
                      :sortable => true,
                      :row_html => {:class => lambda { cycle('even', 'odd')},
                                    :id => lambda {|user| "user-#{user.id}"}} do |table| %>
  <%= table.column :edit %>
  <%= table.column :show %>
  <%= table.column :email, :label => "Email Address" %>
  <%= table.column :label => "Full Name", :sortable => false, :header_html => {:style => "color:orange"} do |user| %>
    <%= "#{user.first_name} #{user.last_name}" %>
  <% end %>
  <%= table.column :delete %>
<% end %>

Templating allows the code to render a partial, but dynamically change how that partial is rendered from outside its rendering. So in the above example, there is a table_for partial that gets rendered, but before it is rendered, the block provided to the table_for call will be parsed so that the table_for template (partial) knows what to render, and occasionally, how to render to render it.

As an easier example, consider the following call in a view file:

<%= BuildingBlocks.render_template(self, "blocks/wizard") do |blocks| %>
  <% blocks.queue :step1 %>
  <% blocks.queue :step2 do %>
    My Overridden Step 2 |
  <% end %>
  <% blocks.queue :step3 %>
  <% blocks.queue do %>
    | Anonymous Step 4
    <% end %>
<% end %>

Here, a template is provided (“/app/views/blocks/_wizard.html.erb”) and four blocks are queued (step1, step2, step3, and an anonymous step). Now, let’s look at the template:

<!-- /app/views/blocks/_wizard.html.erb -->
<% blocks.define :step1 do %>
  Step 1 |
<% end %>

<% blocks.define :step2 do %>
  Step 2 |
<% end %>

<% blocks.define :step3 do %>
  Step 3
<% end %>

<% blocks.queued_blocks.each do |block| %>
  <%= blocks.render block %>
<% end %>

Step1, Step2, and Step3 are all provided default definitions. Step2’s definition is overridden from the outside (i.e. from the view that renders the template). Since all of the queued blocks are rendered, the anonymous block (Step4) will also get rendered. The resulting output will be:

Step 1 | My Overridden Step 2 | Step 3 | Anonymous Step4

Notice the order of the queued blocks was preserved, Step2’s definition was successfully overridden, and the anonymous definition for Step4 was also rendered.

This technique could also very easily be used to do something like wrapping content in some complicated markup, such is wrapping code in html tags to display as a header container:

<%= BuildingBlocks.render_template(self, "blocks/header_container") do |blocks| %>
  My code to wrap
<% end %>

<!-- In /app/views/blocks/_header_container.html.erb -->
<div class="header">
  <div class="wrapper">
    <%= captured_block %>
  </div>
</div>

What will get rendered will be the following:

<div class="header">
  <div class="wrapper">
    My code to wrap
  </div>
</div>

The code called from the view could easily be extracted in a helper method as follows:

def header_container(options={}, &block)
  BuildingBlocks.render_template(self, "blocks/header_container", options, &block) 
end

Then it could be called from the view as follows:

<%= header_container do %>
  My code to wrap
<% end %>

VIDEO TUTORIAL TO COME SHOWING HOW TABLE_FOR CAN BE BUILT FROM SCRATCH

Block Groups

Another advanced feature of BuildingBlocks is the ability to queue up multiple different sets of blocks in different queues, called block groups. It will most often be used in conjunction with the templating feature described above and is best demonstrated with an example:

<%= BuildingBlocks::Base.new(self).render_template("blocks/my_layout") do |blocks| %>
  <% blocks.left_column do %>
    <%= blocks.queue :left_column_block1 %>
    <%= blocks.queue :left_column_block2 do %>
      Some random content on the left
    <% end %>
  <% end %>
  <% blocks.right_column do %>
    <%= blocks.queue :right_column_block1 %>
    <%= blocks.queue :right_column_block2 do %>
      Some random content on the right
    <% end %>
  <% end %>
<% end %>

In this example, two separate block groups are created, one for the left column on the page, one for the right, both of which will contain an ordered list (queue) of blocks. You start a new block group by invoking a method on “blocks” that does not exist, such as :left_column / :right_column. Then, in the template that renders the content, the queue of left and right column blocks can be iterated over and rendered as follows:

<!-- In /app/views/blocks/_my_layout.html.erb -->
<div style="float:left; width: 50%">
  <% blocks.left_column.each do |block| %>
    <%= blocks.render block %>
  <% end %>
</div>
<div style="float:left; width: 50%">
  <% blocks.right_column.each do |block| %>
    <%= blocks.render block %>
  <% end %>
</div>

Questions or Problems?

If you have any issues with BuildingBlocks which you cannot find the solution to in the documentation, please add an issue on GitHub or fork the project and send a pull request.

Special Thanks

Thanks to Todd Fisher of LivingSocial for implementation help and setup of gem and Jon Phillips of LivingSocial for suggestions and use case help.