building-blocks 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -176,40 +176,40 @@ Then, in a specific view that is rendered using this layout, you can add stylesh
176
176
 
177
177
  == Blocks as Partials
178
178
 
179
- 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.use :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 see if a default implementation has been provided for the block and render it if one has been specified. Failing to find that, it will look for a partial by the same name in your current controller's view directory. And 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.
179
+ 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.use :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.
180
180
 
181
181
  As an example, consider the following code, running in a view for PagesController:
182
182
 
183
183
  <%= blocks.use :wizard, :step => @step %>
184
184
 
185
185
  <!-- 1) Check and see if there was a block defined called "wizard" somewhere prior to its use... No? then... -->
186
- <!-- 2) Check and see if there is a default definition provided for "wizard", i.e. specified in the "blocks.use" call... No? Then... -->
187
- <!-- 3) Check and see if there is a controller-specific partial /app/views/pages/wizard.html.erb. No? Then... -->
188
- <!-- 4) Check and see if there is a global partial /app/views/blocks/wizard.html.erb. No? Then render nothing. -->
186
+ <!-- 2) Check and see if there is a controller-specific partial /app/views/pages/wizard.html.erb. No? Then... -->
187
+ <!-- 3) Check and see if there is a global partial /app/views/blocks/wizard.html.erb. No? Then... -->
188
+ <!-- 4) Check and see if there is a default definition provided for "wizard", i.e. specified in the "blocks.use" call... No? Then render nothing -->
189
189
 
190
190
  Let's look at each example individually, written in the order that BuildingBlocks attempts to render them:
191
191
 
192
- 1. Inline definition of a block:
192
+ 1) Inline definition of a block:
193
193
  <% blocks.define :wizard do |options| %>
194
194
  Inline Block Step#<%= options[:step] %>.
195
195
  <% end %>
196
196
 
197
197
  <!-- Elsewhere, you can use the block as follows -->
198
198
  <%= blocks.use :wizard, :step => @step %>
199
- 2. Default implementation of a block:
200
- <%= blocks.use :wizard, :step => @step do |options| do %>
201
- Default Implementation Block Step#<%= options %>.
202
- <% end %>
203
- 3. Controller-specific partial:
199
+ 2) Controller-specific partial:
204
200
  <%= blocks.use :wizard, :step => @step %>
205
201
 
206
202
  <!-- In /app/views/pages/_wizard.html.erb: -->
207
203
  Controller-specific Block Step# <%= step %>.
208
- 4. Global partial:
204
+ 3) Global partial:
209
205
  <%= blocks.use :wizard, :step => @step %>
210
206
 
211
207
  <!-- In /app/views/blocks/_wizard.html.erb: -->
212
208
  Global Block Step#<%= step %>.
209
+ 4) Default implementation of a block:
210
+ <%= blocks.use :wizard, :step => @step do |options| do %>
211
+ Default Implementation Block Step#<%= options %>.
212
+ <% end %>
213
213
 
214
214
  == Overall Render Order
215
215
 
@@ -218,23 +218,24 @@ link:/hunterae/building-blocks/raw/master/blocks_render_order.png
218
218
 
219
219
  == Templating
220
220
 
221
- The most advanced feature of BuildingBlocks is the ability to utilize it for templating and writing your own DTD specifications for the components you write.
221
+ 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.
222
222
 
223
- As an example, consider {table-for}[https://github.com/hunterae/table-for], a library 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:
223
+ As an example, consider {table-for}[https://github.com/hunterae/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:
224
224
 
225
- <%= table_for @users, :row_html => {:class => lambda { cycle('even', 'odd')},
226
- :id => lambda {|user| "user-#{user.id}"}},
227
- :sortable => true do |table| %>
228
- <% table.column :edit %>
229
- <% table.column :show %>
230
- <% table.column :email, :label => "Email Address" %>
231
- <% table.column :name, :header_html => {:style => "background-color:orange"} do |user| %>
225
+ <%= table_for @users, :table_html => {:style => "border: 1px solid black"},
226
+ :sortable => true,
227
+ :row_html => {:class => lambda { cycle('even', 'odd')},
228
+ :id => lambda {|user| "user-#{user.id}"}} do |table| %>
229
+ <%= table.column :edit %>
230
+ <%= table.column :show %>
231
+ <%= table.column :email, :label => "Email Address" %>
232
+ <%= table.column :label => "Full Name", :sortable => false, :header_html => {:style => "color:orange"} do |user| %>
232
233
  <%= "#{user.first_name} #{user.last_name}" %>
233
234
  <% end %>
234
- <% table.column :delete %>
235
+ <%= table.column :delete %>
235
236
  <% end %>
236
237
 
237
- MORE EXPLANATION TO COME
238
+ VIDEO TUTORIAL TO COME
238
239
 
239
240
  == MORE COMING SOON...
240
241
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 1.0.0
@@ -1,13 +1,5 @@
1
1
  require "action_view"
2
2
 
3
- $LOAD_PATH.unshift(File.dirname(__FILE__))
4
-
5
3
  require "building_blocks/base"
6
4
  require "building_blocks/container"
7
- require "building_blocks/helper_methods"
8
-
9
- $LOAD_PATH.shift
10
-
11
- if defined?(ActionView::Base)
12
- ActionView::Base.send :include, BuildingBlocks::HelperMethods
13
- end
5
+ require "building_blocks/view_additions"
@@ -1,4 +1,6 @@
1
1
  module BuildingBlocks
2
+ BUILDING_BLOCKS_TEMPLATE_FOLDER = "blocks"
3
+
2
4
  class Base
3
5
  attr_accessor :view
4
6
 
@@ -6,7 +8,7 @@ module BuildingBlocks
6
8
 
7
9
  attr_accessor :block
8
10
 
9
- # Array of BuildingBlocks::Container objects, storing the order of blocks as they were used
11
+ # Array of BuildingBlocks::Container objects, storing the order of blocks as they were queued
10
12
  attr_accessor :queued_blocks
11
13
 
12
14
  # counter, used to give unnamed blocks a unique name
@@ -67,25 +69,16 @@ module BuildingBlocks
67
69
  end
68
70
 
69
71
  def use(*args, &block)
70
- options = args.extract_options!
71
-
72
- # If the user doesn't specify a block name, we generate an anonymous block name to assure other
73
- # anonymous blocks don't override its definition
74
- name = args.first ? args.shift : self.anonymous_block_name
75
-
76
- # self.define_block_container(name, options, &block) if block_given?
77
- self.render_block name, args, options, &block
72
+ name_or_container = args.first ? args.shift : self.anonymous_block_name
73
+ buffer = ActiveSupport::SafeBuffer.new
74
+ buffer << render_before_blocks(name_or_container, *args)
75
+ buffer << render_block(name_or_container, *args, &block)
76
+ buffer << render_after_blocks(name_or_container, *args)
77
+ buffer
78
78
  end
79
79
 
80
80
  def queue(*args, &block)
81
- options = args.extract_options!
82
-
83
- # If the user doesn't specify a block name, we generate an anonymous block name to assure other
84
- # anonymous blocks don't override its definition
85
- name = args.first ? args.shift : self.anonymous_block_name
86
-
87
- # Delays rendering this block until the partial has been rendered and all the blocks have had a chance to be defined
88
- self.queued_blocks << self.define_block_container(name, options, &block)
81
+ self.queued_blocks << self.define_block_container(*args, &block)
89
82
  nil
90
83
  end
91
84
 
@@ -100,37 +93,13 @@ module BuildingBlocks
100
93
  end
101
94
 
102
95
  def before(name, options={}, &block)
103
- name = "before_#{name.to_s}".to_sym
104
-
105
- block_container = BuildingBlocks::Container.new
106
- block_container.name = name
107
- block_container.options = options
108
- block_container.block = block
109
-
110
- if view.blocks.blocks[name].nil?
111
- blocks[name] = [block_container]
112
- else
113
- blocks[name] << block_container
114
- end
115
-
96
+ self.queue_block_container("before_#{name.to_s}", options, &block)
116
97
  nil
117
98
  end
118
99
  alias prepend before
119
100
 
120
101
  def after(name, options={}, &block)
121
- name = "after_#{name.to_s}".to_sym
122
-
123
- block_container = BuildingBlocks::Container.new
124
- block_container.name = name
125
- block_container.options = options
126
- block_container.block = block
127
-
128
- if view.blocks.blocks[name].nil?
129
- blocks[name] = [block_container]
130
- else
131
- blocks[name] << block_container
132
- end
133
-
102
+ self.queue_block_container("after_#{name.to_s}", options, &block)
134
103
  nil
135
104
  end
136
105
  alias append after
@@ -160,7 +129,7 @@ module BuildingBlocks
160
129
  protected
161
130
 
162
131
  def initialize(view, options={}, &block)
163
- options[:templates_folder] = "blocks" if options[:templates_folder].nil?
132
+ options[:templates_folder] = BuildingBlocks::BUILDING_BLOCKS_TEMPLATE_FOLDER if options[:templates_folder].nil?
164
133
 
165
134
  self.view = view
166
135
  self.global_options = options
@@ -172,11 +141,13 @@ module BuildingBlocks
172
141
  end
173
142
 
174
143
  def anonymous_block_name
175
- self.anonymous_block_number = self.anonymous_block_number + 1
144
+ self.anonymous_block_number += 1
176
145
  "block_#{anonymous_block_number}"
177
146
  end
178
147
 
179
- def render_block(name_or_container, args, runtime_options={}, &block)
148
+ def render_block(name_or_container, *args, &block)
149
+ options = args.extract_options!
150
+
180
151
  buffer = ActiveSupport::SafeBuffer.new
181
152
 
182
153
  block_options = {}
@@ -187,63 +158,28 @@ module BuildingBlocks
187
158
  name = name_or_container.to_sym
188
159
  end
189
160
 
190
- buffer << render_before_blocks(name_or_container, runtime_options)
191
-
192
161
  if blocks[name]
193
162
  block_container = blocks[name]
194
-
195
- args.push(global_options.merge(block_container.options).merge(block_options).merge(runtime_options))
196
-
197
- # If the block is taking more than one parameter, we can use *args
198
- if block_container.block.arity > 1
199
- buffer << view.capture(*args, &block_container.block)
200
-
201
- # However, if the block only takes a single parameter, we do not want ruby to try to cram the args list into that parameter
202
- # as an array
203
- else
204
- buffer << view.capture(args.first, &block_container.block)
205
- end
206
- elsif view.blocks.blocks[name]
207
- block_container = view.blocks.blocks[name]
208
-
209
- args.push(global_options.merge(block_container.options).merge(block_options).merge(runtime_options))
210
-
211
- # If the block is taking more than one parameter, we can use *args
212
- if block_container.block.arity > 1
213
- buffer << view.capture(*args, &block_container.block)
214
-
215
- # However, if the block only takes a single parameter, we do not want ruby to try to cram the args list into that parameter
216
- # as an array
217
- else
218
- buffer << view.capture(args.first, &block_container.block)
219
- end
163
+ args.push(global_options.merge(block_container.options).merge(block_options).merge(options))
164
+ buffer << view.capture(*(args[0, block_container.block.arity]), &block_container.block)
220
165
  else
221
166
  begin
222
- begin
223
- buffer << view.render("#{name.to_s}", global_options.merge(block_options).merge(runtime_options))
224
- rescue ActionView::MissingTemplate
225
- # This partial did not exist in the current controller's view directory; now checking in the default templates folder
226
- buffer << view.render("#{self.global_options[:templates_folder]}/#{name.to_s}", global_options.merge(block_options).merge(runtime_options))
167
+ begin
168
+ buffer << view.render("#{name.to_s}", global_options.merge(block_options).merge(options))
169
+ rescue ActionView::MissingTemplate
170
+ buffer << view.render("#{self.global_options[:templates_folder]}/#{name.to_s}", global_options.merge(block_options).merge(options))
227
171
  end
228
172
  rescue ActionView::MissingTemplate
229
- if block_given?
230
- args.push(global_options.merge(runtime_options))
231
- if block.arity > 1
232
- buffer << view.capture(*args, &block)
233
- else
234
- buffer << view.capture(args.first, &block)
235
- end
236
- end
173
+ args.push(global_options.merge(options))
174
+ buffer << view.capture(*(args[0, block.arity]), &block) if block_given?
237
175
  end
238
176
  end
239
177
 
240
- buffer << render_after_blocks(name_or_container, runtime_options)
241
-
242
178
  buffer
243
179
  end
244
180
 
245
- def render_before_blocks(name_or_container, runtime_options={})
246
- options = global_options
181
+ def render_before_blocks(name_or_container, *args)
182
+ options = args.extract_options!
247
183
 
248
184
  block_options = {}
249
185
  if (name_or_container.is_a?(BuildingBlocks::Container))
@@ -251,37 +187,34 @@ module BuildingBlocks
251
187
  block_options = name_or_container.options
252
188
  else
253
189
  name = name_or_container.to_sym
190
+ block_options = blocks[name].options if blocks[name]
254
191
  end
255
192
 
256
193
  before_name = "before_#{name.to_s}".to_sym
257
-
258
- if blocks[name]
259
- block_container = blocks[name]
260
- options = options.merge(block_container.options)
261
- elsif view.blocks.blocks[name]
262
- block_container = view.blocks.blocks[name]
263
- options = options.merge(block_container.options)
264
- end
265
-
266
194
  buffer = ActiveSupport::SafeBuffer.new
267
195
 
268
- unless blocks[before_name].nil?
196
+ if blocks[before_name].present?
269
197
  blocks[before_name].each do |block_container|
270
- buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
198
+ args_clone = args.clone
199
+ args_clone.push(global_options.merge(block_options).merge(block_container.options).merge(options))
200
+ buffer << view.capture(*(args_clone[0, block_container.block.arity]), &block_container.block)
271
201
  end
272
- end
273
-
274
- unless view.blocks.blocks[before_name].nil? || view.blocks.blocks == blocks
275
- view.blocks.blocks[before_name].each do |block_container|
276
- buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
202
+ else
203
+ begin
204
+ begin
205
+ buffer << view.render("before_#{name.to_s}", global_options.merge(block_options).merge(options))
206
+ rescue ActionView::MissingTemplate
207
+ buffer << view.render("#{self.global_options[:templates_folder]}/before_#{name.to_s}", global_options.merge(block_options).merge(options))
208
+ end
209
+ rescue ActionView::MissingTemplate
277
210
  end
278
211
  end
279
212
 
280
213
  buffer
281
214
  end
282
215
 
283
- def render_after_blocks(name_or_container, runtime_options={})
284
- options = global_options
216
+ def render_after_blocks(name_or_container, *args)
217
+ options = args.extract_options!
285
218
 
286
219
  block_options = {}
287
220
  if (name_or_container.is_a?(BuildingBlocks::Container))
@@ -289,43 +222,54 @@ module BuildingBlocks
289
222
  block_options = name_or_container.options
290
223
  else
291
224
  name = name_or_container.to_sym
225
+ block_options = blocks[name].options if blocks[name]
292
226
  end
293
227
 
294
228
  after_name = "after_#{name.to_s}".to_sym
295
-
296
- if blocks[name]
297
- block_container = blocks[name]
298
-
299
- options = options.merge(block_container.options)
300
- elsif view.blocks.blocks[name]
301
- block_container = view.blocks.blocks[name]
302
-
303
- options = options.merge(block_container.options)
304
- end
305
-
306
229
  buffer = ActiveSupport::SafeBuffer.new
307
230
 
308
- unless blocks[after_name].nil?
231
+ if blocks[after_name].present?
309
232
  blocks[after_name].each do |block_container|
310
- buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
233
+ args_clone = args.clone
234
+ args_clone.push(global_options.merge(block_options).merge(block_container.options).merge(options))
235
+ buffer << view.capture(*(args_clone[0, block_container.block.arity]), &block_container.block)
311
236
  end
312
- end
313
-
314
- unless view.blocks.blocks[after_name].nil? || view.blocks.blocks == blocks
315
- view.blocks.blocks[after_name].each do |block_container|
316
- buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
237
+ else
238
+ begin
239
+ begin
240
+ buffer << view.render("after_#{name.to_s}", global_options.merge(block_options).merge(options))
241
+ rescue ActionView::MissingTemplate
242
+ buffer << view.render("#{self.global_options[:templates_folder]}/after_#{name.to_s}", global_options.merge(block_options).merge(options))
243
+ end
244
+ rescue ActionView::MissingTemplate
317
245
  end
318
246
  end
319
247
 
320
248
  buffer
321
249
  end
322
250
 
323
- def define_block_container(name, options, &block)
251
+ def build_block_container(*args, &block)
252
+ options = args.extract_options!
253
+ name = args.first ? args.shift : self.anonymous_block_name
324
254
  block_container = BuildingBlocks::Container.new
325
- block_container.name = name
255
+ block_container.name = name.to_sym
326
256
  block_container.options = options
327
257
  block_container.block = block
328
- blocks[name.to_sym] = block_container if blocks[name.to_sym].nil? && block_given?
258
+ block_container
259
+ end
260
+
261
+ def queue_block_container(*args, &block)
262
+ block_container = self.build_block_container(*args, &block)
263
+ if blocks[block_container.name].nil?
264
+ blocks[block_container.name] = [block_container]
265
+ else
266
+ blocks[block_container.name] << block_container
267
+ end
268
+ end
269
+
270
+ def define_block_container(*args, &block)
271
+ block_container = self.build_block_container(*args, &block)
272
+ blocks[block_container.name] = block_container if blocks[block_container.name].nil? && block_given?
329
273
  block_container
330
274
  end
331
275
  end
@@ -0,0 +1,13 @@
1
+ module BuildingBlocks
2
+ module ViewAdditions
3
+ module ClassMethods
4
+ def blocks
5
+ @blocks ||= BuildingBlocks::Base.new(self)
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ if defined?(ActionView::Base)
12
+ ActionView::Base.send :include, BuildingBlocks::ViewAdditions::ClassMethods
13
+ end
@@ -2,7 +2,8 @@ require "spec_helper"
2
2
 
3
3
  describe BuildingBlocks::Base do
4
4
  before :each do
5
- @builder = BuildingBlocks::Base.new({})
5
+ @view = ActionView::Base.new
6
+ @builder = BuildingBlocks::Base.new(@view)
6
7
  end
7
8
 
8
9
  describe "defined? method" do
@@ -10,6 +11,21 @@ describe BuildingBlocks::Base do
10
11
  @builder.defined?(:test_block).should be_false
11
12
  @builder.define :test_block do end
12
13
  @builder.defined?(:test_block).should be_true
14
+ end
15
+
16
+ it "should not care whether the block name was defined with a string or a symbol" do
17
+ @builder.defined?(:test_block).should be_false
18
+ @builder.define "test_block" do end
19
+ @builder.defined?(:test_block).should be_true
20
+
21
+ @builder.defined?(:test_block2).should be_false
22
+ @builder.define :test_block2 do end
23
+ @builder.defined?(:test_block2).should be_true
24
+ end
25
+
26
+ it "should not care whether the defined? method is passed a string or a symbol" do
27
+ @builder.defined?("test_block").should be_false
28
+ @builder.define :test_block do end
13
29
  @builder.defined?("test_block").should be_true
14
30
  end
15
31
  end
@@ -35,12 +51,12 @@ describe BuildingBlocks::Base do
35
51
  @builder.define :test_block, :option3 => "value3", :option4 => "value4", &block2
36
52
 
37
53
  test_block = @builder.blocks[:test_block]
38
- test_block.options[:option1].should eql("value1")
39
- test_block.options[:option2].should eql("value2")
40
- test_block.options[:option3].should be_nil
41
- test_block.options[:option4].should be_nil
42
- test_block.name.should eql(:test_block)
43
- test_block.block.should eql(block1)
54
+ test_block.options[:option1].should eql("value1")
55
+ test_block.options[:option2].should eql("value2")
56
+ test_block.options[:option3].should be_nil
57
+ test_block.options[:option4].should be_nil
58
+ test_block.name.should eql(:test_block)
59
+ test_block.block.should eql(block1)
44
60
  end
45
61
  end
46
62
 
@@ -62,23 +78,406 @@ describe BuildingBlocks::Base do
62
78
  end
63
79
  end
64
80
 
81
+ describe "queue method" do
82
+ it "should store all queued blocks in the queued_blocks array" do
83
+ @builder.queued_blocks.should be_empty
84
+ @builder.queue :test_block
85
+ @builder.queued_blocks.length.should eql 1
86
+ @builder.queued_blocks.map(&:name).first.should eql(:test_block)
87
+ end
88
+
89
+ it "should convert a string block name to a symbol" do
90
+ @builder.queue "test_block"
91
+ @builder.queued_blocks.map(&:name).first.should eql(:test_block)
92
+ end
93
+
94
+ it "should queue blocks as BuildingBlocks::Container objects" do
95
+ @builder.queue :test_block, :a => 1, :b => 2, :c => 3
96
+ container = @builder.queued_blocks.first
97
+ container.should be_a(BuildingBlocks::Container)
98
+ container.name.should eql(:test_block)
99
+ container.options.should eql(:a => 1, :b => 2, :c => 3)
100
+ end
101
+
102
+ it "should not require a name for the block being queued" do
103
+ @builder.queue
104
+ @builder.queue
105
+ @builder.queued_blocks.length.should eql 2
106
+ @builder.queued_blocks.map(&:name).first.should eql(:block_1)
107
+ @builder.queued_blocks.map(&:name).second.should eql(:block_2)
108
+ end
109
+
110
+ it "should anonymously define the name of a block if not specified" do
111
+ @builder.queue
112
+ @builder.queue :my_block
113
+ @builder.queue
114
+ @builder.queued_blocks.map(&:name).first.should eql(:block_1)
115
+ @builder.queued_blocks.map(&:name).second.should eql(:my_block)
116
+ @builder.queued_blocks.map(&:name).third.should eql(:block_2)
117
+ end
118
+
119
+ it "should store queued blocks in the order in which they are queued" do
120
+ @builder.queue :block1
121
+ @builder.queue :block3
122
+ @builder.queue :block2
123
+ @builder.queued_blocks.map(&:name).first.should eql(:block1)
124
+ @builder.queued_blocks.map(&:name).second.should eql(:block3)
125
+ @builder.queued_blocks.map(&:name).third.should eql(:block2)
126
+ end
127
+
128
+ it "should allow a definition to be provided for a queued block" do
129
+ block = Proc.new do |options| end
130
+ @builder.queue :test_block, &block
131
+ container = @builder.queued_blocks.first
132
+ container.block.should eql block
133
+ end
134
+ end
135
+
136
+ describe "render method" do
137
+ it "should raise an exception if no :template parameter is specified in the options hash" do
138
+ view = mock()
139
+ builder = BuildingBlocks::Base.new(view)
140
+ lambda { builder.render }.should raise_error("Must specify :template parameter in order to render")
141
+ end
142
+
143
+ it "should attempt to render a partial specified as the :template parameter" do
144
+ view = mock()
145
+ builder = BuildingBlocks::Base.new(view, :template => "my_template")
146
+ view.expects(:render).with{ |template, options| template.should eql "my_template"}
147
+ builder.render
148
+ end
149
+
150
+ it "should set all of the global options as local variables to the partial it renders" do
151
+ view = mock()
152
+ builder = BuildingBlocks::Base.new(view, :template => "some_template")
153
+ view.expects(:render).with { |template, options| options.should eql :templates_folder => 'blocks', :template => 'some_template', :blocks => builder }
154
+ builder.render
155
+ end
156
+
157
+ it "should capture the data of a block if a block has been specified" do
158
+ block = Proc.new { |options| "my captured block" }
159
+ builder = BuildingBlocks::Base.new(@view, :template => "template", &block)
160
+ @view.expects(:render).with { |tempate, options| options[:captured_block].should eql("my captured block") }
161
+ builder.render
162
+ end
163
+
164
+ it "should by default add a variable to the partial called 'blocks' as a pointer to the BuildingBlocks::Base instance" do
165
+ view = mock()
166
+ builder = BuildingBlocks::Base.new(view, :template => "some_template")
167
+ view.expects(:render).with { |template, options| options[:blocks].should eql(builder) }
168
+ builder.render
169
+ end
170
+
171
+ it "should allow the user to override the local variable passed to the partial as a pointer to the BuildingBlocks::Base instance" do
172
+ view = mock()
173
+ builder = BuildingBlocks::Base.new(view, :variable => "my_variable", :template => "some_template")
174
+ view.expects(:render).with { |template, options| options[:blocks].should be_nil }
175
+ builder.render
176
+ end
177
+ end
178
+
179
+ describe "before method" do
180
+ it "should defined before blocks as the block name with the word 'before_' prepended to it" do
181
+ block = Proc.new { |options| }
182
+ @builder.before :some_block, &block
183
+ @builder.blocks[:before_some_block].should be_present
184
+ end
185
+
186
+ it "should store a before block in an array" do
187
+ block = Proc.new { |options| }
188
+ @builder.before :some_block, &block
189
+ before_blocks = @builder.blocks[:before_some_block]
190
+ before_blocks.should be_a(Array)
191
+ end
192
+
193
+ it "should store a before block as a BuildingBlocks::Container" do
194
+ block = Proc.new { |options| }
195
+ @builder.before :some_block, :option1 => "some option", &block
196
+ before_blocks = @builder.blocks[:before_some_block]
197
+ block_container = before_blocks.first
198
+ block_container.should be_a(BuildingBlocks::Container)
199
+ block_container.options.should eql :option1 => "some option"
200
+ block_container.block.should eql block
201
+ end
202
+
203
+ it "should queue before blocks if there are multiple defined" do
204
+ block = Proc.new { |options| }
205
+ block2 = Proc.new { |options| }
206
+ @builder.before :some_block, &block
207
+ @builder.before :some_block, &block2
208
+ before_blocks = @builder.blocks[:before_some_block]
209
+ before_blocks.length.should eql 2
210
+ end
211
+
212
+ it "should store before blocks in the order in which they are defined" do
213
+ block = Proc.new { |options| }
214
+ block2 = Proc.new { |options| }
215
+ block3 = Proc.new { |options| }
216
+ @builder.before :some_block, &block
217
+ @builder.before :some_block, &block2
218
+ @builder.before :some_block, &block3
219
+ before_blocks = @builder.blocks[:before_some_block]
220
+ before_blocks.first.block.should eql block
221
+ before_blocks.second.block.should eql block2
222
+ before_blocks.third.block.should eql block3
223
+ end
224
+ end
225
+
226
+ describe "after method" do
227
+ it "should defined after blocks as the block name with the word 'after_' prepended to it" do
228
+ block = Proc.new { |options| }
229
+ @builder.after :some_block, &block
230
+ @builder.blocks[:after_some_block].should be_present
231
+ end
232
+
233
+ it "should store a after block in an array" do
234
+ block = Proc.new { |options| }
235
+ @builder.after :some_block, &block
236
+ after_blocks = @builder.blocks[:after_some_block]
237
+ after_blocks.should be_a(Array)
238
+ end
239
+
240
+ it "should store a after block as a BuildingBlocks::Container" do
241
+ block = Proc.new { |options| }
242
+ @builder.after :some_block, :option1 => "some option", &block
243
+ after_blocks = @builder.blocks[:after_some_block]
244
+ block_container = after_blocks.first
245
+ block_container.should be_a(BuildingBlocks::Container)
246
+ block_container.options.should eql :option1 => "some option"
247
+ block_container.block.should eql block
248
+ end
249
+
250
+ it "should queue after blocks if there are multiple defined" do
251
+ block = Proc.new { |options| }
252
+ block2 = Proc.new { |options| }
253
+ @builder.after :some_block, &block
254
+ @builder.after :some_block, &block2
255
+ after_blocks = @builder.blocks[:after_some_block]
256
+ after_blocks.length.should eql 2
257
+ end
258
+
259
+ it "should store after blocks in the order in which they are defined" do
260
+ block = Proc.new { |options| }
261
+ block2 = Proc.new { |options| }
262
+ block3 = Proc.new { |options| }
263
+ @builder.after :some_block, &block
264
+ @builder.after :some_block, &block2
265
+ @builder.after :some_block, &block3
266
+ after_blocks = @builder.blocks[:after_some_block]
267
+ after_blocks.first.block.should eql block
268
+ after_blocks.second.block.should eql block2
269
+ after_blocks.third.block.should eql block3
270
+ end
271
+ end
272
+
65
273
  describe "use method" do
274
+ before :each do
275
+ @builder.expects(:render_before_blocks).at_least_once
276
+ @builder.expects(:render_after_blocks).at_least_once
277
+ end
278
+
66
279
  it "should be able to use a defined block by its name" do
67
- @builder.expects(:render_block).with(:test_block, [], {})
280
+ block = Proc.new {"output"}
281
+ @builder.define :some_block, &block
282
+ @builder.use(:some_block).should eql "output"
283
+ end
68
284
 
69
- block = Proc.new do |options| end
70
- @builder.define :test_block, :option1 => "value1", :option2 => "value2", &block
285
+ it "should automatically pass in an options hash to a defined block that takes one paramter when that block is used" do
286
+ block = Proc.new {|options| "Templates folder is #{options[:templates_folder]}"}
287
+ @builder.define :some_block, &block
288
+ @builder.use(:some_block).should eql "Templates folder is blocks"
289
+ end
71
290
 
72
- @builder.use :test_block
291
+ it "should be able to use a defined block by its name and pass in runtime arguments as a hash" do
292
+ block = Proc.new do |options|
293
+ print_hash(options)
294
+ end
295
+ @builder.define :some_block, &block
296
+ @builder.use(:some_block, :param1 => 1, :param2 => "value2").should eql print_hash(:templates_folder => 'blocks', :param1 => 1, :param2 => "value2")
73
297
  end
74
298
 
75
- it "should be able to use a defined block by its name and pass in runtime arguments" do
76
- @builder.expects(:render_block).with(:test_block, ["value5"], {:option3 => "value3", :option4 => "value4"})
299
+ it "should be able to use a defined block by its name and pass in runtime arguments one by one" do
300
+ block = Proc.new do |first_param, second_param, options|
301
+ "first_param: #{first_param}, second_param: #{second_param}, #{print_hash options}"
302
+ end
303
+ @builder.define :some_block, &block
304
+ @builder.use(:some_block, 3, 4, :param1 => 1, :param2 => "value2").should eql("first_param: 3, second_param: 4, #{print_hash(:templates_folder => 'blocks', :param1 => 1, :param2 => "value2")}")
305
+ end
77
306
 
78
- block = Proc.new do |options| end
79
- @builder.define :test_block, :option1 => "value1", :option2 => "value2", &block
307
+ it "should match up the number of arguments to a defined block with the parameters passed when a block is used" do
308
+ block = Proc.new {|first_param| "first_param = #{first_param}"}
309
+ @builder.define :some_block, &block
310
+ @builder.use(:some_block, 3, 4, :param1 => 1, :param2 => "value2").should eql "first_param = 3"
311
+
312
+ block = Proc.new {|first_param, second_param| "first_param = #{first_param}, second_param = #{second_param}"}
313
+ @builder.replace :some_block, &block
314
+ @builder.use(:some_block, 3, 4, :param1 => 1, :param2 => "value2").should eql "first_param = 3, second_param = 4"
315
+
316
+ block = Proc.new do |first_param, second_param, options|
317
+ "first_param: #{first_param}, second_param: #{second_param}, #{print_hash options}"
318
+ end
319
+ @builder.replace :some_block, &block
320
+ @builder.use(:some_block, 3, 4, :param1 => 1, :param2 => "value2").should eql("first_param: 3, second_param: 4, #{print_hash(:templates_folder => 'blocks', :param1 => 1, :param2 => "value2")}")
321
+ end
322
+
323
+ it "should not render anything if using a block that has been defined" do
324
+ @view.expects(:capture).never
325
+ @view.expects(:render).with("some_block", :templates_folder => 'blocks').raises(ActionView::MissingTemplate.new([],[],[],[],[]))
326
+ @view.expects(:render).with("blocks/some_block", :templates_folder => 'blocks').raises(ActionView::MissingTemplate.new([],[],[],[],[]))
327
+ @builder.use :some_block
328
+ end
329
+
330
+ it "should first attempt to capture a block's contents when blocks.use is called" do
331
+ block = Proc.new {|options|}
332
+ @view.expects(:capture).with(:templates_folder => 'blocks', :value1 => 1, :value2 => 2)
333
+ @view.expects(:render).with("some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
334
+ @view.expects(:render).with("blocks/some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
335
+ @builder.define :some_block, &block
336
+ @builder.use :some_block, :value1 => 1, :value2 => 2
337
+ end
338
+
339
+ it "should second attempt to render a local partial by the block's name when blocks.use is called" do
340
+ @view.expects(:capture).with(:templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
341
+ @view.expects(:render).with("some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
342
+ @view.expects(:render).with("blocks/some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
343
+ @builder.use :some_block, :value1 => 1, :value2 => 2
344
+ end
345
+
346
+ it "should third attempt to render a global partial by the block's name when blocks.use is called" do
347
+ @view.expects(:capture).with(:templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
348
+ @view.expects(:render).with("some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).raises(ActionView::MissingTemplate.new([],[],[],[],[]))
349
+ @view.expects(:render).with("blocks/some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
350
+ @builder.use :some_block, :value1 => 1, :value2 => 2
351
+ end
352
+
353
+ it "should fourth attempt to render a default block when blocks.use is called" do
354
+ block = Proc.new {|options|}
355
+ @view.expects(:render).with("some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).raises(ActionView::MissingTemplate.new([],[],[],[],[]))
356
+ @view.expects(:render).with("blocks/some_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).raises(ActionView::MissingTemplate.new([],[],[],[],[]))
357
+ @view.expects(:capture).with(:templates_folder => 'blocks', :value1 => 1, :value2 => 2)
358
+ @builder.use :some_block, :value1 => 1, :value2 => 2, &block
359
+ end
360
+
361
+ it "should override hash options for a block by merging the runtime options the define default options into the queue level options into the global options" do
362
+ block = Proc.new {|options|}
363
+ @builder.global_options.merge!(:param1 => "global level", :param2 => "global level", :param3 => "global level", :param4 => "global level")
364
+ @builder.queue(:my_before_block, :param1 => "queue level", :param2 => "queue level")
365
+ @builder.define(:my_before_block, :param1 => "define level", :param2 => "define level", :param3 => "define level", &block)
366
+ block_container = @builder.queued_blocks.first
367
+ @view.expects(:capture).with(:param4 => 'global level', :param1 => 'use level', :templates_folder => 'blocks', :param2 => 'queue level', :param3 => 'define level')
368
+ @builder.use block_container, :param1 => "use level"
369
+ end
370
+
371
+ it "should render the contents of a defined block when that block is used" do
372
+ block = Proc.new {}
373
+ @view.expects(:capture).with(nil).returns("rendered content")
374
+ @builder.define :some_block, &block
375
+ buffer = @builder.use :some_block
376
+ buffer.should eql "rendered content"
377
+ end
378
+ end
379
+
380
+ describe "use method - before blocks" do
381
+ before :each do
382
+ @builder.expects(:render_block).at_least_once
383
+ @builder.expects(:render_after_blocks).at_least_once
384
+ end
385
+
386
+ it "should render before blocks when using a block" do
387
+ block = Proc.new {|value1, value2, options|}
388
+ @builder.before("my_before_block", &block)
389
+ @view.expects(:capture).with(1, 2, :templates_folder => 'blocks', :value3 => 3, :value4 => 4)
390
+ @builder.use :my_before_block, 1, 2, :value3 => 3, :value4 => 4
391
+ end
392
+
393
+ it "should try and render a before block as a local partial if no before blocks are specified" do
394
+ block = Proc.new {}
395
+ @view.expects(:capture).never
396
+ @view.expects(:render).with("before_my_before_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
397
+ @view.expects(:render).with("blocks/before_my_before_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
398
+ @builder.use :my_before_block, :value1 => 1, :value2 => 2
399
+ end
400
+
401
+ it "should try and render a before block as a global partial if no after blocks are specified and the local partial does not exist" do
402
+ block = Proc.new {}
403
+ @view.expects(:capture).never
404
+ @view.expects(:render).with("before_my_before_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).raises(ActionView::MissingTemplate.new([],[],[],[],[]))
405
+ @view.expects(:render).with("blocks/before_my_before_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
406
+ @builder.use :my_before_block, :value1 => 1, :value2 => 2
407
+ end
408
+
409
+ it "should override hash options for before blocks by merging the runtime options into the before block options into the block options into the global options" do
410
+ block = Proc.new {|options|}
411
+ @builder.global_options.merge!(:param1 => "global level", :param2 => "global level", :param3 => "global level", :param4 => "global level")
412
+ @builder.define(:my_before_block, :param1 => "block level", :param2 => "block level", :param3 => "block level", &block)
413
+ @builder.before(:my_before_block, :param1 => "before block level", :param2 => "before block level", &block)
414
+ @view.expects(:capture).with(:param4 => 'global level', :param1 => 'top level', :templates_folder => 'blocks', :param2 => 'before block level', :param3 => 'block level')
415
+ @builder.use :my_before_block, :param1 => "top level"
416
+ end
417
+ end
418
+
419
+ describe "use method - after blocks" do
420
+ before :each do
421
+ @builder.expects(:render_block).at_least_once
422
+ @builder.expects(:render_before_blocks).at_least_once
423
+ end
424
+
425
+ it "should render after blocks when using a block" do
426
+ block = Proc.new {|value1, value2, options|}
427
+ @builder.after("my_after_block", &block)
428
+ @view.expects(:capture).with(1, 2, :templates_folder => 'blocks', :value3 => 3, :value4 => 4)
429
+ @builder.use :my_after_block, 1, 2, :value3 => 3, :value4 => 4
430
+ end
431
+
432
+ it "should try and render a after block as a local partial if no after blocks are specified" do
433
+ block = Proc.new {}
434
+ @view.expects(:capture).never
435
+ @view.expects(:render).with("after_my_after_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
436
+ @view.expects(:render).with("blocks/after_my_after_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).never
437
+ @builder.use :my_after_block, :value1 => 1, :value2 => 2
438
+ end
439
+
440
+ it "should try and render a after block as a global partial if no after blocks are specified and the local partial does not exist" do
441
+ block = Proc.new {}
442
+ @view.expects(:capture).never
443
+ @view.expects(:render).with("after_my_after_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).raises(ActionView::MissingTemplate.new([],[],[],[],[]))
444
+ @view.expects(:render).with("blocks/after_my_after_block", :templates_folder => 'blocks', :value1 => 1, :value2 => 2).once
445
+ @builder.use :my_after_block, :value1 => 1, :value2 => 2
446
+ end
447
+
448
+ it "should override hash options for after blocks by merging the runtime options into the after block options into the block options into the global options" do
449
+ block = Proc.new {|options|}
450
+ @builder.global_options.merge!(:param1 => "global level", :param2 => "global level", :param3 => "global level", :param4 => "global level")
451
+ @builder.define(:my_after_block, :param1 => "block level", :param2 => "block level", :param3 => "block level", &block)
452
+ @builder.after(:my_after_block, :param1 => "after block level", :param2 => "after block level", &block)
453
+ @view.expects(:capture).with(:param4 => 'global level', :param1 => 'top level', :templates_folder => 'blocks', :param2 => 'after block level', :param3 => 'block level')
454
+ @builder.use :my_after_block, :param1 => "top level"
455
+ end
456
+ end
457
+
458
+ describe "method_missing method" do
459
+ it "should start a new block group if a method is missing" do
460
+ @builder.some_method
461
+ queued_blocks = @builder.block_groups[:some_method]
462
+ queued_blocks.should eql []
463
+ end
80
464
 
81
- @builder.use :test_block, "value5", :option3 => "value3", :option4 => "value4"
465
+ it "should add items to a queue when a new block group is started" do
466
+ @builder.some_method do
467
+ @builder.queue :myblock1
468
+ @builder.queue :myblock2
469
+ end
470
+ @builder.some_method2 do
471
+ @builder.queue :myblock3
472
+ end
473
+ queued_blocks = @builder.block_groups[:some_method]
474
+ queued_blocks.length.should eql 2
475
+ queued_blocks.first.name.should eql :myblock1
476
+ queued_blocks.second.name.should eql :myblock2
477
+ queued_blocks = @builder.block_groups[:some_method2]
478
+ queued_blocks.length.should eql 1
479
+ queued_blocks.first.name.should eql :myblock3
480
+ @builder.queued_blocks.should eql []
82
481
  end
83
482
  end
84
483
  end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,10 @@ require 'bundler/setup'
3
3
 
4
4
  require 'building-blocks' # and any other gems you need
5
5
 
6
+ def print_hash(hash)
7
+ hash.inject("") { |s, (k, v)| "#{s} #{k}: #{v}." }
8
+ end
9
+
6
10
  RSpec.configure do |config|
7
11
  config.mock_with :mocha
8
12
  # config.mock_with :flexmock
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: building-blocks
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
- - 0
8
- - 1
9
7
  - 1
10
- version: 0.1.1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andrew Hunter
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-27 00:00:00 -05:00
18
+ date: 2012-02-04 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -104,6 +104,34 @@ dependencies:
104
104
  prerelease: false
105
105
  type: :development
106
106
  requirement: *id006
107
+ - !ruby/object:Gem::Dependency
108
+ name: jeweler
109
+ version_requirements: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ hash: 3
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ prerelease: false
119
+ type: :development
120
+ requirement: *id007
121
+ - !ruby/object:Gem::Dependency
122
+ name: jeweler
123
+ version_requirements: &id008 !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ prerelease: false
133
+ type: :development
134
+ requirement: *id008
107
135
  description: ""
108
136
  email: hunterae@gmail.com
109
137
  executables: []
@@ -119,7 +147,7 @@ files:
119
147
  - lib/building-blocks.rb
120
148
  - lib/building_blocks/base.rb
121
149
  - lib/building_blocks/container.rb
122
- - lib/building_blocks/helper_methods.rb
150
+ - lib/building_blocks/view_additions.rb
123
151
  - rails/init.rb
124
152
  - spec/building-blocks/base_spec.rb
125
153
  - spec/spec_helper.rb
@@ -1,7 +0,0 @@
1
- module BuildingBlocks
2
- module HelperMethods
3
- def blocks
4
- @blocks ||= BuildingBlocks::Base.new(self)
5
- end
6
- end
7
- end