kodekopelli 0.8.0

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.
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>