Class: Blocks::Base

Inherits:
Object
  • Object
show all
Includes:
CallWithParams
Defined in:
lib/blocks/base.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(view, options = {}) ⇒ Base

Returns a new instance of Base.



424
425
426
427
428
429
430
# File 'lib/blocks/base.rb', line 424

def initialize(view, options={})
  self.view = view
  self.global_options = Blocks.config.merge(options)
  self.skipped_blocks = HashWithIndifferentAccess.new
  self.blocks = HashWithIndifferentAccess.new
  self.anonymous_block_number = 0
end

Instance Attribute Details

#anonymous_block_numberObject

counter, used to give unnamed blocks a unique name



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

def anonymous_block_number
  @anonymous_block_number
end

#blocksObject

Hash of block names to Blocks::Container objects



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

def blocks
  @blocks
end

#global_optionsObject

These are the options that are passed into the initalize method



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

def global_options
  @global_options
end

#skipped_blocksObject

Hash of block names that have been explicitely skipped



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

def skipped_blocks
  @skipped_blocks
end

#viewObject

a pointer to the ActionView that called Blocks



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

def view
  @view
end

Instance Method Details

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

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.render :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



364
365
366
367
# File 'lib/blocks/base.rb', line 364

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

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

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

around blocks may be queued. They will render in the order in which they are declared when the
"blocks#render" method is called, with the last declared around block being rendered as the outer-most code, and
the first declared around block rendered as the inner-most code. Any options specified to the after block will override any options
specified in the block definition. The user of an around block must declare a block with at least one parameter and
should invoke the #call method on that argument.

 <% blocks.define :my_block do %>
   test
 <% end %>

 <% blocks.around :my_block do |content_block| %>
   <h1>
     <%= content_block.call %>
   </h1>
 <% end %>

 <% blocks.around :my_block do |content_block| %>
   <span style="color:red">
     <%= content_block.call %>
   </span>
 <% end %>

 <%= blocks.render :my_block %>

 <!-- Will render:
 <h1>
   <span style="color:red">
     test
   </span>
 </h1>

Options:

name

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

options

Any options to specify to the around 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



411
412
413
414
# File 'lib/blocks/base.rb', line 411

def around(name, options={}, &block)
  self.add_block_container_to_list("around_#{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.render :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



325
326
327
328
# File 'lib/blocks/base.rb', line 325

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

#content_tag_with_block(tag, tag_html, *args, &block) ⇒ Object



416
417
418
419
420
421
422
# File 'lib/blocks/base.rb', line 416

def (tag, tag_html, *args, &block)
  if tag
    view.(tag, view.capture(&block), call_each_hash_value_with_params(tag_html, *args))
  else
    view.capture(&block)
  end
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 or a Proc)

options

The default options for the block definition. Any or all of these options may be overridden by whomever calls “blocks.render” on this block. If :collection => some_array, Blocks will assume that the first argument is a Proc and define a block for each object in the collection

block

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



46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/blocks/base.rb', line 46

def define(name, options={}, &block)
  collection = options.delete(:collection)

  if collection
    collection.each do |object|
      define(call_with_params(name, object, options), options, &block)
    end
  else
    self.define_block_container(name, options, &block)
  end

  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)


27
28
29
# File 'lib/blocks/base.rb', line 27

def defined?(name)
  !blocks[name].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. Additionally, a collection may also be passed in, and Blocks will render an the block, along with corresponding before and after blocks for each element of the collection. Blocks will make either two or four different attempts to render the block, depending on how use_partials is globally set, or an option is passed in to the render call to either use partials or skip partials:

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) [IF use_partials is globally set to true or passed in as a runtime option,
    and skip_partials is not passed in as a runtime option]
   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) [IF use_partials is globally set to true or passed in as a runtime option,
    and skip_partials is not passed in as a runtime option]
   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_or_container

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). The last argument in the list can be a hash and can include the following special options:

[:collection]
  The collection of elements to render blocks for
[:as]
  The variable name to assign the current element in the collection being rendered over
[:wrap_with]
  The content tag to render around this block (For example: :wrap_with => {:tag => TAG_TYPE, :class => "my-class", :style => "border: 1px solid black"})
[:wrap_each]
  The content tag to render around each item in a collection (For example: :wrap_each { :class => lambda { cycle("even", "odd") }})
[:use_partials]
  Overrides the globally defined use_partials and tells Blocks to render partials in trying to render a block
[:skip_partials]
  Overrides the globally defined use_partials and tells Blocks to not render any partials in trying to render a block
block

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



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/blocks/base.rb', line 151

def render(name_or_container, *args, &block)
  options = args.extract_options!
  collection = options.delete(:collection)
  name = extract_block_name name_or_container
  if skipped_blocks[name] && global_options.skip_applies_to_surrounding_blocks
    return
  end

  buffer = ActiveSupport::SafeBuffer.new

  wrap_with = options.delete(:wrap_with) || {}

  if collection
    as = options.delete(:as)
    wrap_each = options.delete(:wrap_each) || {}

    buffer = (wrap_with[:tag], wrap_with.except(:tag), *args) do
      collection.each_with_index do |object, index|
        cloned_args = args.clone
        cloned_args.unshift(object)
        cloned_options = options.clone
        cloned_options[:current_index] = index
        cloned_options = cloned_options.merge(object.options) if object.is_a?(Blocks::Container)
        cloned_args.push(cloned_options)

        block_name = call_with_params(name_or_container, *cloned_args)
        as_name = (as.presence || block_name).to_sym
        cloned_options[as_name] = object
        cloned_options[:wrap_with] = wrap_each

        buffer << render(block_name, *cloned_args, &block)
      end
      buffer
    end
  else
    args.push(options)

    if global_options.merge(options)[:wrap_before_and_after_blocks]
      buffer << (wrap_with[:tag], wrap_with.except(:tag), *args) do
        temp_buffer = ActiveSupport::SafeBuffer.new
        temp_buffer << render_before_blocks(name_or_container, *args)
        temp_buffer << render_block_with_around_blocks(name_or_container, *args, &block)
        temp_buffer << render_after_blocks(name_or_container, *args)
      end
    else
      buffer << render_before_blocks(name_or_container, *args)
      buffer << (wrap_with[:tag], wrap_with.except(:tag), *args) do
        render_block_with_around_blocks(name_or_container, *args, &block)
      end
      buffer << render_after_blocks(name_or_container, *args)
    end
  end

  buffer
end

#render_with_partials(name_or_container, *args, &block) ⇒ Object

Render a block, first rendering any “before” blocks, then rendering the block itself, then rendering any “after” blocks. Additionally, a collection may also be passed in, and Blocks will render an the block, along with corresponding before and after blocks for each element of the collection. Blocks 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_or_container

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). The last argument in the list can be a hash and can include the following special options:

[:collection]
  The collection of elements to render blocks for
[:as]
  The variable name to assign the current element in the collection being rendered over
[:wrap_with]
  The content tag to render around this block (For example: :wrap_with => {:tag => TAG_TYPE, :class => "my-class", :style => "border: 1px solid black"})
[:wrap_each]
  The content tag to render around each item in a collection (For example: :wrap_each { :class => lambda { cycle("even", "odd") }})
block

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



285
286
287
288
289
290
# File 'lib/blocks/base.rb', line 285

def render_with_partials(name_or_container, *args, &block)
  options = args.extract_options!
  options[:use_partials] = true
  args.push(options)
  render(name_or_container, *args, &block)
end

#render_without_partials(name_or_container, *args, &block) ⇒ Object

Render a block, first rendering any “before” blocks, then rendering the block itself, then rendering any “after” blocks. Additionally, a collection may also be passed in, and Blocks will render an the block, along with corresponding before and after blocks for each element of the collection. Blocks will make two 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) 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_or_container

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). The last argument in the list can be a hash and can include the following special options:

[:collection]
  The collection of elements to render blocks for
[:as]
  The variable name to assign the current element in the collection being rendered over
[:wrap_with]
  The content tag to render around this block (For example: :wrap_with => {:tag => TAG_TYPE, :class => "my-class", :style => "border: 1px solid black"})
[:wrap_each]
  The content tag to render around each item in a collection (For example: :wrap_each { :class => lambda { cycle("even", "odd") }})
block

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



238
239
240
241
242
243
# File 'lib/blocks/base.rb', line 238

def render_without_partials(name_or_container, *args, &block)
  options = args.extract_options!
  options[:skip_partials] = true
  args.push(options)
  render(name_or_container, *args, &block)
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 overridden by whomever calls “blocks.render” on this block.

block

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



76
77
78
79
80
# File 'lib/blocks/base.rb', line 76

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

#skip(name) ⇒ Object

Skip the rendering of a particular block when blocks.render is called for the a particular block name

<%= blocks.define :some_block_name do %>
  My output
<% end %>

<%= blocks.skip :some_block_name %>

<%= blocks.render :some_block_name %>
<%# will not render anything %>

Options:

name

The name of the block to skip rendering for



94
95
96
97
98
99
100
# File 'lib/blocks/base.rb', line 94

def skip(name)
  blocks[name] = nil
  skipped_blocks[name] = true
  self.define_block_container(name) do
  end
  nil
end