fortitude 0.9.1-java → 0.9.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +91 -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
|