kodekopelli 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +32 -0
  3. data/README +78 -0
  4. data/Rakefile +87 -0
  5. data/bin/kodekopelli +5 -0
  6. data/examples/faqomatic/config.xml +40 -0
  7. data/examples/faqomatic/content.rhtml +376 -0
  8. data/examples/faqomatic/generated/README.txt +1 -0
  9. data/examples/faqomatic/includes/shampoo_answer.txt +1 -0
  10. data/examples/faqomatic/templates/faqs.txt +22 -0
  11. data/examples/faqomatic/templates/great_big.txt +3 -0
  12. data/examples/faqomatic/templates/html_wrapper.txt +9 -0
  13. data/examples/thankyounotes/config.xml +18 -0
  14. data/examples/thankyounotes/content.rhtml +528 -0
  15. data/examples/thankyounotes/generated/README.txt +1 -0
  16. data/examples/thankyounotes/includes/poem.txt +3 -0
  17. data/examples/thankyounotes/templates/thanks.txt +14 -0
  18. data/examples/treemenu/config.xml +110 -0
  19. data/examples/treemenu/content.rhtml +324 -0
  20. data/examples/treemenu/generated/README.txt +1 -0
  21. data/examples/treemenu/templates/authorized.txt +1 -0
  22. data/examples/treemenu/templates/menu_category.txt +4 -0
  23. data/examples/treemenu/templates/menu_item.txt +3 -0
  24. data/examples/treemenu/templates/simple_menu.txt +3 -0
  25. data/lib/kodekopelli.rb +56 -0
  26. data/lib/kodekopelli/expandable_properties.rb +63 -0
  27. data/lib/kodekopelli/file_generator.rb +726 -0
  28. data/lib/kodekopelli/frozen_key_hash.rb +67 -0
  29. data/lib/kodekopelli/minimal_logger.rb +50 -0
  30. data/lib/kodekopelli/properties_file.rb +41 -0
  31. data/lib/kodekopelli/util.rb +30 -0
  32. data/rakefile +87 -0
  33. data/test/abstract_unit.rb +4 -0
  34. data/test/fixtures/definitions/invalid/empty +0 -0
  35. data/test/fixtures/definitions/invalid/gibberish.txt +6 -0
  36. data/test/fixtures/definitions/kodekopelli.xsd +95 -0
  37. data/test/fixtures/definitions/valid/empty_files.xml +49 -0
  38. data/test/fixtures/definitions/valid/kodekopelli_rocks_files.xml +359 -0
  39. data/test/fixtures/definitions/valid/simplest.xml +8 -0
  40. data/test/fixtures/includes/bang.txt +1 -0
  41. data/test/fixtures/includes/c.txt +1 -0
  42. data/test/fixtures/includes/d.txt +1 -0
  43. data/test/fixtures/includes/e.txt +1 -0
  44. data/test/fixtures/includes/empty.txt +0 -0
  45. data/test/fixtures/includes/i.txt +1 -0
  46. data/test/fixtures/includes/k.txt +1 -0
  47. data/test/fixtures/includes/k_upper.txt +1 -0
  48. data/test/fixtures/includes/kodekopelli_rocks.txt +1 -0
  49. data/test/fixtures/includes/l.txt +1 -0
  50. data/test/fixtures/includes/o.txt +1 -0
  51. data/test/fixtures/includes/p.txt +1 -0
  52. data/test/fixtures/includes/r.txt +1 -0
  53. data/test/fixtures/includes/s.txt +1 -0
  54. data/test/fixtures/includes/space.txt +1 -0
  55. data/test/fixtures/properties/comments.properties +17 -0
  56. data/test/fixtures/properties/empty.properties +0 -0
  57. data/test/fixtures/properties/expandable.properties +2 -0
  58. data/test/fixtures/properties/none_valid.properties +1 -0
  59. data/test/fixtures/properties/simple.properties +5 -0
  60. data/test/fixtures/templates/anything_att.txt +1 -0
  61. data/test/fixtures/templates/anything_att_prop.txt +1 -0
  62. data/test/fixtures/templates/anything_prop.txt +1 -0
  63. data/test/fixtures/templates/anything_prop_att.txt +1 -0
  64. data/test/fixtures/templates/blank.txt +0 -0
  65. data/test/fixtures/templates/child_output.txt +1 -0
  66. data/test/fixtures/templates/kodekopelli_blanks +1 -0
  67. data/test/tc_expandable_properties.rb +106 -0
  68. data/test/tc_file_generator.rb +20 -0
  69. data/test/tc_frozen_key_hash.rb +34 -0
  70. data/test/tc_properties_file.rb +52 -0
  71. data/test/tc_util.rb +55 -0
  72. data/test/ts_all_tests.rb +9 -0
  73. data/test/ts_functional.rb +86 -0
  74. metadata +133 -0
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ *0.8.0* (19 September, 2005)
2
+
3
+ * Initial release into the wild.
data/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ COPYRIGHT AND PERMISSION NOTICE
2
+
3
+ Copyright (c) 2005 Free Range Data LLC
4
+
5
+ All rights reserved.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a
8
+ copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included
16
+ in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ Except as contained in this notice, the name of a copyright holder shall
27
+ not be used in advertising or otherwise to promote the sale, use or other
28
+ dealings in this Software without prior written authorization of the
29
+ copyright holder.
30
+
31
+ Kodekopelli and the Kodekopelli logo are trademarks of
32
+ Free Range Data.
data/README ADDED
@@ -0,0 +1,78 @@
1
+ K
2
+ ODE KO
3
+ PEL LI K
4
+ ODEKOP EL LI KO
5
+ DEK O PE LL
6
+ IK OD EK OP
7
+ ELLIKODE KO PELL IK
8
+ O DEK OPELLIK ODE
9
+ KOPELLIKODEK
10
+ OPELLIKODEK
11
+ OPELLIKODEKOPELLIKOD
12
+ EKOPELLIKODEKOPELLIKOD
13
+ EKOPELLIKODE KOPELLIKOD
14
+ EKOPELLIKODEK OPELLI
15
+ KODEKOPELLIKOD EK
16
+ OPELLIKODEKOPELL IKO
17
+ DEKOPELLIKODEKOPE LLI
18
+ KODEPELLIKODE KOPELLIKODE
19
+ KOPELLIKODEKOPEL LI
20
+ KODEKOPE LLIKODEKOPELLIK
21
+ ODEKOPELL IK
22
+ ODEKOPELL IK
23
+ ODEKOPELLI KOD
24
+ EKOPELLIKODE KO
25
+ PELLIKODEKOPELLIK OD
26
+ EKOPELLIKODEKOPEL
27
+ LIKODEKOPEL LI
28
+ KODEKOP ELLIKOD
29
+ EKOPE LLIKO
30
+ DEK
31
+ OPELL TM
32
+ IKODEKOP
33
+ ELLIKOD
34
+
35
+ Kodekopelli -- making code generation rock since 2003. Do it well.
36
+ Do it once. Let Kodekopelli do the rest.
37
+
38
+ Kodekopelli is an unobtrusive, unassuming code generator that leverages the
39
+ expressiveness of the Ruby language and the ERB lightweight templating system
40
+ to help facilitate the Don't Repeat Yourself (DRY) Principle in a manner
41
+ consistent with agile development practices. That means playing well in an
42
+ environment of continuous integration and utilizing idioms familiar
43
+ (or, at least, intuitive) to users of automated build systems like Ant,
44
+ NAnt and Rake. Kodekopelli provides an additional layer of abstraction
45
+ between the creative thought and real engineering work in which humans
46
+ excel and the drudgery of those tedious tasks better left to computers.
47
+ All too often, said tedium steals valuable time from the lives of developers
48
+ who accept those burdens as badges of honor or as inevitable occupational hazards.
49
+
50
+ == License
51
+
52
+ Kodekopelli is released under the business-friendly MIT license.
53
+
54
+ == _Legalese_
55
+
56
+ The Kodekopelli logo and the Kodekopelli name are trademarks of Free Range Data LLC.
57
+
58
+ == Additional Documentation
59
+
60
+ Additional documentation, including a usage manual, may be found at the
61
+ Kodekopelli site[http://www.kodekopelli.org].
62
+
63
+ == Examples
64
+
65
+ Numerous examples may be found both in the /examples folder
66
+ of this distribution and on the
67
+ Kodekopelli site[http://www.kodekopelli.org].
68
+
69
+ == Support
70
+
71
+ The Kodekopelli site is located at http://www.kodekopelli.org
72
+
73
+ Technical assistance and Kodekopelli enterprise support services
74
+ are available from Free Range Data LLC (http://www.freerangedata.com).
75
+
76
+ Please feel free to submit comments, feature requests, or enhancements.
77
+ If your enhancement is integrated into Kodekopelli, your name will be
78
+ added to the list of contributors on the main site.
data/Rakefile ADDED
@@ -0,0 +1,87 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/contrib/rubyforgepublisher'
8
+
9
+ PKG_NAME = 'kodekopelli'
10
+ PKG_VERSION = ENV['KODEKOPELLI_VERSION'] || '0.8.0'
11
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
12
+ BUILD_DIR = "build"
13
+ TEST_OUTPUT_DIR = "test/output"
14
+ PACKAGE_DIR = "pkg"
15
+
16
+ RELEASE_NAME = "REL #{PKG_VERSION}"
17
+
18
+ RUBY_FORGE_PROJECT = 'kodekopelli'
19
+ RUBY_FORGE_USER = 'hastyb0y'
20
+
21
+ PKG_FILES = FileList[
22
+ "lib/**/*", "test/**/*", "doc/**/*", "examples/**/*", "[A-Z]*", "rakefile"
23
+ ].exclude(/\bCVS\b|-$/)
24
+
25
+ desc "Default Task"
26
+ task :default => [ :clean, :prepare, :test, :rdoc ]
27
+
28
+ desc "Scorch-the-earth removal of all build artifacts."
29
+ task :clean do
30
+ rm_rf BUILD_DIR
31
+ rm_rf TEST_OUTPUT_DIR
32
+ rm_rf PACKAGE_DIR
33
+ end
34
+
35
+ desc "Prepares the blank canvas for build process(es)."
36
+ task :prepare do
37
+ FileUtils.mkdir_p TEST_OUTPUT_DIR
38
+ end
39
+
40
+ # Run the unit and functional tests
41
+ Rake::TestTask.new { |t|
42
+ t.libs << "test"
43
+ t.pattern = 'test/ts_all_tests.rb'
44
+ t.verbose = true
45
+ }
46
+ desc "Run the unit tests."
47
+ task :test => [ :clean, :prepare ] do
48
+ end
49
+
50
+ # Generate the RDoc documentation
51
+ Rake::RDocTask.new { |rdoc|
52
+ rdoc.rdoc_dir = "#{BUILD_DIR}/doc/rdoc"
53
+ rdoc.title = "Kodekopelli"
54
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
55
+ rdoc.rdoc_files.include('README', 'LICENSE', 'CHANGELOG')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ }
58
+
59
+ # Create compressed packages
60
+ spec = Gem::Specification.new do |s|
61
+ s.platform = Gem::Platform::RUBY
62
+ s.name = PKG_NAME
63
+ s.summary = "Making code generation rock since 2003. Do it well. Do it once. Let Kodekopelli do the rest."
64
+ s.version = PKG_VERSION
65
+
66
+ s.author = "Rich Wertz"
67
+ s.email = "rich@kodekopelli.org"
68
+ s.rubyforge_project = RUBY_FORGE_PROJECT
69
+ s.homepage = "http://www.kodekopelli.org"
70
+
71
+ s.has_rdoc = true
72
+ s.extra_rdoc_files = [ "README", "CHANGELOG", "LICENSE" ]
73
+ s.test_file = "test/ts_all_tests.rb"
74
+ s.requirements << 'none'
75
+ s.require_path = "lib"
76
+ s.autorequire = PKG_NAME
77
+ s.files = PKG_FILES
78
+
79
+ s.bindir = "bin"
80
+ s.executables = ["kodekopelli"]
81
+ s.default_executable = "kodekopelli"
82
+ end
83
+
84
+ Rake::GemPackageTask.new(spec) do |pkg|
85
+ # pkg.need_tar = true
86
+ # pkg.need_zip = true
87
+ end
data/bin/kodekopelli ADDED
@@ -0,0 +1,5 @@
1
+ Signal.trap("INT") { puts; exit }
2
+
3
+ require File.dirname(__FILE__) + '/../lib/kodekopelli'
4
+
5
+ Kodekopelli::FileGenerator.new.run(ARGV)
@@ -0,0 +1,40 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kodekopelli>
3
+ <property location="." name="basedir" />
4
+ <property location="${basedir}/templates" name="template.dir" />
5
+ <property location="${basedir}/includes" name="include.dir" />
6
+ <property location="${basedir}/generated" name="output.dir" />
7
+ <property location="${template.dir}/html_wrapper.txt" name="html" />
8
+ <property location="${template.dir}/great_big.txt" name="big" />
9
+ <property location="${template.dir}/faqs.txt" name="faqs" />
10
+ <filegroup>
11
+ <file name="${output.dir}/faqs.html">
12
+ <composite template="${html}">
13
+ <composite template="${faqs}">
14
+ <composite question="Does fuzzy logic tickle?">
15
+ <template>Yes, but only on Tuesdays.</template>
16
+ </composite>
17
+ <composite
18
+ question="If vegetarians eat vegetables, what do humanitarians eat?">
19
+ <template><![CDATA[The world may never know...]]></template>
20
+ </composite>
21
+ <composite question="Is animal shampoo tested on humans?">
22
+ <include file="${include.dir}/shampoo_answer.txt" />
23
+ </composite>
24
+ <composite question="Can someone be a closet claustrophobic?">
25
+ <composite bigs="1" template="${big}">
26
+ <template>No.</template>
27
+ </composite>
28
+ </composite>
29
+ <composite
30
+ question="Can you imagine a world without hypothetical questions?">
31
+ <template>Absolutely</template>
32
+ <template> </template>
33
+ <template>not</template>
34
+ <template>.</template>
35
+ </composite>
36
+ </composite>
37
+ </composite>
38
+ </file>
39
+ </filegroup>
40
+ </kodekopelli>
@@ -0,0 +1,376 @@
1
+ <p class="note">
2
+ <strong>Please Note</strong> This example should be considered a work in progress.
3
+ Although all associated configuration files and templates have been completed and
4
+ are included in this example, not all supporting text has been added.
5
+ </p>
6
+ <p>
7
+ This simple example demonstrates basic Kodekopelli functionality
8
+ and recommended practices. The following Kodekopelli concepts
9
+ not covered by previous examples are illustrated herein:
10
+ </p>
11
+ <ul>
12
+ <li>
13
+ Using <a href="<%= att 'manual' %>#templates">template</a> elements.
14
+ </li>
15
+ <li>
16
+ Using <a href="<%= att 'manual' %>#composites">composite</a> elements.
17
+ </li>
18
+ <li>
19
+ Creating <a href="<%= att 'manual' %>#kodekopellitemplates">Kodekopelli templates</a>
20
+ templates that access the attribute values of child
21
+ elements.
22
+ </li>
23
+ <li>
24
+ Explicitly specifying template text in a Kodekopelli configuration
25
+ file.
26
+ </li>
27
+ </ul>
28
+ <h2>Problem</h2>
29
+ <p>
30
+ Your static web site contains a page dedicated to FAQs, or
31
+ <em>frequently-asked questions</em>. Each time that a new
32
+ question/answer combination is added, you must
33
+ manually add the new question, create a link to the question's
34
+ answer and, possibly, shuffle the order of the questions. Although
35
+ the amount of work required isn't overwhelming, it's tedious--especially
36
+ when questions must be rearranged. Also, you would like to minimize the
37
+ coupling between the actual questions and answers and the presentation
38
+ logic.
39
+ </p>
40
+ <h2>Solution</h2>
41
+ <p>
42
+ Use Kodekopelli to abstract questions and answers away from presentation
43
+ logic, so that any reordering or question additions, modifications or
44
+ deletions may be performed
45
+ within a single Kodekopelli configuration file.
46
+ </p>
47
+ <h3>Project Structure</h3>
48
+ <p>
49
+ Starting from a fresh, empty folder
50
+ of your choice, create subfolders mirroring the following structure:
51
+ </p>
52
+ <pre class="code">
53
+ /generated
54
+ /templates
55
+ /includes
56
+ </pre>
57
+ <p>
58
+ The <em>generated</em> folder will contain those files that Kodekopelli
59
+ generates during processing. The <em>templates</em> folder will contain
60
+ those files that Kodekopelli will interpret as
61
+ <a href="<%= att 'manual' %>#kodekopellitemplates">Kodekopelli templates</a>.
62
+ The <em>includes</em> folder will contain those static files that are available
63
+ to be <a href="<%= att 'manual' %>#includes">included</a>,
64
+ but not interpreted, by Kodekopelli. Please note that this project layout is
65
+ not required for Kodekopelli to function correctly; rather, it is introduced as
66
+ a respectable starting point for your projects.
67
+ </p>
68
+ <h3>The Goal</h3>
69
+ <p>
70
+ This example will demonstrate using Kodekopelli to create the simple
71
+ FAQ list shown below. Questions listed at the top of the list
72
+ will contain links to their corresponding answers that will, conversely,
73
+ contain links back to their questions. FAQ lists structured in this
74
+ manner make it very easy for users to navigate back and forth
75
+ between questions and answers without losing their relative positions
76
+ within the question list.
77
+ </p>
78
+ <div class="example">
79
+ <big>
80
+ <strong>
81
+ Questions</strong></big>
82
+ <br />
83
+ <ol>
84
+ <li id="question_1">
85
+ Does fuzzy logic tickle?
86
+ <a href="#faq_1">
87
+ Answer&nbsp;&#187;
88
+ </a>
89
+ </li>
90
+ <li id="question_2">
91
+ If vegetarians eat vegetables, what do humanitarians eat?
92
+ <a href="#faq_2">
93
+ Answer&nbsp;&#187;
94
+ </a>
95
+ </li>
96
+ <li id="question_3">
97
+ Is animal shampoo tested on humans?
98
+ <a href="#faq_3">
99
+ Answer&nbsp;&#187;
100
+ </a>
101
+ </li>
102
+ <li id="question_4">
103
+ Can someone be a closet claustrophobic?
104
+ <a href="#faq_4">
105
+ Answer&nbsp;&#187;
106
+ </a>
107
+ </li>
108
+ <li id="question_5">
109
+ Can you imagine a world without hypothetical questions?
110
+ <a href="#faq_5">
111
+ Answer&nbsp;&#187;
112
+ </a>
113
+ </li>
114
+ </ol>
115
+ <big>
116
+ <strong>
117
+ Answers</strong></big>
118
+ <br />
119
+ <br />
120
+ <strong id="faq_1">
121
+ 1.&nbsp;Does fuzzy logic tickle?</strong>
122
+ <br />
123
+ Yes, but only on Tuesdays.
124
+ <p>
125
+ <a href="#question_1">
126
+ &#171;&nbsp;Back
127
+ </a>
128
+ </p>
129
+ <strong id="faq_2">
130
+ 2.&nbsp;If vegetarians eat vegetables, what do humanitarians eat?</strong>
131
+ <br />
132
+ The world may never know...
133
+ <p>
134
+ <a href="#question_2">
135
+ &#171;&nbsp;Back
136
+ </a>
137
+ </p>
138
+ <strong id="faq_3">
139
+ 3.&nbsp;Is animal shampoo tested on humans?</strong>
140
+ <br />
141
+ Who told you!?!
142
+ <p>
143
+ <a href="#question_3">
144
+ &#171;&nbsp;Back
145
+ </a>
146
+ </p>
147
+ <strong id="faq_4">
148
+ 4.&nbsp;Can someone be a closet claustrophobic?</strong>
149
+ <br />
150
+ <big>
151
+ <big>
152
+ <big>
153
+ <big>
154
+ No.</big></big></big>
155
+ </big>
156
+ <p>
157
+ <a href="#question_4">
158
+ &#171;&nbsp;Back
159
+ </a>
160
+ </p>
161
+ <strong id="faq_5">
162
+ 5.&nbsp;Can you imagine a world without hypothetical questions?</strong>
163
+ <br />
164
+ Absolutely not.
165
+ <p>
166
+ <a href="#question_5">
167
+ &#171;&nbsp;Back
168
+ </a>
169
+ </p>
170
+ </div>
171
+ <h3>The Configuration File</h3>
172
+ <p>
173
+ Using your favorite text or XML
174
+ editor, create a file called config.xml with the following content at the
175
+ root level of your project folder:
176
+ </p>
177
+ <pre class="code">
178
+ &lt;?xml version="1.0" encoding="UTF-8"?&gt;
179
+ &lt;kodekopelli&gt;
180
+ &lt;/kodekopelli&gt;
181
+ </pre>
182
+ <p>
183
+ Create properties representing the project structure.
184
+ </p>
185
+ <pre class="code">
186
+ &lt;?xml version="1.0" encoding="UTF-8"?&gt;
187
+ <span class="focus">&lt;property location=&quot;.&quot; name=&quot;basedir&quot; /&gt;
188
+ &lt;property location=&quot;${basedir}/templates&quot; name=&quot;template.dir&quot; /&gt;
189
+ &lt;property location=&quot;${basedir}/includes&quot; name=&quot;include.dir&quot; /&gt;
190
+ &lt;property location=&quot;${basedir}/generated&quot; name=&quot;output.dir&quot; /&gt;</span>
191
+ &lt;/kodekopelli&gt;
192
+ </pre>
193
+ <p>
194
+ The configuration file at this point does not contain enough information
195
+ for Kodekopelli to process without error. Before additional configuration directives
196
+ are provided, however, our attention should turn to the templates that will
197
+ be required to create the <em>FAQ-O-Matic</em>. Since the FAQ list
198
+ created in this example will be rendered as an HTML snippet, it would be
199
+ wise to create a wrapper template containing enough markup to house the
200
+ FAQ list in a well-formed HTML document.
201
+ </p>
202
+ <h3>The HTML Wrapper Template</h3>
203
+ <p>
204
+ Using your favorite
205
+ text editor, create a new file containing the following markup.
206
+ </p>
207
+ <pre class="code">
208
+ &lt;html&gt;
209
+ &lt;head&gt;
210
+ &lt;title&gt;&lt;%= h att(&apos;title&apos;) %&gt;&lt;/title&gt;
211
+ &lt;/head&gt;
212
+ &lt;body&gt;
213
+ &lt;h1&gt;&lt;%= h att(&apos;title&apos;) %&gt;&lt;/h1&gt;
214
+ &lt;%= child_output %&gt;
215
+ &lt;/body&gt;
216
+ &lt;/html&gt;
217
+ </pre>
218
+ <p>
219
+ This template creates a minimal HTML wrapper that checks for the presence
220
+ of an attribute named <em>title</em>. If found, the value of the
221
+ <em>title</em> attribute is used for the page title and also
222
+ for the page header. More importantly, the template also includes the
223
+ outcome of all child element processing within the body of the HTML
224
+ document, using the <em>child_output</em> method. Now that the
225
+ template is complete, save it under the <em>templates</em> folder
226
+ using the name <em>html_wrapper.txt</em>.
227
+ </p>
228
+ <p>
229
+ Now add the information for the new template to the configuration file.
230
+ </p>
231
+ <pre class="code">
232
+ &lt;?xml version="1.0" encoding="UTF-8"?&gt;
233
+ &lt;property location=&quot;.&quot; name=&quot;basedir&quot; /&gt;
234
+ &lt;property location=&quot;${basedir}/templates&quot; name=&quot;template.dir&quot; /&gt;
235
+ &lt;property location=&quot;${basedir}/includes&quot; name=&quot;include.dir&quot; /&gt;
236
+ &lt;property location=&quot;${basedir}/generated&quot; name=&quot;output.dir&quot; /&gt;
237
+ <span class="focus">&lt;property location=&quot;${template.dir}/html_wrapper.txt&quot; name=&quot;html&quot; /&gt;</span>
238
+ &lt;/kodekopelli&gt;
239
+ </pre>
240
+
241
+ <h3>The Great Big Text Template</h3>
242
+ <p>
243
+ This template is very simple, as it wraps a variable number of
244
+ &lt;big&gt;...&lt;/big&gt; elements around all child element output.
245
+ The number of <em>big</em> wrappers added corresponds
246
+ to the value of the <em>bigs</em> attribute provided. If no value is
247
+ provided for the <em>bigs</em> attribute, a default value of '1' is
248
+ used. Save this template under the <em>templates</em> folder using the name
249
+ <em>great_big.txt</em>.
250
+ </p>
251
+ <pre class="code">
252
+ &lt;% att(&apos;bigs&apos;, 1).to_i.times {%&gt;&lt;big&gt;&lt;%} %&gt;
253
+ &lt;%= child_output %&gt;
254
+ &lt;% att(&apos;bigs&apos;, 1).to_i.times {%&gt;&lt;/big&gt;&lt;%} %&gt;
255
+ </pre>
256
+ <p>
257
+ Add the information for the <em>great big text</em> template to the configuration file.
258
+ </p>
259
+ <pre class="code">
260
+ &lt;?xml version="1.0" encoding="UTF-8"?&gt;
261
+ &lt;property location=&quot;.&quot; name=&quot;basedir&quot; /&gt;
262
+ &lt;property location=&quot;${basedir}/templates&quot; name=&quot;template.dir&quot; /&gt;
263
+ &lt;property location=&quot;${basedir}/includes&quot; name=&quot;include.dir&quot; /&gt;
264
+ &lt;property location=&quot;${basedir}/generated&quot; name=&quot;output.dir&quot; /&gt;
265
+ &lt;property location=&quot;${template.dir}/html_wrapper.txt&quot; name=&quot;html&quot; /&gt;
266
+ <span class="focus">&lt;property location=&quot;${template.dir}/great_big.txt&quot; name=&quot;big&quot; /&gt;</span>
267
+ &lt;/kodekopelli&gt;
268
+ </pre>
269
+ <h3>The FAQs Template</h3>
270
+ <p>
271
+ The FAQs template is a <em>composite</em> template responsible for building
272
+ the actual FAQ list.
273
+ </p>
274
+
275
+ <pre class="code">
276
+ &lt;big&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/big&gt;&lt;br /&gt;
277
+ &lt;ol&gt;
278
+ &lt;% question_counter = 1 %&gt;
279
+ &lt;% child_iterator { |current_child, text| %&gt;
280
+ &lt;li id="question_&lt;%= question_counter.to_s %&gt;"&gt;
281
+ &lt;%= node_att(current_child, 'question') %&gt;
282
+ &lt;a href="&lt;%= "#faq_#{question_counter.to_s}" %&gt;"&gt;Answer&nbsp;&#187;&lt;/a&gt;
283
+ &lt;/li&gt;
284
+ &lt;% question_counter += 1 %&gt;
285
+ &lt;% } %&gt;
286
+ &lt;/ol&gt;
287
+ &lt;big&gt;&lt;strong&gt;Answers&lt;/strong&gt;&lt;/big&gt;&lt;br /&gt;&lt;br /&gt;
288
+ &lt;% answer_counter = 1 %&gt;
289
+ &lt;% child_iterator { |current_child, text| %&gt;
290
+ &lt;strong id="faq_&lt;%= answer_counter.to_s %&gt;"&gt;
291
+ &lt;%= answer_counter.to_s %&gt;.&nbsp;&lt;%= node_att(current_child,'question') %&gt;&lt;/strong&gt;&lt;br /&gt;
292
+ &lt;%= text %&gt;
293
+ &lt;p&gt;
294
+ &lt;a href="#question_&lt;%= answer_counter.to_s %&gt;"&gt;&#171;&nbsp;Back&lt;/a&gt;
295
+ &lt;/p&gt;
296
+ &lt;% answer_counter += 1 %&gt;
297
+ &lt;% } %&gt;
298
+ </pre>
299
+ <p>
300
+ Add the information for the final template to the configuration file.
301
+ </p>
302
+ <pre class="code">
303
+ &lt;?xml version="1.0" encoding="UTF-8"?&gt;
304
+ &lt;property location=&quot;.&quot; name=&quot;basedir&quot; /&gt;
305
+ &lt;property location=&quot;${basedir}/templates&quot; name=&quot;template.dir&quot; /&gt;
306
+ &lt;property location=&quot;${basedir}/includes&quot; name=&quot;include.dir&quot; /&gt;
307
+ &lt;property location=&quot;${basedir}/generated&quot; name=&quot;output.dir&quot; /&gt;
308
+ &lt;property location=&quot;${template.dir}/html_wrapper.txt&quot; name=&quot;html&quot; /&gt;
309
+ &lt;property location=&quot;${template.dir}/great_big.txt&quot; name=&quot;big&quot; /&gt;
310
+ <span class="focus">&lt;property location=&quot;${template.dir}/faqs.txt&quot; name=&quot;faqs&quot; /&gt;</span>
311
+ &lt;/kodekopelli&gt;
312
+ </pre>
313
+ <h3>Back to the Configuration File</h3>
314
+ <pre class="code">
315
+ &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
316
+ &lt;kodekopelli&gt;
317
+ &lt;property location=&quot;.&quot; name=&quot;basedir&quot; /&gt;
318
+ &lt;property location=&quot;${basedir}/templates&quot; name=&quot;template.dir&quot; /&gt;
319
+ &lt;property location=&quot;${basedir}/includes&quot; name=&quot;include.dir&quot; /&gt;
320
+ &lt;property location=&quot;${basedir}/generated&quot; name=&quot;output.dir&quot; /&gt;
321
+ &lt;property location=&quot;${template.dir}/html_wrapper.txt&quot; name=&quot;html&quot; /&gt;
322
+ &lt;property location=&quot;${template.dir}/great_big.txt&quot; name=&quot;big&quot; /&gt;
323
+ &lt;property location=&quot;${template.dir}/faqs.txt&quot; name=&quot;faqs&quot; /&gt;
324
+ &lt;filegroup&gt;
325
+ &lt;file name=&quot;${output.dir}/faqs.html&quot;&gt;
326
+ &lt;composite template=&quot;${html}&quot;&gt;
327
+ &lt;composite template=&quot;${faqs}&quot;&gt;
328
+ &lt;composite question=&quot;Does fuzzy logic tickle?&quot;&gt;
329
+ &lt;template&gt;Yes, but only on Tuesdays.&lt;/template&gt;
330
+ &lt;/composite&gt;
331
+ &lt;composite
332
+ question=&quot;If vegetarians eat vegetables, what do humanitarians eat?&quot;&gt;
333
+ &lt;template&gt;&lt;![CDATA[The world may never know...]]&gt;&lt;/template&gt;
334
+ &lt;/composite&gt;
335
+ &lt;composite question=&quot;Is animal shampoo tested on humans?&quot;&gt;
336
+ &lt;include file=&quot;${include.dir}/shampoo_answer.txt&quot; /&gt;
337
+ &lt;/composite&gt;
338
+ &lt;composite question=&quot;Can someone be a closet claustrophobic?&quot;&gt;
339
+ &lt;composite bigs=&quot;1&quot; template=&quot;${big}&quot;&gt;
340
+ &lt;template&gt;No.&lt;/template&gt;
341
+ &lt;/composite&gt;
342
+ &lt;/composite&gt;
343
+ &lt;composite
344
+ question=&quot;Can you imagine a world without hypothetical questions?&quot;&gt;
345
+ &lt;template&gt;Absolutely&lt;/template&gt;
346
+ &lt;template&gt; &lt;/template&gt;
347
+ &lt;template&gt;not&lt;/template&gt;
348
+ &lt;template&gt;.&lt;/template&gt;
349
+ &lt;/composite&gt;
350
+ &lt;/composite&gt;
351
+ &lt;/composite&gt;
352
+ &lt;/file&gt;
353
+ &lt;/filegroup&gt;
354
+ &lt;/kodekopelli&gt;
355
+ </pre>
356
+ <p>
357
+ The configuration file is complete and is now ready for processing by Kodekopelli.
358
+ From the top level of your project folder, invoke Kodekopelli from the command line:
359
+ </p>
360
+ <pre class="code">
361
+ % <span class="focus">kodekopelli config.xml</span>
362
+ </pre>
363
+ <p>
364
+ After Kodekopelli has finished processing the file, you may view the FAQ list
365
+ within the context of the HTML page located in the project's
366
+ <em>generated</em> folder.
367
+ </p>
368
+ <h2>Notes</h2>
369
+ <ul>
370
+ <li>
371
+ The template and composite level elements used to create the answers
372
+ for the FAQ questions above have been made intentionally diverse so that
373
+ the reader may be aware that there are numerous solutions to the same
374
+ problem when using Kodekopelli.
375
+ </li>
376
+ </ul>