building-blocks 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +241 -0
- data/VERSION +1 -1
- data/lib/building-blocks.rb +1 -1
- data/lib/building_blocks/{building_blocks.rb → base.rb} +105 -114
- data/spec/building-blocks/base_spec.rb +84 -0
- data/spec/spec_helper.rb +11 -0
- metadata +27 -11
data/README.rdoc
CHANGED
@@ -0,0 +1,241 @@
|
|
1
|
+
= BuildingBlocks
|
2
|
+
|
3
|
+
BuildingBlocks is an intricate way of rendering blocks of code, while adding several features that go above and beyond what a simple content_for with yield is capable of doing.
|
4
|
+
|
5
|
+
1. It provides the ability to pass parameters to a defined block of code (something content_for with yield is incapable of doing).
|
6
|
+
2. It provides "before" and "after" hooks that can render code before and after a specified block of code is rendered (this can be particularly useful when you want to specify a dynamic list of javascript or stylesheet includes after your standard includes).
|
7
|
+
3. It allows the developer to define a block of code as a global partial, a controller-specific partial, or an inline block of code, all with the same syntax usage.
|
8
|
+
4. (Probably the most powerful aspect) It allows the developer to build complex reusable UI components by essentially providing their own DTD (see the separate project {table-for}[https://github.com/hunterae/table-for] for an example of a nice table builder that was created using minimal code by apply BuildingBlocks).
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
In <b>Rails 3</b>, add this to your Gemfile.
|
13
|
+
|
14
|
+
gem "building-blocks"
|
15
|
+
|
16
|
+
== Defining and using blocks
|
17
|
+
|
18
|
+
The syntax for defining and using blocks is similar to how content_for and yield are used. At its simplest form:
|
19
|
+
|
20
|
+
<% blocks.define :my_block do %>
|
21
|
+
My code to render
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<!-- Elsewhere, you can use the block as follows -->
|
25
|
+
<%= blocks.use :my_block %>
|
26
|
+
|
27
|
+
<!-- Will render: My code to render -->
|
28
|
+
|
29
|
+
== Passing parameters to blocks
|
30
|
+
|
31
|
+
Parameters may also be passed into defined blocks:
|
32
|
+
|
33
|
+
<% blocks.define :my_block do |options| %>
|
34
|
+
The user of this block passed in "<%= options[:my_parameter] %>" as :my_parameter.
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<!-- Elsewhere, you can use the block as follows -->
|
38
|
+
<%= blocks.use :my_block, :my_parameter => "The value I'm passing in" %>
|
39
|
+
|
40
|
+
<!-- Will render: The user of this block passed in "The value I'm passing in" as :my_parameter. -->
|
41
|
+
|
42
|
+
<!-- If the anticipated parameters are not passed: -->
|
43
|
+
<%= blocks.use :my_block %>
|
44
|
+
|
45
|
+
<!-- Will render: The user of this block passed in "" as :my_parameter. -->
|
46
|
+
|
47
|
+
The parameters are not required, but unexpected results might occur if the "necessary" parameters are not passed in.
|
48
|
+
|
49
|
+
== Passing non-hash parameters into defined blocks:
|
50
|
+
|
51
|
+
<% blocks.define :my_block do |first_parameter, second_parameter, options| %>
|
52
|
+
First parameter: <%= first_parameter %>, Second parameter: <%= second_parameter %>, Third parameter: <%= options[:third_parameter] %>
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
<!-- Elsewhere, you can use the block as follows -->
|
56
|
+
<%= blocks.use :my_block, "Value 1", 2, :third_parameter => "Value 3" %>
|
57
|
+
|
58
|
+
<!-- Will render: First parameter: Value 1, Second parameter: 2, Third parameter: Value 3 -->
|
59
|
+
|
60
|
+
== Providing default values for parameters to blocks
|
61
|
+
|
62
|
+
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 used, the values passed in override the default values.
|
63
|
+
|
64
|
+
<% blocks.define :my_block, :parameter1 => "Parameter 1", :parameter2 => "Parameter 2" do |options| %>
|
65
|
+
The values specified are :parameter1 = "<%= options[:parameter1] %>", :parameter2 = "<%= options[:parameter2] %>"
|
66
|
+
<% end %>
|
67
|
+
|
68
|
+
<!-- Elsewhere, you can use the block as follows (specifying zero, one, or both of the parameters the block uses) -->
|
69
|
+
<%= blocks.use :my_block %><br />
|
70
|
+
<%= blocks.use :my_block, :parameter1 => "New Parameter 1" %><br />
|
71
|
+
<%= blocks.use :my_block, :parameter2 => "New Parameter 2" %><br />
|
72
|
+
<%= blocks.use :my_block, :parameter1 => "New Parameter 1", :parameter2 => "New Parameter 2" %>
|
73
|
+
|
74
|
+
<!--
|
75
|
+
Will render:
|
76
|
+
The values specified are :parameter1 = "Parameter 1", :parameter2 = "Parameter 2"
|
77
|
+
The values specified are :parameter1 = "New Parameter 1", :parameter2 = "Parameter 2"
|
78
|
+
The values specified are :parameter1 = "Parameter 1", :parameter2 = "New Parameter 2"
|
79
|
+
The values specified are :parameter1 = "New Parameter 1", :parameter2 = "New Parameter 2"
|
80
|
+
-->
|
81
|
+
|
82
|
+
== Providing a default definition for a block
|
83
|
+
|
84
|
+
What happens if you attempt to "use" a block that hasn't been "define"d? Nothing will get rendered.
|
85
|
+
|
86
|
+
However, you may want to provide a default definition for a block to "use" if such a block was never "define"d. You can do this as follows:
|
87
|
+
|
88
|
+
<%= blocks.use :my_block, :my_parameter_1 => "Parameter 1" do %>
|
89
|
+
This is my default definition of :my_block.
|
90
|
+
<% end %>
|
91
|
+
|
92
|
+
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:
|
93
|
+
This is my default definition of :my_block.
|
94
|
+
|
95
|
+
If however, you have defined :my_block elsewhere, it would have used that definition:
|
96
|
+
|
97
|
+
<% blocks.define :my_block do |options| %>
|
98
|
+
Some other definition of :my_block with :my_parameter_1 set to "<%= options[:my_parameter_1] %>"
|
99
|
+
<% end %>
|
100
|
+
|
101
|
+
<!-- Elsewhere, you can use the block as follows -->
|
102
|
+
<%= blocks.use :my_block, :my_parameter_1 => "Parameter 1" do %>
|
103
|
+
This is my default definition of :my_block.
|
104
|
+
<% end %>
|
105
|
+
|
106
|
+
<!-- Will render: Some other definition of :my_block with :my_parameter_1 set to "Parameter 1" -->
|
107
|
+
<!-- (since the block was defined, i.e. the default definition is not needed) -->
|
108
|
+
|
109
|
+
== Using "before" and "after filters"
|
110
|
+
|
111
|
+
"Before" and "After" hooks render code before and after the code produced by a "blocks.use" call. A practical example of this would be adding view-specific javascript and stylesheet includes to a global layout file.
|
112
|
+
|
113
|
+
In your application.html layout file, you might use this as follows:
|
114
|
+
|
115
|
+
<html>
|
116
|
+
<head>
|
117
|
+
<%= blocks.use :includes do %>
|
118
|
+
<%= blocks.use :stylesheets do %>
|
119
|
+
<%= stylesheet_link_tag "jquery" %>
|
120
|
+
<% end %>
|
121
|
+
<%= blocks.use :javscripts do %>
|
122
|
+
<%= javascript_include_tag "jquery" %>
|
123
|
+
<% end %>
|
124
|
+
<% end %>
|
125
|
+
</head>
|
126
|
+
<body>
|
127
|
+
<%= yield %>
|
128
|
+
</body>
|
129
|
+
</html>
|
130
|
+
|
131
|
+
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:
|
132
|
+
|
133
|
+
<% blocks.before :includes do %>
|
134
|
+
<%= stylesheet_link_tag "first_overall_stylesheet" %>
|
135
|
+
<% end %>
|
136
|
+
|
137
|
+
<% blocks.after :includes do %>
|
138
|
+
<%= stylesheet_link_tag "last_overall_stylesheet" %>
|
139
|
+
<% end %>
|
140
|
+
|
141
|
+
<% blocks.before :stylesheets do %>
|
142
|
+
<%= stylesheet_link_tag "stylesheet_before_jquery" %>
|
143
|
+
<% end %>
|
144
|
+
|
145
|
+
<% blocks.after :stylesheets do %>
|
146
|
+
<%= stylesheet_link_tag "stylesheet_after_jquery" %>
|
147
|
+
<% end %>
|
148
|
+
|
149
|
+
<% blocks.before :javascripts do %>
|
150
|
+
<%= javascript_include_tag "javascript_before_jquery" %>
|
151
|
+
<% end %>
|
152
|
+
|
153
|
+
<% blocks.after :javascripts do %>
|
154
|
+
<%= javascript_include_tag "stylesheet_before_jquery" %>
|
155
|
+
<% end %>
|
156
|
+
|
157
|
+
<!--
|
158
|
+
When index.html.erb is rendered, it will output:
|
159
|
+
<html>
|
160
|
+
<head>
|
161
|
+
<link href="/stylesheets/first_overall_stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
|
162
|
+
<link href="/stylesheets/stylesheet_before_jquery.css" media="screen" rel="stylesheet" type="text/css" />
|
163
|
+
<link href="/stylesheets/jquery.css" media="screen" rel="stylesheet" type="text/css" />
|
164
|
+
<link href="/stylesheets/stylesheet_after_jquery.css" media="screen" rel="stylesheet" type="text/css" />
|
165
|
+
<script src="/javascripts/javascript_before_jquery.js" type="text/javascript"></script>
|
166
|
+
<script src="/javascripts/jquery.js" type="text/javascript"></script>
|
167
|
+
<script src="/javascripts/javascript_after_jquery.js" type="text/javascript"></script>
|
168
|
+
<link href="/stylesheets/last_overall_stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
|
169
|
+
</head>
|
170
|
+
<body>
|
171
|
+
</body>
|
172
|
+
</html>
|
173
|
+
-->
|
174
|
+
(An alternative syntax to "blocks.before" and "blocks.after" would be, respectively, "blocks.prepend" and "blocks.append")
|
175
|
+
|
176
|
+
== Blocks as Partials
|
177
|
+
|
178
|
+
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
|
+
|
180
|
+
As an example, consider the following code, running in a view for PagesController:
|
181
|
+
|
182
|
+
<%= blocks.use :wizard, :step => @step %>
|
183
|
+
|
184
|
+
<!-- 1) Check and see if there was a block defined called "wizard" somewhere prior to its use... No? then... -->
|
185
|
+
<!-- 2) Check and see if there is a default definition provided for "wizard", i.e. specified in the "blocks.use" call... No? Then... -->
|
186
|
+
<!-- 3) Check and see if there is a controller-specific partial /app/views/pages/wizard.html.erb. No? Then... -->
|
187
|
+
<!-- 4) Check and see if there is a global partial /app/views/blocks/wizard.html.erb. No? Then render nothing. -->
|
188
|
+
|
189
|
+
Let's look at each example individually, written in the order that BuildingBlocks attempts to render them:
|
190
|
+
|
191
|
+
1. Inline definition of a block:
|
192
|
+
<% blocks.define :wizard do |options| %>
|
193
|
+
Inline Block Step#<%= options[:step] %>.
|
194
|
+
<% end %>
|
195
|
+
|
196
|
+
<!-- Elsewhere, you can use the block as follows -->
|
197
|
+
<%= blocks.use :wizard, :step => @step %>
|
198
|
+
2. Default implementation of a block:
|
199
|
+
<%= blocks.use :wizard, :step => @step do |options| do %>
|
200
|
+
Default Implementation Block Step#<%= options %>.
|
201
|
+
<% end %>
|
202
|
+
3. Controller-specific partial:
|
203
|
+
<%= blocks.use :wizard, :step => @step %>
|
204
|
+
|
205
|
+
<!-- In /app/views/pages/_wizard.html.erb: -->
|
206
|
+
Controller-specific Block Step# <%= step %>.
|
207
|
+
4. Global partial:
|
208
|
+
<%= blocks.use :wizard, :step => @step %>
|
209
|
+
|
210
|
+
<!-- In /app/views/blocks/_wizard.html.erb: -->
|
211
|
+
Global Block Step#<%= step %>.
|
212
|
+
|
213
|
+
== Overall Render Order
|
214
|
+
|
215
|
+
Putting all the pieces together from the previous examples, here is what BuildingBlock is doing when a block is rendered:
|
216
|
+
link:/hunterae/building-blocks/raw/master/blocks_render_order.png
|
217
|
+
|
218
|
+
== Templating
|
219
|
+
|
220
|
+
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
|
+
|
222
|
+
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
|
+
|
224
|
+
<%= table_for @users do |table| %>
|
225
|
+
<% table.column :name do |user| %>
|
226
|
+
<%= "#{user.first_name} #{user.last_name}" %>
|
227
|
+
<% end %>
|
228
|
+
<% table.column :email %>
|
229
|
+
<% end %>
|
230
|
+
|
231
|
+
MORE EXPLANATION TO COME
|
232
|
+
|
233
|
+
== MORE COMING SOON...
|
234
|
+
|
235
|
+
== Questions or Problems?
|
236
|
+
|
237
|
+
If you have any issues with BuildingBlocks which you cannot find the solution to in the documentation, please add an {issue on GitHub}[https://github.com/hunterae/building-blocks/issues] or fork the project and send a pull request.
|
238
|
+
|
239
|
+
== Special Thanks
|
240
|
+
|
241
|
+
Thanks to {Todd Fisher}[https://github.com/taf2] of LivingSocial for implementation help and setup of gem and {Jon Phillips}[https://github.com/elguapo1611] of LivingSocial for suggestions and use case help.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.8
|
data/lib/building-blocks.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
module BuildingBlocks
|
2
2
|
class Base
|
3
3
|
attr_accessor :view
|
4
|
-
|
5
|
-
attr_accessor :blocks
|
6
|
-
|
4
|
+
|
5
|
+
attr_accessor :blocks
|
6
|
+
|
7
7
|
attr_accessor :block
|
8
|
-
|
8
|
+
|
9
9
|
# Array of BuildingBlocks::Container objects, storing the order of blocks as they were used
|
10
|
-
attr_accessor :
|
11
|
-
|
10
|
+
attr_accessor :queued_blocks
|
11
|
+
|
12
12
|
# counter, used to give unnamed blocks a unique name
|
13
13
|
attr_accessor :anonymous_block_number
|
14
|
-
|
15
|
-
attr_accessor :start_rendering_blocks
|
16
|
-
|
14
|
+
|
17
15
|
attr_accessor :block_groups
|
18
16
|
|
19
17
|
# These are the options that are passed into the initalize method
|
20
18
|
attr_accessor :global_options
|
21
|
-
|
19
|
+
|
22
20
|
# Checks if a particular block has been defined within the current block scope.
|
23
21
|
# <%= blocks.defined? :some_block_name %>
|
24
22
|
# Options:
|
@@ -27,40 +25,30 @@ module BuildingBlocks
|
|
27
25
|
def defined?(name)
|
28
26
|
!blocks[name.to_sym].nil?
|
29
27
|
end
|
30
|
-
|
28
|
+
|
31
29
|
# Define a block, unless a block by the same name is already defined.
|
32
30
|
# <%= blocks.define :some_block_name, :parameter1 => "1", :parameter2 => "2" do |options| %>
|
33
31
|
# <%= options[:parameter1] %> and <%= options[:parameter2] %>
|
34
32
|
# <% end %>
|
35
|
-
#
|
33
|
+
#
|
36
34
|
# Options:
|
37
35
|
# [+name+]
|
38
36
|
# The name of the block being defined (either a string or a symbol)
|
39
37
|
# [+options+]
|
40
|
-
# The default options for the block definition. Any or all of these options may be overrideen by
|
38
|
+
# The default options for the block definition. Any or all of these options may be overrideen by
|
41
39
|
# whomever calls "blocks.use" on this block.
|
42
40
|
# [+block+]
|
43
41
|
# The block that is to be rendered when "blocks.use" is called for this block.
|
44
42
|
def define(name, options={}, &block)
|
45
|
-
|
46
|
-
if blocks[name.to_sym].nil?
|
47
|
-
# Store the attributes of the defined block in a container for later use
|
48
|
-
block_container = BuildingBlocks::Container.new
|
49
|
-
block_container.name = name
|
50
|
-
block_container.options = options
|
51
|
-
block_container.block = block
|
52
|
-
|
53
|
-
blocks[name.to_sym] = block_container
|
54
|
-
end
|
55
|
-
|
43
|
+
self.define_block_container(name, options, &block)
|
56
44
|
nil
|
57
45
|
end
|
58
|
-
|
46
|
+
|
59
47
|
# Define a block, replacing an existing block by the same name if it is already defined.
|
60
48
|
# <%= blocks.define :some_block_name, :parameter1 => "1", :parameter2 => "2" do |options| %>
|
61
49
|
# <%= options[:parameter1] %> and <%= options[:parameter2] %>
|
62
50
|
# <% end %>
|
63
|
-
#
|
51
|
+
#
|
64
52
|
# <%= blocks.replace :some_block_name, :parameter3 => "3", :parameter4 => "4" do |options| %>
|
65
53
|
# <%= options[:parameter3] %> and <%= options[:parameter4] %>
|
66
54
|
# <% end %>
|
@@ -68,58 +56,53 @@ module BuildingBlocks
|
|
68
56
|
# [+name+]
|
69
57
|
# The name of the block being defined (either a string or a symbol)
|
70
58
|
# [+options+]
|
71
|
-
# The default options for the block definition. Any or all of these options may be overrideen by
|
59
|
+
# The default options for the block definition. Any or all of these options may be overrideen by
|
72
60
|
# whomever calls "blocks.use" on this block.
|
73
61
|
# [+block+]
|
74
62
|
# The block that is to be rendered when "blocks.use" is called for this block.
|
75
63
|
def replace(name, options={}, &block)
|
76
64
|
blocks[name.to_sym] = nil
|
77
|
-
|
65
|
+
self.define_block_container(name, options, &block)
|
66
|
+
nil
|
78
67
|
end
|
79
|
-
|
68
|
+
|
80
69
|
def use(*args, &block)
|
81
|
-
options = args.extract_options!
|
82
|
-
|
70
|
+
options = args.extract_options!
|
71
|
+
|
83
72
|
# If the user doesn't specify a block name, we generate an anonymous block name to assure other
|
84
73
|
# anonymous blocks don't override its definition
|
85
74
|
name = args.first ? args.shift : self.anonymous_block_name
|
86
|
-
|
87
|
-
block_container = BuildingBlocks::Container.new
|
88
|
-
block_container.name = name
|
89
|
-
block_container.options = options
|
90
|
-
block_container.block = block
|
91
75
|
|
92
|
-
|
93
|
-
|
94
|
-
if start_rendering_blocks
|
95
|
-
render_block name, args, options
|
96
|
-
else
|
97
|
-
# Delays rendering this block until the partial has been rendered and all the blocks have had a chance to be defined
|
98
|
-
self.block_positions << block_container
|
99
|
-
nil
|
100
|
-
end
|
101
|
-
|
102
|
-
# nil
|
76
|
+
self.render_block name, args, options
|
103
77
|
end
|
104
|
-
|
78
|
+
|
79
|
+
def queue(*args, &block)
|
80
|
+
options = args.extract_options!
|
81
|
+
|
82
|
+
# If the user doesn't specify a block name, we generate an anonymous block name to assure other
|
83
|
+
# anonymous blocks don't override its definition
|
84
|
+
name = args.first ? args.shift : self.anonymous_block_name
|
85
|
+
|
86
|
+
# Delays rendering this block until the partial has been rendered and all the blocks have had a chance to be defined
|
87
|
+
self.queued_blocks << self.define_block_container(name, options, &block)
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
105
91
|
def render
|
106
|
-
|
107
|
-
|
108
|
-
global_options[:captured_block] = view.capture(self, &self.block) if self.block
|
109
|
-
|
110
|
-
self.start_rendering_blocks = true
|
92
|
+
raise "Must specify :template parameter in order to render" unless global_options[:template]
|
111
93
|
|
94
|
+
global_options[:captured_block] = view.capture(self, &self.block) if self.block
|
112
95
|
view.render global_options[:template], global_options
|
113
96
|
end
|
114
|
-
|
97
|
+
|
115
98
|
def before(name, options={}, &block)
|
116
99
|
name = "before_#{name.to_s}".to_sym
|
117
|
-
|
100
|
+
|
118
101
|
block_container = BuildingBlocks::Container.new
|
119
102
|
block_container.name = name
|
120
103
|
block_container.options = options
|
121
104
|
block_container.block = block
|
122
|
-
|
105
|
+
|
123
106
|
if view.blocks.blocks[name].nil?
|
124
107
|
blocks[name] = [block_container]
|
125
108
|
else
|
@@ -129,15 +112,15 @@ module BuildingBlocks
|
|
129
112
|
nil
|
130
113
|
end
|
131
114
|
alias prepend before
|
132
|
-
|
115
|
+
|
133
116
|
def after(name, options={}, &block)
|
134
117
|
name = "after_#{name.to_s}".to_sym
|
135
|
-
|
118
|
+
|
136
119
|
block_container = BuildingBlocks::Container.new
|
137
120
|
block_container.name = name
|
138
121
|
block_container.options = options
|
139
122
|
block_container.block = block
|
140
|
-
|
123
|
+
|
141
124
|
if view.blocks.blocks[name].nil?
|
142
125
|
blocks[name] = [block_container]
|
143
126
|
else
|
@@ -147,51 +130,52 @@ module BuildingBlocks
|
|
147
130
|
nil
|
148
131
|
end
|
149
132
|
alias append after
|
150
|
-
|
133
|
+
|
151
134
|
# If a method is missing, we'll assume the user is starting a new block group by that missing method name
|
152
|
-
def method_missing(m,
|
135
|
+
def method_missing(m, *args, &block)
|
136
|
+
options = args.extract_options!
|
137
|
+
|
153
138
|
# If the specified block group has already been defined, it is simply returned here for iteration.
|
154
139
|
# It will consist of all the blocks used in this block group that have yet to be rendered,
|
155
140
|
# as the call for their use occurred before the template was rendered (where their definitions likely occurred)
|
156
141
|
return self.block_groups[m] unless self.block_groups[m].nil?
|
157
|
-
|
142
|
+
|
158
143
|
# Allows for nested block groups, store the current block positions array and start a new one
|
159
|
-
|
160
|
-
self.
|
161
|
-
self.block_groups[m] = self.
|
162
|
-
|
144
|
+
original_queued_blocks = self.queued_blocks
|
145
|
+
self.queued_blocks = []
|
146
|
+
self.block_groups[m] = self.queued_blocks
|
147
|
+
|
163
148
|
# Capture the contents of the block group (this will only capture block definitions and block uses)
|
164
149
|
view.capture(global_options.merge(options), &block) if block_given?
|
165
|
-
|
150
|
+
|
166
151
|
# restore the original block positions array
|
167
|
-
self.
|
152
|
+
self.queued_blocks = original_queued_blocks
|
168
153
|
nil
|
169
154
|
end
|
170
|
-
|
155
|
+
|
171
156
|
protected
|
172
|
-
|
157
|
+
|
173
158
|
def initialize(view, options={}, &block)
|
174
159
|
options[options[:variable] ? options[:variable].to_sym : :blocks] = self
|
175
160
|
options[:templates_folder] = "blocks" if options[:templates_folder].nil?
|
176
|
-
|
161
|
+
|
177
162
|
self.view = view
|
178
163
|
self.global_options = options
|
179
164
|
self.block = block
|
180
|
-
self.
|
181
|
-
self.blocks = {}
|
165
|
+
self.queued_blocks = []
|
166
|
+
self.blocks = {}
|
182
167
|
self.anonymous_block_number = 0
|
183
|
-
self.block_groups = {}
|
184
|
-
self.start_rendering_blocks = true
|
168
|
+
self.block_groups = {}
|
185
169
|
end
|
186
|
-
|
170
|
+
|
187
171
|
def anonymous_block_name
|
188
172
|
self.anonymous_block_number = self.anonymous_block_number + 1
|
189
173
|
"block_#{anonymous_block_number}"
|
190
174
|
end
|
191
|
-
|
192
|
-
def render_block(name_or_container, args, runtime_options={})
|
175
|
+
|
176
|
+
def render_block(name_or_container, args, runtime_options={})
|
193
177
|
buffer = ActiveSupport::SafeBuffer.new
|
194
|
-
|
178
|
+
|
195
179
|
block_options = {}
|
196
180
|
if (name_or_container.is_a?(BuildingBlocks::Container))
|
197
181
|
name = name_or_container.name.to_sym
|
@@ -199,18 +183,18 @@ module BuildingBlocks
|
|
199
183
|
else
|
200
184
|
name = name_or_container.to_sym
|
201
185
|
end
|
202
|
-
|
186
|
+
|
203
187
|
buffer << render_before_blocks(name_or_container, runtime_options)
|
204
|
-
|
205
|
-
if blocks[name]
|
188
|
+
|
189
|
+
if blocks[name]
|
206
190
|
block_container = blocks[name]
|
207
|
-
|
191
|
+
|
208
192
|
args.push(global_options.merge(block_container.options).merge(block_options).merge(runtime_options))
|
209
|
-
|
193
|
+
|
210
194
|
# If the block is taking more than one parameter, we can use *args
|
211
195
|
if block_container.block.arity > 1
|
212
196
|
buffer << view.capture(*args, &block_container.block)
|
213
|
-
|
197
|
+
|
214
198
|
# However, if the block only takes a single parameter, we do not want ruby to try to cram the args list into that parameter
|
215
199
|
# as an array
|
216
200
|
else
|
@@ -218,13 +202,13 @@ module BuildingBlocks
|
|
218
202
|
end
|
219
203
|
elsif view.blocks.blocks[name]
|
220
204
|
block_container = view.blocks.blocks[name]
|
221
|
-
|
205
|
+
|
222
206
|
args.push(global_options.merge(block_container.options).merge(block_options).merge(runtime_options))
|
223
|
-
|
207
|
+
|
224
208
|
# If the block is taking more than one parameter, we can use *args
|
225
209
|
if block_container.block.arity > 1
|
226
210
|
buffer << view.capture(*args, &block_container.block)
|
227
|
-
|
211
|
+
|
228
212
|
# However, if the block only takes a single parameter, we do not want ruby to try to cram the args list into that parameter
|
229
213
|
# as an array
|
230
214
|
else
|
@@ -242,15 +226,15 @@ module BuildingBlocks
|
|
242
226
|
# This block does not exist and no partial can be found to satify it
|
243
227
|
end
|
244
228
|
end
|
245
|
-
|
229
|
+
|
246
230
|
buffer << render_after_blocks(name_or_container, runtime_options)
|
247
|
-
|
231
|
+
|
248
232
|
buffer
|
249
233
|
end
|
250
|
-
|
234
|
+
|
251
235
|
def render_before_blocks(name_or_container, runtime_options={})
|
252
236
|
options = global_options
|
253
|
-
|
237
|
+
|
254
238
|
block_options = {}
|
255
239
|
if (name_or_container.is_a?(BuildingBlocks::Container))
|
256
240
|
name = name_or_container.name.to_sym
|
@@ -258,39 +242,37 @@ module BuildingBlocks
|
|
258
242
|
else
|
259
243
|
name = name_or_container.to_sym
|
260
244
|
end
|
261
|
-
|
245
|
+
|
262
246
|
before_name = "before_#{name.to_s}".to_sym
|
263
|
-
|
264
|
-
if blocks[name]
|
247
|
+
|
248
|
+
if blocks[name]
|
265
249
|
block_container = blocks[name]
|
266
|
-
|
267
250
|
options = options.merge(block_container.options)
|
268
251
|
elsif view.blocks.blocks[name]
|
269
252
|
block_container = view.blocks.blocks[name]
|
270
|
-
|
271
253
|
options = options.merge(block_container.options)
|
272
|
-
end
|
273
|
-
|
254
|
+
end
|
255
|
+
|
274
256
|
buffer = ActiveSupport::SafeBuffer.new
|
275
|
-
|
257
|
+
|
276
258
|
unless blocks[before_name].nil?
|
277
259
|
blocks[before_name].each do |block_container|
|
278
260
|
buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
|
279
261
|
end
|
280
262
|
end
|
281
|
-
|
263
|
+
|
282
264
|
unless view.blocks.blocks[before_name].nil? || view.blocks.blocks == blocks
|
283
265
|
view.blocks.blocks[before_name].each do |block_container|
|
284
266
|
buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
|
285
267
|
end
|
286
268
|
end
|
287
|
-
|
269
|
+
|
288
270
|
buffer
|
289
271
|
end
|
290
|
-
|
272
|
+
|
291
273
|
def render_after_blocks(name_or_container, runtime_options={})
|
292
274
|
options = global_options
|
293
|
-
|
275
|
+
|
294
276
|
block_options = {}
|
295
277
|
if (name_or_container.is_a?(BuildingBlocks::Container))
|
296
278
|
name = name_or_container.name.to_sym
|
@@ -298,34 +280,43 @@ module BuildingBlocks
|
|
298
280
|
else
|
299
281
|
name = name_or_container.to_sym
|
300
282
|
end
|
301
|
-
|
283
|
+
|
302
284
|
after_name = "after_#{name.to_s}".to_sym
|
303
|
-
|
304
|
-
if blocks[name]
|
285
|
+
|
286
|
+
if blocks[name]
|
305
287
|
block_container = blocks[name]
|
306
|
-
|
288
|
+
|
307
289
|
options = options.merge(block_container.options)
|
308
290
|
elsif view.blocks.blocks[name]
|
309
291
|
block_container = view.blocks.blocks[name]
|
310
|
-
|
292
|
+
|
311
293
|
options = options.merge(block_container.options)
|
312
|
-
end
|
313
|
-
|
294
|
+
end
|
295
|
+
|
314
296
|
buffer = ActiveSupport::SafeBuffer.new
|
315
|
-
|
297
|
+
|
316
298
|
unless blocks[after_name].nil?
|
317
299
|
blocks[after_name].each do |block_container|
|
318
300
|
buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
|
319
301
|
end
|
320
302
|
end
|
321
|
-
|
303
|
+
|
322
304
|
unless view.blocks.blocks[after_name].nil? || view.blocks.blocks == blocks
|
323
305
|
view.blocks.blocks[after_name].each do |block_container|
|
324
306
|
buffer << view.capture(options.merge(block_container.options).merge(block_options).merge(runtime_options), &block_container.block)
|
325
307
|
end
|
326
308
|
end
|
327
|
-
|
309
|
+
|
328
310
|
buffer
|
329
311
|
end
|
312
|
+
|
313
|
+
def define_block_container(name, options, &block)
|
314
|
+
block_container = BuildingBlocks::Container.new
|
315
|
+
block_container.name = name
|
316
|
+
block_container.options = options
|
317
|
+
block_container.block = block
|
318
|
+
blocks[name.to_sym] = block_container if blocks[name.to_sym].nil? && block_given?
|
319
|
+
block_container
|
320
|
+
end
|
330
321
|
end
|
331
322
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe BuildingBlocks::Base do
|
4
|
+
before :each do
|
5
|
+
@builder = BuildingBlocks::Base.new({})
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "defined? method" do
|
9
|
+
it "should be able to determine if a block by a specific name is already defined" do
|
10
|
+
@builder.defined?(:test_block).should be_false
|
11
|
+
@builder.define :test_block do end
|
12
|
+
@builder.defined?(:test_block).should be_true
|
13
|
+
@builder.defined?("test_block").should be_true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "define method" do
|
18
|
+
it "should be able to define a new block" do
|
19
|
+
block = Proc.new { |options| }
|
20
|
+
|
21
|
+
@builder.define :test_block, :option1 => "value1", :option2 => "value2", &block
|
22
|
+
|
23
|
+
test_block = @builder.blocks[:test_block]
|
24
|
+
test_block.options[:option1].should eql("value1")
|
25
|
+
test_block.options[:option2].should eql("value2")
|
26
|
+
test_block.name.should eql(:test_block)
|
27
|
+
test_block.block.should eql(block)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not replace a defined block with another attempted definition" do
|
31
|
+
block1 = Proc.new do |options| end
|
32
|
+
@builder.define :test_block, :option1 => "value1", :option2 => "value2", &block1
|
33
|
+
|
34
|
+
block2 = Proc.new do |options| end
|
35
|
+
@builder.define :test_block, :option3 => "value3", :option4 => "value4", &block2
|
36
|
+
|
37
|
+
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)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "replace method" do
|
48
|
+
it "should be able to replace a defined block" do
|
49
|
+
block1 = Proc.new do |options| end
|
50
|
+
@builder.define :test_block, :option1 => "value1", :option2 => "value2", &block1
|
51
|
+
|
52
|
+
block2 = Proc.new do |options| end
|
53
|
+
@builder.replace :test_block, :option3 => "value3", :option4 => "value4", &block2
|
54
|
+
|
55
|
+
test_block = @builder.blocks[:test_block]
|
56
|
+
test_block.options[:option1].should be_nil
|
57
|
+
test_block.options[:option2].should be_nil
|
58
|
+
test_block.options[:option3].should eql("value3")
|
59
|
+
test_block.options[:option4].should eql("value4")
|
60
|
+
test_block.name.should eql(:test_block)
|
61
|
+
test_block.block.should eql(block2)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "use method" do
|
66
|
+
it "should be able to use a defined block by its name" do
|
67
|
+
@builder.expects(:render_block).with(:test_block, [], {})
|
68
|
+
|
69
|
+
block = Proc.new do |options| end
|
70
|
+
@builder.define :test_block, :option1 => "value1", :option2 => "value2", &block
|
71
|
+
|
72
|
+
@builder.use :test_block
|
73
|
+
end
|
74
|
+
|
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"})
|
77
|
+
|
78
|
+
block = Proc.new do |options| end
|
79
|
+
@builder.define :test_block, :option1 => "value1", :option2 => "value2", &block
|
80
|
+
|
81
|
+
@builder.use :test_block, "value5", :option3 => "value3", :option4 => "value4"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/spec_helper.rb
ADDED
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:
|
5
|
-
prerelease:
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 8
|
10
|
+
version: 0.0.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andrew Hunter
|
@@ -15,12 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-20 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
prerelease: false
|
23
|
-
type: :runtime
|
24
22
|
name: building-blocks
|
25
23
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
24
|
none: false
|
@@ -31,10 +29,10 @@ dependencies:
|
|
31
29
|
segments:
|
32
30
|
- 0
|
33
31
|
version: "0"
|
34
|
-
requirement: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
32
|
prerelease: false
|
37
33
|
type: :runtime
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
38
36
|
name: rails
|
39
37
|
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
38
|
none: false
|
@@ -47,7 +45,23 @@ dependencies:
|
|
47
45
|
- 0
|
48
46
|
- 0
|
49
47
|
version: 3.0.0
|
48
|
+
prerelease: false
|
49
|
+
type: :runtime
|
50
50
|
requirement: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: jeweler
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
prerelease: false
|
63
|
+
type: :development
|
64
|
+
requirement: *id003
|
51
65
|
description: ""
|
52
66
|
email: hunterae@gmail.com
|
53
67
|
executables: []
|
@@ -61,10 +75,12 @@ files:
|
|
61
75
|
- Rakefile
|
62
76
|
- VERSION
|
63
77
|
- lib/building-blocks.rb
|
64
|
-
- lib/building_blocks/
|
78
|
+
- lib/building_blocks/base.rb
|
65
79
|
- lib/building_blocks/container.rb
|
66
80
|
- lib/building_blocks/helper_methods.rb
|
67
81
|
- rails/init.rb
|
82
|
+
- spec/building-blocks/base_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
68
84
|
has_rdoc: true
|
69
85
|
homepage: http://github.com/hunterae/building-blocks
|
70
86
|
licenses: []
|
@@ -95,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
111
|
requirements: []
|
96
112
|
|
97
113
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.
|
114
|
+
rubygems_version: 1.5.2
|
99
115
|
signing_key:
|
100
116
|
specification_version: 3
|
101
117
|
summary: ""
|