Class: BuildingBlocks::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/building_blocks/base.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &block) ⇒ Object (protected)

If a method is missing, we’ll assume the user is starting a new block group by that missing method name



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/building_blocks/base.rb', line 301

def method_missing(m, *args, &block)
  options = args.extract_options!

  # If the specified block group has already been defined, it is simply returned here for iteration.
  #  It will consist of all the blocks used in this block group that have yet to be rendered,
  #   as the call for their use occurred before the template was rendered (where their definitions likely occurred)
  return self.block_groups[m] unless self.block_groups[m].nil?

  # Allows for nested block groups, store the current block positions array and start a new one
  original_queued_blocks = self.queued_blocks
  self.queued_blocks = []
  self.block_groups[m] = self.queued_blocks

  # Capture the contents of the block group (this will only capture block definitions and block renders; it will ignore anything else)
  view.capture(global_options.merge(options), &block) if block_given?

  # restore the original block positions array
  self.queued_blocks = original_queued_blocks
  nil
end

Instance Attribute Details

#anonymous_block_numberObject

counter, used to give unnamed blocks a unique name



17
18
19
# File 'lib/building_blocks/base.rb', line 17

def anonymous_block_number
  @anonymous_block_number
end

#block_groupsObject

A Hash of queued_blocks arrays; a new array is started when method_missing is invoked



20
21
22
# File 'lib/building_blocks/base.rb', line 20

def block_groups
  @block_groups
end

#blocksObject

Hash of block names to BuildingBlocks::Container objects



11
12
13
# File 'lib/building_blocks/base.rb', line 11

def blocks
  @blocks
end

#global_optionsObject

These are the options that are passed into the initalize method



23
24
25
# File 'lib/building_blocks/base.rb', line 23

def global_options
  @global_options
end

#queued_blocksObject

Array of BuildingBlocks::Container objects, storing the order of blocks as they were queued



14
15
16
# File 'lib/building_blocks/base.rb', line 14

def queued_blocks
  @queued_blocks
end

#templates_folderObject

The default folder to look in for global partials



26
27
28
# File 'lib/building_blocks/base.rb', line 26

def templates_folder
  @templates_folder
end

#use_partialsObject

Boolean variable for whether BuildingBlocks should attempt to render blocks as partials if a defined block cannot be found



32
33
34
# File 'lib/building_blocks/base.rb', line 32

def use_partials
  @use_partials
end

#use_partials_for_before_and_after_hooksObject

Boolean variable for whether BuildingBlocks should attempt to render blocks before and after blocks as partials if no before or after blocks exist



35
36
37
# File 'lib/building_blocks/base.rb', line 35

def use_partials_for_before_and_after_hooks
  @use_partials_for_before_and_after_hooks
end

#variableObject

The variable to use when rendering the partial for the templating feature (by default, “blocks”)



29
30
31
# File 'lib/building_blocks/base.rb', line 29

def variable
  @variable
end

#viewObject

a pointer to the ActionView that called BuildingBlocks



8
9
10
# File 'lib/building_blocks/base.rb', line 8

def view
  @view
end

Instance Method Details

#after(name, options = {}, &block) ⇒ Object Also known as: append

Add a block to render after another block. This after block will be put into an array so that multiple

after blocks may be queued. They will render in the order in which they are declared when the
"blocks#render" method is called. Any options specified to the after block will override any options
specified in the block definition.
 <% blocks.define :wizard, :option1 => 1, :option2 => 2 do |options| %>
   Step 2 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <% blocks.after :wizard, :option1 => 3 do
   Step 3 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <% blocks.after :wizard, :option2 => 4 do
   Step 4 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <%= blocks.use :wizard %>

 <!-- Will render:
   Step 2 (:option1 => 1, :option2 => 2)<br />
   Step 3 (:option1 => 3, :option2 => 2)<br />
   Step 4 (:option1 => 1, :option2 => 4)<br />
 -->

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

Options:

name

The name of the block to render this code after when that block is rendered

options

Any options to specify to the after block when it renders. These will override any options specified when the block was defined.

block

The block of code to render after another block



281
282
283
284
# File 'lib/building_blocks/base.rb', line 281

def after(name, options={}, &block)
  self.queue_block_container("after_#{name.to_s}", options, &block)
  nil
end

#before(name, options = {}, &block) ⇒ Object Also known as: prepend

Add a block to render before another block. This before block will be put into an array so that multiple

before blocks may be queued. They will render in the order in which they are declared when the
"blocks#render" method is called. Any options specified to the before block will override any options
specified in the block definition.
 <% blocks.define :wizard, :option1 => 1, :option2 => 2 do |options| %>
   Step 2 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <% blocks.before :wizard, :option1 => 3 do
   Step 0 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <% blocks.before :wizard, :option2 => 4 do
   Step 1 (:option1 => <%= options[option1] %>, :option2 => <%= options[option2] %>)<br />
 <% end %>

 <%= blocks.use :wizard %>

 <!-- Will render:
   Step 0 (:option1 => 3, :option2 => 2)<br />
   Step 1 (:option1 => 1, :option2 => 4)<br />
   Step 2 (:option1 => 1, :option2 => 2)<br />
 -->

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

Options:

name

The name of the block to render this code before when that block is rendered

options

Any options to specify to the before block when it renders. These will override any options specified when the block was defined.

block

The block of code to render before another block



242
243
244
245
# File 'lib/building_blocks/base.rb', line 242

def before(name, options={}, &block)
  self.queue_block_container("before_#{name.to_s}", options, &block)
  nil
end

#define(name, options = {}, &block) ⇒ Object

Define a block, unless a block by the same name is already defined.

<%= blocks.define :some_block_name, :parameter1 => "1", :parameter2 => "2" do |options| %>
  <%= options[:parameter1] %> and <%= options[:parameter2] %>
<% end %>

Options:

name

The name of the block being defined (either a string or a symbol)

options

The default options for the block definition. Any or all of these options may be overrideen by whomever calls “blocks.render” on this block.

block

The block that is to be rendered when “blocks.render” is called for this block.



59
60
61
62
# File 'lib/building_blocks/base.rb', line 59

def define(name, options={}, &block)
  self.define_block_container(name, options, &block)
  nil
end

#defined?(name) ⇒ Boolean

Checks if a particular block has been defined within the current block scope.

<%= blocks.defined? :some_block_name %>

Options:

name

The name of the block to check

Returns:

  • (Boolean)


42
43
44
# File 'lib/building_blocks/base.rb', line 42

def defined?(name)
  !blocks[name.to_sym].nil?
end

#evaluated_proc(*args) ⇒ Object



292
293
294
295
296
# File 'lib/building_blocks/base.rb', line 292

def evaluated_proc(*args)
  return nil unless args.present?
  v = args.pop
  v.is_a?(Proc) ? v.call(*args) : v
end

#evaluated_procs(*args) ⇒ Object



287
288
289
290
# File 'lib/building_blocks/base.rb', line 287

def evaluated_procs(*args)
  options = args.extract_options!
  options.inject({}) { |hash, (k, v)| hash[k] = (v.is_a?(Proc) ? v.call(*args) : v); hash} unless options.nil?
end

#queue(*args, &block) ⇒ Object

Queue a block for later rendering, such as within a template.

<%= BuildingBlocks::Base.new(self).render_template("shared/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 %>

<!-- In /app/views/shared/wizard -->
<% 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 %>

<!-- Will render: Step 1 | My overridden Step 2 | Step 3 | Anonymous Step 4-->

Options:

*args

The options to pass in when this block is rendered. These will override any options provided to the actual block definition. Any or all of these options may be overriden by whoever calls “blocks.render” on this block. Usually the first of these args will be the name of the block being queued (either a string or a symbol)

block

The optional block definition to render when the queued block is rendered



161
162
163
164
# File 'lib/building_blocks/base.rb', line 161

def queue(*args, &block)
  self.queued_blocks << self.define_block_container(*args, &block)
  nil
end

#render(name_or_container, *args, &block) ⇒ Object Also known as: use

Render a block, first rendering any “before” blocks, then rendering the block itself, then rendering any “after” blocks. BuildingBlocks will make four different attempts to render block:

1) Look for a block that has been defined inline elsewhere, using the blocks.define method:
   <% blocks.define :wizard do |options| %>
     Inline Block Step#<%= options[:step] %>.
   <% end %>

   <%= blocks.render :wizard, :step => @step %>
2) Look for a partial within the current controller's view directory:
   <%= blocks.render :wizard, :step => @step %>

   <!-- In /app/views/pages/_wizard.html.erb (assuming it is the pages controller running): -->
   Controller-specific Block Step# <%= step %>.
3) Look for a partial with the global blocks view directory (by default /app/views/blocks/):
   <%= blocks.render :wizard, :step => @step %>

   <!-- In /app/views/blocks/_wizard.html.erb: -->
   Global Block Step#<%= step %>.
4) Render the default implementation for the block if provided to the blocks.render call:
   <%= blocks.render :wizard, :step => @step do |options| do %>
     Default Implementation Block Step#<%= options %>.
   <% end %>

Options:

name

The name of the block to render (either a string or a symbol)

*args

Any arguments to pass to the block to be rendered (and also to be passed to any “before” and “after” blocks).

block

The default block to render if no such block block that is to be rendered when “blocks.render” is called for this block.



115
116
117
118
119
120
121
# File 'lib/building_blocks/base.rb', line 115

def render(name_or_container, *args, &block)
  buffer = ActiveSupport::SafeBuffer.new
  buffer << render_before_blocks(name_or_container, *args)
  buffer << render_block(name_or_container, *args, &block)
  buffer << render_after_blocks(name_or_container, *args)
  buffer
end

#render_template(partial, &block) ⇒ Object

Render a partial, treating it as a template, and any code in the block argument will impact how the template renders

<%= BuildingBlocks::Base.new(self).render_template("shared/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 %>

<!-- In /app/views/shared/wizard -->
<% 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 %>

<!-- Will render: Step 1 | My overridden Step 2 | Step 3 | Anonymous Step 4-->

Options:

partial

The partial to render as a template

block

An optional block with code that affects how the template renders



201
202
203
204
205
206
207
# File 'lib/building_blocks/base.rb', line 201

def render_template(partial, &block)
  render_options = global_options.clone
  render_options[self.variable] = self
  render_options[:captured_block] = view.capture(self, &block) if block_given?

  view.render partial, render_options
end

#replace(name, options = {}, &block) ⇒ Object

Define a block, replacing an existing block by the same name if it is already defined.

<%= blocks.define :some_block_name, :parameter1 => "1", :parameter2 => "2" do |options| %>
  <%= options[:parameter1] %> and <%= options[:parameter2] %>
<% end %>

<%= blocks.replace :some_block_name, :parameter3 => "3", :parameter4 => "4" do |options| %>
  <%= options[:parameter3] %> and <%= options[:parameter4] %>
<% end %>

Options:

name

The name of the block being defined (either a string or a symbol)

options

The default options for the block definition. Any or all of these options may be overrideen by whomever calls “blocks.render” on this block.

block

The block that is to be rendered when “blocks.render” is called for this block.



80
81
82
83
84
# File 'lib/building_blocks/base.rb', line 80

def replace(name, options={}, &block)
  blocks[name.to_sym] = nil
  self.define_block_container(name, options, &block)
  nil
end