fortitude 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +28 -22
- data/CHANGES.md +50 -0
- data/CONTRIBUTORS.md +1 -0
- data/doc/.gitignore +18 -0
- data/doc/Gemfile +21 -0
- data/doc/config.rb +92 -0
- data/doc/source/images/background.png +0 -0
- data/doc/source/images/middleman.png +0 -0
- data/doc/source/images/why/icon_button.png +0 -0
- data/doc/source/images/why/icon_button@2x.png +0 -0
- data/doc/source/images/why/modal_dialog@2x.png +0 -0
- data/doc/source/index.html.pcss +96 -0
- data/doc/source/index.html.rb +66 -0
- data/doc/source/javascripts/all.js +1 -0
- data/doc/source/javascripts/highlight.pack.js +1 -0
- data/doc/source/layouts/layout.rb +55 -0
- data/doc/source/portable/fortitude-bootstrap.rb +53 -0
- data/doc/source/shared/base.pcss +62 -0
- data/doc/source/shared/base.rb +30 -0
- data/doc/source/shared/common.rb +55 -0
- data/doc/source/shared/standard_page.rb +40 -0
- data/doc/source/stylesheets/_shared_prefix.scss +25 -0
- data/doc/source/stylesheets/all.css.scss +7 -0
- data/doc/source/stylesheets/basics.css.scss +20 -0
- data/doc/source/stylesheets/bootstrap_importer.css.scss +1 -0
- data/doc/source/stylesheets/highlight/arta.css +140 -0
- data/doc/source/stylesheets/highlight/ascetic.css +52 -0
- data/doc/source/stylesheets/highlight/atelier-dune.dark.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-dune.light.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-forest.dark.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-forest.light.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-heath.dark.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-heath.light.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-lakeside.dark.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-lakeside.light.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-seaside.dark.css +95 -0
- data/doc/source/stylesheets/highlight/atelier-seaside.light.css +95 -0
- data/doc/source/stylesheets/highlight/brown_paper.css +104 -0
- data/doc/source/stylesheets/highlight/brown_papersq.png +0 -0
- data/doc/source/stylesheets/highlight/codepen-embed.css +108 -0
- data/doc/source/stylesheets/highlight/color-brewer.css +168 -0
- data/doc/source/stylesheets/highlight/dark.css +104 -0
- data/doc/source/stylesheets/highlight/default.css +152 -0
- data/doc/source/stylesheets/highlight/docco.css +135 -0
- data/doc/source/stylesheets/highlight/far.css +111 -0
- data/doc/source/stylesheets/highlight/foundation.css +136 -0
- data/doc/source/stylesheets/highlight/github.css +124 -0
- data/doc/source/stylesheets/highlight/googlecode.css +147 -0
- data/doc/source/stylesheets/highlight/hybrid.css +170 -0
- data/doc/source/stylesheets/highlight/idea.css +125 -0
- data/doc/source/stylesheets/highlight/ir_black.css +109 -0
- data/doc/source/stylesheets/highlight/kimbie.dark.css +96 -0
- data/doc/source/stylesheets/highlight/kimbie.light.css +96 -0
- data/doc/source/stylesheets/highlight/magula.css +121 -0
- data/doc/source/stylesheets/highlight/mono-blue.css +69 -0
- data/doc/source/stylesheets/highlight/monokai.css +127 -0
- data/doc/source/stylesheets/highlight/monokai_sublime.css +154 -0
- data/doc/source/stylesheets/highlight/obsidian.css +153 -0
- data/doc/source/stylesheets/highlight/paraiso.dark.css +95 -0
- data/doc/source/stylesheets/highlight/paraiso.light.css +95 -0
- data/doc/source/stylesheets/highlight/pojoaque.css +107 -0
- data/doc/source/stylesheets/highlight/pojoaque.jpg +0 -0
- data/doc/source/stylesheets/highlight/railscasts.css +187 -0
- data/doc/source/stylesheets/highlight/rainbow.css +108 -0
- data/doc/source/stylesheets/highlight/school_book.css +112 -0
- data/doc/source/stylesheets/highlight/school_book.png +0 -0
- data/doc/source/stylesheets/highlight/solarized_dark.css +108 -0
- data/doc/source/stylesheets/highlight/solarized_light.css +108 -0
- data/doc/source/stylesheets/highlight/sunburst.css +164 -0
- data/doc/source/stylesheets/highlight/tomorrow-night-blue.css +95 -0
- data/doc/source/stylesheets/highlight/tomorrow-night-bright.css +94 -0
- data/doc/source/stylesheets/highlight/tomorrow-night-eighties.css +94 -0
- data/doc/source/stylesheets/highlight/tomorrow-night.css +95 -0
- data/doc/source/stylesheets/highlight/tomorrow.css +92 -0
- data/doc/source/stylesheets/highlight/vs.css +93 -0
- data/doc/source/stylesheets/highlight/xcode.css +158 -0
- data/doc/source/stylesheets/highlight/zenburn.css +118 -0
- data/doc/source/why/a_larger_view.html.rb +774 -0
- data/doc/source/why/a_simple_helper.html.rb +332 -0
- data/doc/source/why/building_a_rich_modal_dialog.html.rb +156 -0
- data/doc/source/why/commonality_and_inheritance.html.rb +564 -0
- data/doc/source/why/example_list.rb +60 -0
- data/doc/source/why/example_page.rb +116 -0
- data/doc/source/why/index.html.rb +86 -0
- data/doc/source/why/other_benefits.html.rb +110 -0
- data/fortitude.gemspec +6 -1
- data/lib/fortitude/doctypes/html4_tags_strict.rb +1 -0
- data/lib/fortitude/doctypes/html5.rb +1 -0
- data/lib/fortitude/method_templates/tag_method_template.rb.smpl +27 -14
- data/lib/fortitude/rails/helpers.rb +2 -2
- data/lib/fortitude/rendering_context.rb +10 -1
- data/lib/fortitude/tags/tag.rb +3 -2
- data/lib/fortitude/tags/tag_support.rb +8 -3
- data/lib/fortitude/tilt/fortitude_template.rb +6 -2
- data/lib/fortitude/version.rb +1 -1
- data/lib/fortitude/widget.rb +2 -0
- data/lib/fortitude/widget/convenience.rb +30 -0
- data/lib/fortitude/widget/files.rb +22 -11
- data/lib/fortitude/widget/needs.rb +5 -3
- data/spec/helpers/system_helpers.rb +1 -0
- data/spec/rails/development_mode_system_spec.rb +0 -1
- data/spec/rails/rendering_system_spec.rb +20 -1
- data/spec/rails/templates/rendering_system_spec/app/controllers/rendering_system_spec_controller.rb +13 -0
- data/spec/rails/templates/rendering_system_spec/app/views/rendering_system_spec/render_hash_subclass.rb +18 -0
- data/spec/rails/templates/rendering_system_spec/lib/my_hash.rb +5 -0
- data/spec/system/convenience_methods_system_spec.rb +166 -0
- data/spec/system/formatting_system_spec.rb +25 -1
- data/spec/system/tag_rendering_system_spec.rb +73 -0
- data/spec/system/tilt_system_spec.rb +31 -0
- data/spec/system/widget_class_from_spec.rb +20 -4
- metadata +92 -4
@@ -0,0 +1,332 @@
|
|
1
|
+
require 'source/why/example_page'
|
2
|
+
|
3
|
+
module Views
|
4
|
+
module Why
|
5
|
+
class ASimpleHelper < Views::Why::ExamplePage
|
6
|
+
def example_intro
|
7
|
+
p %{Fortitude expresses your view code as Ruby itself, using a simple DSL patterned
|
8
|
+
after HTML. One big benefit of this approach is its consistency: views, helpers,
|
9
|
+
and partials are all written in the exact same language, Ruby.}
|
10
|
+
|
11
|
+
p %{Ruby is a great deal nicer to
|
12
|
+
work with than either an HTML templating language or the string interpolation of traditional
|
13
|
+
helper methods. We’ll see the difference this can make in the following example.}
|
14
|
+
end
|
15
|
+
|
16
|
+
def example_description
|
17
|
+
p %{The following ERb code was extracted from a real-world application, and is a great, simple
|
18
|
+
example of where Fortitude can help the most.}
|
19
|
+
|
20
|
+
erb <<-EOS
|
21
|
+
...
|
22
|
+
<a href='<%= conditional_refresh_url(:user => @user) %>'
|
23
|
+
class="button icon refresh" onclick="javascript:handleRefreshClick();">
|
24
|
+
<div class="button_text">
|
25
|
+
<p>Refresh this page if:</p>
|
26
|
+
<ul>
|
27
|
+
<li>Content has changed</li>
|
28
|
+
<li>Local data is <%= @out_of_date_condition %></li>
|
29
|
+
</ul>
|
30
|
+
</div>
|
31
|
+
</a>
|
32
|
+
...
|
33
|
+
EOS
|
34
|
+
|
35
|
+
p %{Combined with appropriate CSS, this code creates an “icon button” — a small icon, with a tooltip available:}
|
36
|
+
|
37
|
+
featured_image 'why/icon_button.png'
|
38
|
+
|
39
|
+
p {
|
40
|
+
text %{Unsurprisingly, this button gets used in }; em "many"; text %{ places throughout the application. }
|
41
|
+
text %{Using ERb, let’s see what we can do to factor out this common pattern.}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def using_standard_engines
|
46
|
+
refactoring_choices
|
47
|
+
using_a_helper
|
48
|
+
using_a_partial
|
49
|
+
end
|
50
|
+
|
51
|
+
def refactoring_choices
|
52
|
+
p %{Using traditional templating engines, we have two choices:}
|
53
|
+
|
54
|
+
ul {
|
55
|
+
li {
|
56
|
+
text "We can create a "; em "helper"; text ", which is written using Ruby string interpolation and called "
|
57
|
+
text "like a traditional Ruby method;"
|
58
|
+
}
|
59
|
+
li {
|
60
|
+
text "Or we can create a "; em "partial"; text ", which is written using our templating language of choice "
|
61
|
+
text "and called using "; code "render :partial => ..."; text "."
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
p %{In either case, we’ll need to make sure we allow for several variations in our desired output:}
|
66
|
+
|
67
|
+
ul {
|
68
|
+
li {
|
69
|
+
text "The URL (the "; code "href"; text " parameter) is, of course, different in almost every case."
|
70
|
+
}
|
71
|
+
li {
|
72
|
+
text %{Sometimes we need to add additional attributes }; em "(e.g."; text ", "; code "onclick"; text ", "
|
73
|
+
code "data-*"; text ", "; em "etc."; text ") to the "; code "a"; text " element, and sometimes not."
|
74
|
+
}
|
75
|
+
li {
|
76
|
+
text %{What’s inside the tooltip varies: it can be plain text or HTML, it can have variable substitutions —
|
77
|
+
really, it can be anything at all.}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
p %{Given these constraints, let’s see how each of these looks.}
|
82
|
+
end
|
83
|
+
|
84
|
+
def using_a_helper
|
85
|
+
h5 "Using a Helper"
|
86
|
+
|
87
|
+
p %{Here’s what our new helper method looks like:}
|
88
|
+
|
89
|
+
ruby "application_helper.rb", <<-EOS
|
90
|
+
def icon_button(icon_name, target, tooltip_html, additional_attributes_string = "")
|
91
|
+
"<a href=\"\#{target}\" class="button icon \#{icon_name}" \#{additional_attributes_string}><div class="button_text">\#{tooltip_html}</div></a>"
|
92
|
+
end
|
93
|
+
EOS
|
94
|
+
|
95
|
+
p %{And here’s how we’d call it for the example above:}
|
96
|
+
|
97
|
+
erb <<-EOS
|
98
|
+
...
|
99
|
+
<%= icon_button(:refresh, conditional_refresh_url(:user => @user), "<p>Refresh this page if:</p><ul><li>Content has changed</li><li>Local data is \#{h(@out_of_date_condition)}</li></ul>", "onclick=\"javascript:handleRefreshClick();\"")
|
100
|
+
EOS
|
101
|
+
|
102
|
+
p "What’s good and what’s bad about this?"
|
103
|
+
|
104
|
+
ul {
|
105
|
+
li {
|
106
|
+
strong "Concise"; text ": Both the caller and helper are quite short; but:"
|
107
|
+
}
|
108
|
+
li {
|
109
|
+
strong "Inconsistent"; text ": Suddenly, we’re forced to write both the helper method "; em "and"
|
110
|
+
text " the HTML for the interior of the button using Ruby string interpolation, rather than ERb — "
|
111
|
+
text "which leads to:"
|
112
|
+
}
|
113
|
+
li {
|
114
|
+
strong "Messy"; text ": We have significant stretches of HTML sitting around in Ruby strings, "
|
115
|
+
text "which is quite hard to read;"
|
116
|
+
}
|
117
|
+
li {
|
118
|
+
strong "Bad Formatting"; text ": Because of this, the generated HTML will be quite poorly formatted, "
|
119
|
+
text "with the HTML in those strings all run-together. But, most importantly:"
|
120
|
+
}
|
121
|
+
li {
|
122
|
+
strong { em "Dangerous (XSS Potential)" }; text ": We’re now responsible for worrying about HTML escaping "
|
123
|
+
text "in a way we weren’t before: "; em "every single caller"; text " needs to make sure that any user "
|
124
|
+
text "data in the "; code "additional_attributes_string"; text " is escaped "; em "before"; text " being "
|
125
|
+
text "passed. Similarly, we need to remember to call "; code "h()"; text " on the variable being "
|
126
|
+
text "interpolated into the "; code "tooltip_html"; text " string before passing it, or else we’ll be "
|
127
|
+
text "vulnerable to XSS attacks again."
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
p {
|
132
|
+
text "Yikes. We’ve factored out this helper method, which cleans up our calling code, but at a rather significant "
|
133
|
+
text "expense. Perhaps we can do better by creating an actual partial, instead?"
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def using_a_partial
|
138
|
+
h5 "Using a Partial"
|
139
|
+
|
140
|
+
p %{Here’s what our new partial looks like:}
|
141
|
+
|
142
|
+
erb <<-EOS
|
143
|
+
<a href="<%= target %>" class="button icon <%= icon_name %>"
|
144
|
+
<%= (defined?(additional_attributes) ? additional_attributes || '') %>">
|
145
|
+
<div class="button_text">
|
146
|
+
<%= tooltip_html %>
|
147
|
+
</div>
|
148
|
+
</a>
|
149
|
+
EOS
|
150
|
+
|
151
|
+
p %{And here’s how we’d call it for the example above:}
|
152
|
+
|
153
|
+
erb <<-EOS
|
154
|
+
<%= render :partial => '/shared/buttons/icon_button', :locals => {
|
155
|
+
:target => conditional_refresh_url(:user => @user),
|
156
|
+
:icon_name => 'refresh',
|
157
|
+
:additional_attributes => 'onclick="javascript:handleRefreshClick();"'.html_safe,
|
158
|
+
:tooltip_html => %{<p>Refresh this page if:</p>
|
159
|
+
<ul>
|
160
|
+
<li>Content has changed</li>
|
161
|
+
<li>Local data is \#{h(@out_of_date_condition)}</li>
|
162
|
+
</ul>}.html_safe
|
163
|
+
} %>
|
164
|
+
EOS
|
165
|
+
|
166
|
+
p "Again, what’s good and what’s bad about this?"
|
167
|
+
|
168
|
+
ul {
|
169
|
+
li {
|
170
|
+
strong "Consistency/Inconsistency"; text ": on one hand, at least now the partial itself is expressed using "
|
171
|
+
code "ERb"; text ", not a Ruby string. On the other hand, we still have to use a Ruby string for the HTML we’re "
|
172
|
+
text "passing in to it."
|
173
|
+
}
|
174
|
+
li {
|
175
|
+
strong "Verbose and Messy"; text ": we’ve actually managed to create a caller that is 33% "
|
176
|
+
em "longer"; text " than the original code it replaced, and is actually quite a lot harder to read at "
|
177
|
+
text "first glance. (The helper method was a lot better here.)"
|
178
|
+
}
|
179
|
+
li {
|
180
|
+
strong "Formatting"; text ": the resulting HTML will at least look a lot better than in our earlier example;"
|
181
|
+
}
|
182
|
+
li {
|
183
|
+
strong "Method Signature"; text ": when reading the new "; code "_icon_button.html.erb"; text "partial, "
|
184
|
+
text "how can you easily tell which variables you need to pass in, and which of those are optional? "
|
185
|
+
em "You can’t"; text " — except by reading through the entire text of the partial and thinking about "
|
186
|
+
text "each use carefully, which is really painful. (In this, the helper method was certainly "
|
187
|
+
text "a lot cleaner, too.)"
|
188
|
+
}
|
189
|
+
li {
|
190
|
+
strong { em "Dangerous (XSS Potential)" }; text ": Once again, we’re now responsible for worrying about "
|
191
|
+
text "HTML escaping in a way we weren’t before: "; em "every single caller"; text " needs to make sure that "
|
192
|
+
text "any user data in the "; code "tooltip_html"; text " string is correctly escaped using "; code "h()"
|
193
|
+
text " before being passed in. (We also need to make sure we call "; code "#html_safe"; text " on the "
|
194
|
+
text "value we pass to "; code "additional_attributes"; text ", too; this is less dangerous, but also very "
|
195
|
+
text "easy to forget, and will cause corrupt HTML if we forget about it.)"
|
196
|
+
}
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
def standard_engine_issues
|
201
|
+
p {
|
202
|
+
text "Both cases above are far from perfect. Helper methods are more succinct to call, yet also a lot "
|
203
|
+
text "messier to both write and call when passing around HTML, and have serious XSS risks. Partials mitigate "
|
204
|
+
text "some of those problems, but introduce others, and are "; em "really"; text " verbose and messy to call."
|
205
|
+
}
|
206
|
+
|
207
|
+
p {
|
208
|
+
text "Ironically, some of the issues above may feel a little unfamiliar, and it’s probably because of this: "
|
209
|
+
em "nobody does this"; text " — because both these approaches have such serious tradeoffs, most teams, "
|
210
|
+
text "developers, or designers just leave well enough alone for small(ish) examples like this, and "
|
211
|
+
text "repeat the original HTML everywhere, over "
|
212
|
+
text "and over. And when it comes time to change the "
|
213
|
+
text "HTML structure of this “icon button” element that’s been repeated all over the site, we either just "
|
214
|
+
text "bite the bullet and do a really painful global search and repeated manual modificaftion, or give up, "
|
215
|
+
text "hack on CSS to make it do sort of what we want it to do, and then leave "; em "that"
|
216
|
+
text " mess in place."
|
217
|
+
}
|
218
|
+
|
219
|
+
p {
|
220
|
+
text "We imagine that this is "; em "just the way views are"; text ". "
|
221
|
+
text "In fact, these problems are all because we generally don’t have the right tools to do a better job. "
|
222
|
+
text "Views don’t need to be like this, any more than "; em "any"; text " code needs to be like this."
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
def using_fortitude
|
227
|
+
p %{To see what Fortitude brings to this example, let’s start by looking at Fortitude code for the original
|
228
|
+
example, before we try to refactor it:}
|
229
|
+
|
230
|
+
fortitude <<-EOS
|
231
|
+
...
|
232
|
+
a(:href => conditional_refresh_url(:user => @user),
|
233
|
+
:class => 'button icon refresh') {
|
234
|
+
div(:class => 'button_text') {
|
235
|
+
p "Refresh this page if:"
|
236
|
+
ul {
|
237
|
+
li "Content has changed"
|
238
|
+
li "Local data is \#{@out_of_date_condition}"
|
239
|
+
}
|
240
|
+
}
|
241
|
+
}
|
242
|
+
...
|
243
|
+
EOS
|
244
|
+
|
245
|
+
p {
|
246
|
+
text %{We won’t delve more deeply into Fortitude’s syntax immediately, but it should be clear that
|
247
|
+
Fortitude expresses HTML using a very simple Ruby DSL that’s easy to grasp.}
|
248
|
+
}
|
249
|
+
|
250
|
+
p {
|
251
|
+
text %{Using this syntax, we can now factor out this shared code as a “helper method” that
|
252
|
+
we can easily make available on any view that needs it.}
|
253
|
+
}
|
254
|
+
|
255
|
+
p {
|
256
|
+
text "(We put “helper method” in quotes because "
|
257
|
+
text "this isn’t a traditional helper that we’d put into something like "
|
258
|
+
code "app/helpers/application_helper.rb"; text " and write using Ruby string interpolation. Rather, it’s a "
|
259
|
+
text "method we define in a module, and mix into any view class that needs it — Fortitude views are actually "
|
260
|
+
text "just normal Ruby classes — or into our base view class to make it magically available everywhere.)"
|
261
|
+
}
|
262
|
+
|
263
|
+
p {
|
264
|
+
text "Using this strategy, we can create the following “helper method”:"
|
265
|
+
}
|
266
|
+
|
267
|
+
fortitude <<-EOS
|
268
|
+
def icon_button(icon_name, target, additional_attributes = { })
|
269
|
+
a(additional_attributes.merge(
|
270
|
+
:href => target, :class => "button icon \#{icon_name}")) {
|
271
|
+
div(:class => :button_text) {
|
272
|
+
yield
|
273
|
+
}
|
274
|
+
}
|
275
|
+
end
|
276
|
+
EOS
|
277
|
+
|
278
|
+
p "And the calling code for the example above now looks like this:"
|
279
|
+
|
280
|
+
fortitude <<-EOS
|
281
|
+
...
|
282
|
+
icon_button('refresh', conditional_refresh_url(:user => @user),
|
283
|
+
:onclick => 'javascript:handleRefreshClick();') {
|
284
|
+
p "Refresh this page if:"
|
285
|
+
ul {
|
286
|
+
li "Content has changed"
|
287
|
+
li "Local data is \#{@out_of_date_condition}"
|
288
|
+
}
|
289
|
+
}
|
290
|
+
...
|
291
|
+
EOS
|
292
|
+
end
|
293
|
+
|
294
|
+
def fortitude_benefits
|
295
|
+
p %{Let’s take a look at how much cleaner this is. A few of the most obvious improvements:}
|
296
|
+
|
297
|
+
ul {
|
298
|
+
li {
|
299
|
+
strong "Concise"; text %{: The calling code is now actually both much }; em "shorter"; text " and "
|
300
|
+
em "more expressive"; text " than the code it replaced."
|
301
|
+
}
|
302
|
+
li {
|
303
|
+
strong "Consistent"; text ": both the new “helper method” and its caller are written in the same "
|
304
|
+
text "language — which is the same language you use for all Fortitude code and the entire rest of your "
|
305
|
+
text "application, Ruby."
|
306
|
+
}
|
307
|
+
li {
|
308
|
+
strong "Well-Formatted"; text ": Because Fortitude is not interpolating strings, but, rather, understands the "
|
309
|
+
text "structure of the HTML, all output is perfectly formatted. (In development, it’s indented correctly, "
|
310
|
+
text "no matter how the code is written; in production, it is automatically produced with minimal spacing, "
|
311
|
+
text "to reduce the size of the HTML transmitted across the network.)"
|
312
|
+
}
|
313
|
+
li {
|
314
|
+
strong "Clean and Clear"; text ": Because it’s a Ruby method, any Ruby programmer will instantly understand "
|
315
|
+
text "what needs to be passed to it; because all of the code is Ruby, it all reads cleanly and clearly."
|
316
|
+
}
|
317
|
+
li {
|
318
|
+
strong { em "Safe" }; text ": at no point does any caller, nor the method itself, need to worry about HTML "
|
319
|
+
text "escaping; all data will be escaped properly without worrying about it."
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
p {
|
324
|
+
text "However, one of the biggest improvements we’ve made is slightly more subtle: by creating this new "
|
325
|
+
code "icon_button"; text " method, we’ve started slowly building up a "; em "customized view language"
|
326
|
+
text " that is specific to our needs and our application. As we proceed through further examples, we’ll see "
|
327
|
+
text "how powerful this can be in creating clean, concise, maintainable views that are a joy to use."
|
328
|
+
}
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'source/why/example_page'
|
2
|
+
|
3
|
+
module Views
|
4
|
+
module Why
|
5
|
+
class BuildingARichModalDialog < Views::Why::ExamplePage
|
6
|
+
def example_intro
|
7
|
+
p {
|
8
|
+
text "In our last example, we saw how using inheritance lets you easily solve view-factoring problems "
|
9
|
+
text "with Fortitude that remain painful in traditional templating engines. In this example, we’ll see "
|
10
|
+
text "how Fortitude’s widget classes let us create specialized contexts for rendering views that are "
|
11
|
+
text "very powerful. In effect, they become little mini-languages, introducing view primitives exactly "
|
12
|
+
text "when you need them and providing an elegant language for you to describe what your view should "
|
13
|
+
text "look like."
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def example_description
|
18
|
+
p {
|
19
|
+
text "Almost everybody knows the concept of a "; em "modal dialog"; text " — a “layer” of HTML that "
|
20
|
+
text "requests some kind of input or confirmation from the user before proceeding, and which obscures "
|
21
|
+
text "the rest of the web page so that the user is forced to provide this input or confirmation before "
|
22
|
+
text "proceeding. They’re often also known as a "; em "lightbox"; text ", "; em "modal window"; text ", or "
|
23
|
+
em "heavy window"; text ", and have become a staple of modern Web design."
|
24
|
+
}
|
25
|
+
|
26
|
+
p {
|
27
|
+
text "Here’s an example of a modal dialog that appears in Google Docs when you choose “Make a Copy”: "
|
28
|
+
}
|
29
|
+
|
30
|
+
featured_image 'why/modal_dialog.png'
|
31
|
+
|
32
|
+
p {
|
33
|
+
text "This simple example has a number of features that modal dialogs tend to share:"
|
34
|
+
}
|
35
|
+
|
36
|
+
ul {
|
37
|
+
li "A clear border, often with a drop shadow, around the entire dialog."
|
38
|
+
li "A large layer that dims or otherwise obscures the rest of the page behind the dialog."
|
39
|
+
li "Some kind of “close” or “dismiss” control, often in the upper-right-hand corner."
|
40
|
+
li "A title of some kind at the top — sometimes styled, sometimes not."
|
41
|
+
li "Primary content, which can be more-or-less arbitraily complex."
|
42
|
+
li "At the bottom, at least one button (“OK”), and often more than one."
|
43
|
+
}
|
44
|
+
|
45
|
+
p {
|
46
|
+
text "These simple dialogs present an interesting challenge for view builders. Even the frame of the dialog "
|
47
|
+
text "will consist of multiple elements (usually "; code "div"; text "s), with some important CSS classes "
|
48
|
+
text "applied. There will be a standard way of creating tht title, and likely a container that the primary "
|
49
|
+
text "content needs to go into. The buttons at the bottom will typically have their own container and styles, "
|
50
|
+
text "and standard ways of rendering them."
|
51
|
+
}
|
52
|
+
|
53
|
+
p {
|
54
|
+
text "Let’s look at an example modal dialog in completely non-refactored (one-off) HTML:"
|
55
|
+
}
|
56
|
+
|
57
|
+
erb 'app/views/docs/copy_document.html.erb', <<-EOS
|
58
|
+
<div class="modal-background">
|
59
|
+
<div class="modal-dialog">
|
60
|
+
<div class="window-controls"><span class="dialog-control">X</span></div>
|
61
|
+
<div class="modal-title">
|
62
|
+
<h3>Copy document</h3>
|
63
|
+
</div>
|
64
|
+
|
65
|
+
<div class="modal-contents">
|
66
|
+
<form class="modal-form">
|
67
|
+
<label for="document_name">Enter a new document name:</label>
|
68
|
+
<input type="text" name="document_name">Copy of Untitled document</input>
|
69
|
+
<span class="form-input-footnote">Comments will not be copied to the new document.</span>
|
70
|
+
|
71
|
+
<input type="checkbox" name="copy_sharing"></input>
|
72
|
+
<label for="copy_sharing">Share it with the same people</label>
|
73
|
+
</form>
|
74
|
+
</div>
|
75
|
+
|
76
|
+
<div class="modal-actions">
|
77
|
+
<button name="OK" value="ok" class="modal-button">OK</button>
|
78
|
+
<button name="Cancel" value="cancel" class="modal-button">Cancel</button>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
</div>
|
82
|
+
EOS
|
83
|
+
end
|
84
|
+
|
85
|
+
def using_standard_engines
|
86
|
+
p {
|
87
|
+
text "Using traditional templating engines, what’s the best we can do with this? In many ways, it’s similar "
|
88
|
+
text "to our previous example: we can either use partials/helpers for shared structural elements and count "
|
89
|
+
text "on calling them in the right order, or use "; code "capture"; text " to construct blocks of HTML and "
|
90
|
+
text "pass them into a shared view."
|
91
|
+
}
|
92
|
+
|
93
|
+
p {
|
94
|
+
text "One of the big differences here is that the chunks of HTML we pass in themselves need certain "
|
95
|
+
text "predefined structure. For example, the modal title may need to be in a certain format (at least most "
|
96
|
+
text "of the time), and the set of buttons at the bottom may need certain classes, and so on."
|
97
|
+
}
|
98
|
+
|
99
|
+
p {
|
100
|
+
text "Without further ado, let’s see what the end result looks like:"
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
erb 'app/views/docs/copy_document.html.erb', <<-EOS
|
105
|
+
<% the_modal_title = capture do %>
|
106
|
+
<%= modal_title("Copy document") %>
|
107
|
+
<% end %>
|
108
|
+
|
109
|
+
<% the_modal_contents = capture do %>
|
110
|
+
<%= modal_form do %>
|
111
|
+
<label for="document_name">Enter a new document name:</label>
|
112
|
+
<input type="text" name="document_name">Copy of Untitled document</input>
|
113
|
+
<span class="form-input-footnote">Comments will not be copied to the new document.</span>
|
114
|
+
|
115
|
+
<input type="checkbox" name="copy_sharing"></input>
|
116
|
+
<label for="copy_sharing">Share it with the same people</label>
|
117
|
+
<% end %>
|
118
|
+
<% end %>
|
119
|
+
<div class="modal-title">
|
120
|
+
<h3 class="modal-title">Copy document</h3>
|
121
|
+
</div>
|
122
|
+
|
123
|
+
<div class="modal-contents">
|
124
|
+
<form class="modal-form">
|
125
|
+
<label for="document_name">Enter a new document name:</label>
|
126
|
+
<input type="text" name="document_name">Copy of Untitled document</input>
|
127
|
+
<span class="form-input-footnote">Comments will not be copied to the new document.</span>
|
128
|
+
|
129
|
+
<input type="checkbox" name="copy_sharing"></input>
|
130
|
+
<label for="copy_sharing">Share it with the same people</label>
|
131
|
+
</form>
|
132
|
+
</div>
|
133
|
+
|
134
|
+
<div class="modal-actions">
|
135
|
+
<button name="OK" value="ok" class="modal-button">OK</button>
|
136
|
+
<button name="Cancel" value="cancel" class="modal-button">Cancel</button>
|
137
|
+
</div>
|
138
|
+
</div>
|
139
|
+
</div>
|
140
|
+
EOS
|
141
|
+
end
|
142
|
+
|
143
|
+
def standard_engine_issues
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def using_fortitude
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def fortitude_benefits
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|