manager 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHART.html +1270 -0
  3. data/MANUAL.html +1252 -0
  4. data/bin/manager +43 -0
  5. data/examples/array/CHART.html +1376 -0
  6. data/examples/array/MANUAL.html +1126 -0
  7. data/examples/array/spec +3438 -0
  8. data/lib/manager.rb +528 -0
  9. data/lib/manager/annotation +96 -0
  10. data/lib/manager/input +189 -0
  11. data/lib/manager/js +257 -0
  12. data/lib/manager/refine_module +142 -0
  13. data/lib/manager/refine_object_mapping +143 -0
  14. data/lib/manager/refine_test +97 -0
  15. data/lib/manager/render +1228 -0
  16. data/lib/manager/spell_check +49 -0
  17. data/lib/manager/test +404 -0
  18. data/lib/manager/test_helper +9 -0
  19. data/license +9 -0
  20. data/manager.gemspec +21 -0
  21. data/spec/alternatives_implemented.png +0 -0
  22. data/spec/alternatives_unimplemented.png +0 -0
  23. data/spec/annotations.png +0 -0
  24. data/spec/benchmark_test.png +0 -0
  25. data/spec/citation.png +0 -0
  26. data/spec/code_block.png +0 -0
  27. data/spec/context_module.png +0 -0
  28. data/spec/documentation +1289 -0
  29. data/spec/external_link.png +0 -0
  30. data/spec/image.png +0 -0
  31. data/spec/list.png +0 -0
  32. data/spec/long.png +0 -0
  33. data/spec/main_and_object.png +0 -0
  34. data/spec/markup.png +0 -0
  35. data/spec/module_diagram.png +0 -0
  36. data/spec/navigation.png +0 -0
  37. data/spec/nested_section_headers.png +0 -0
  38. data/spec/ruby.png +0 -0
  39. data/spec/setup_teardown.png +0 -0
  40. data/spec/short.png +0 -0
  41. data/spec/signature.png +0 -0
  42. data/spec/spec +76 -0
  43. data/spec/spec_example.png +0 -0
  44. data/spec/table.png +0 -0
  45. data/spec/test_header.png +0 -0
  46. data/spec/test_non_unit_spec +184 -0
  47. data/spec/test_program +71 -0
  48. data/spec/test_unit_spec +790 -0
  49. data/spec/tutorial_1.png +0 -0
  50. data/spec/tutorial_2.png +0 -0
  51. data/spec/tutorial_3.png +0 -0
  52. data/spec/tutorial_4.png +0 -0
  53. data/spec/tutorial_5.png +0 -0
  54. data/spec/tutorial_6.png +0 -0
  55. data/spec/tutorial_7.png +0 -0
  56. data/spec/tutorial_8.png +0 -0
  57. data/spec/unambiguous_links.png +0 -0
  58. data/spec/unit_test_failure.png +0 -0
  59. data/spec/unit_test_raise.png +0 -0
  60. data/spec/unit_test_receiver.png +0 -0
  61. data/spec/unit_test_succeed.png +0 -0
  62. data/spec/unit_test_success.png +0 -0
  63. data/spec/unit_test_throw.png +0 -0
  64. data/spec/valid_heading.png +0 -0
  65. data/spec/with_expr.png +0 -0
  66. data/spec/without_expr.png +0 -0
  67. data/theme/2016a.css +670 -0
  68. data/theme/coderay_github.css +132 -0
  69. metadata +140 -11
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2016 sawa
4
+
5
+ class Object
6
+ def in? other
7
+ other.include?(self)
8
+ end
9
+ end
data/license ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014--2016 sawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do
2
+ |s|
3
+ s.name = "manager"
4
+ s.version = "0.1.0"
5
+ s.date = "2016-04-04"
6
+ s.authors = ["sawa"]
7
+ s.email = []
8
+ s.license = "MIT"
9
+ s.summary = "Documentation and test framework for software written in Ruby"
10
+ s.description = "Manager generates a user's manual and a developer's chart simultaneously from a single spec file that contains both kinds of information. More precisely, it is a document generator, source code annotation extracter, source code analyzer, class diagram generator, unit test framework, benchmark measurer for alternative implementations of a feature, all in one."
11
+ s.homepage = "http://sawa.github.io/manager"
12
+ s.executables << "manager"
13
+ s.files = Dir["*"] - Dir["*.gem"] + Dir["lib/**/*"] + Dir["theme/**/*"] + Dir["examples/**/*"] + Dir["spec/**/*"]
14
+ s.platform = Gem::Platform::RUBY
15
+ s.required_ruby_version = ">= 2.3.1"
16
+ s.add_runtime_dependency "benchmark-ips", ">= 0"
17
+ s.add_runtime_dependency "dom", ">= 1.0.0"
18
+ s.add_runtime_dependency "coderay", ">= 0"
19
+ s.add_development_dependency "manager", ">= 0"
20
+ s.requirements << "ffi-aspell gem (optional), a web browser compatible with HTML5 and CSS3"
21
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,1289 @@
1
+ #! frozen_string_literal: false
2
+
3
+ spec "=License",
4
+ "The MIT License (MIT)",
5
+ "Copyright (c) 2014–2016 sawa",
6
+ "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:",
7
+ "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.",
8
+ "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
9
+ coda
10
+
11
+ spec "=Overview",
12
+ "Manager generates a user's manual and a developer's chart simultaneously from a single spec file that contains both kinds of information. More precisely, it is a document generator, annotation extractor, program code analyzer, unit test framework, benchmark measure, all in one.",
13
+ "Manager combines software documentation and software test in a single process. The leading idea is that documentation and test should be closely related to each other and be separated from program coding process in order to put Documentation Driven Development (DDD) and Test Driven Development (TDD) into practice. Documentation and tests describe a piece of software from the designing side whereas the program code describes it from the implementation side. Manager allows a programmer to describe the software from each perspective in a separate file.",
14
+ "Separating documentation and tests on the one hand and the program code on the other is beneficial to both. Majority of conventional documentation generation tools require documentation to interleave with the program code within the same file, and in order to avoid affecting the program code, documentation is embedded within comments, and is written in a markup language especially designed for the documentation generator. This requires the developer to learn additional syntax. Also with most text editors, documentation embedded inside a comment cannot be highlighted for the markup language, making it difficult for the programmer to write or read. Writing or reading the program code is also disrupted by the noise of documentation. In contrast, Manager requires documentation to be written in a separate file from the program code and in the Ruby language. With the files separated, both the documentation and the program code are kept clean.",
15
+ "An often heard argumentation against separating documentation from the program code is that it would easily break synchronization of the two as a programmer may update one without updating the other (either forgetting to do so or by laziness), and that having both written in the same file alongside each other would lower the mental barrier against updating both at the same time. Manager features functionality to warn the developer about discrepancies between the documentation and the program code, motivating the developer to fix them.",
16
+ "Regarding tests, a major advantage of Manager as opposed to most conventional Ruby test frameworks is that Manager does not use matchers: methods especially designed for unit testing. Using Manager, unit tests and benchmarks are written naturally as if one were writing conditional expressions in Ruby code.",
17
+ "Tests in Manager also uses placeholder methods for a constant or a method that is the object of the test. This enforces tests to follow the proper format; each unit testing expression would be forced to have a receiver that belongs to an appropriate class, and the examined feature will be called once automatically, whose result will be verified once per unit testing expression. There is no way to test a constant or a method other than what is supposed to be tested where the test is written. Any testing expressions that do not follow the format will be reported as failure. It is highly unlikely for an inappropriately written unit test to succeed by accident (if it does, it is Manager's bug).",
18
+ coda
19
+
20
+ spec "==Simple Example",
21
+ "Suppose we have the following problem:",
22
+ "> Write an instance method `same_counts` on `Array` that takes another array as an argument, and returns `true` if `self` and the other array have the composition of elements (i.e. counts of all elements are the same) and otherwise returns `false`.",
23
+ "Let us start by writing a test. Create a new file named `test.rb`, and write the following line at the top:",
24
+ <<~'RUBY'.code,
25
+ # frozen_string_literal: false
26
+ RUBY
27
+ "Under it, we shall describe a specification for the instance method `same_counts` on `Array`. We write the following:",
28
+ <<~'RUBY'.code,
29
+ class Array
30
+ spec "#same_counts",
31
+ coda
32
+ end
33
+ RUBY
34
+ "Now we are ready to fill in some unit tests inside the `spec` ... `coda`. The first test may be a trivial case:",
35
+ <<~'RUBY'.code,
36
+ ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true,
37
+ RUBY
38
+ "Here, `UT` is a placeholder for the object of the test (`same_counts`), and the whole expression describes what we expect to be evaluated to be a truthy value. Don't forget the comma at the end. Next, we might want to add an example with a shuffled argument:",
39
+ <<~'RUBY'.code,
40
+ ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true,
41
+ RUBY
42
+ "To make sure that not only the inventory of the elements but also the number of them matters, we can a test like this:",
43
+ <<~'RUBY'.code,
44
+ ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false,
45
+ RUBY
46
+ "Now, the file `test.rb` should look like:",
47
+ <<~'RUBY'.code,
48
+ # frozen_string_literal: false
49
+
50
+ class Array
51
+ spec "#same_counts",
52
+ ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true,
53
+ ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true,
54
+ ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false,
55
+ coda
56
+ end
57
+ RUBY
58
+ "Let us run this on Manager. On the command line, move to the directory we wrote the `test.rb` file, and type:",
59
+ <<~'BASH'.code(:bash),
60
+ manager test.rb
61
+ BASH
62
+ "Two files named `MANUAL.html` and `CHART.html` should have been generated in the directory. Open `CHART.html` with a web browser. It should have a section that looks like this:",
63
+ image("Simple example step 1", "spec/tutorial_1.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
64
+ "From this, we can read that we wrote a specification for an instance method `same_counts` in the `Array` class. It is tagged as “Unimplemented” because we have not defined the method yet. It has three tests, all of which are shown with a numbered colored bullet and with a message saying that the method is not implemented.",
65
+ "Let us go on and implement the method. The simplest way is to write it in the same file we are working on. At the bottom of what we have, write a line:",
66
+ <<~'RUBY'.code,
67
+ __END__
68
+ RUBY
69
+ "This will be the border between the specification code and the program code that we are about to write. Under this line, let us add an implementation of the method. The file `test.rb` should now look like this:",
70
+ <<~'RUBY'.code,
71
+ # frozen_string_literal: false
72
+
73
+ class Array
74
+ spec "#same_counts",
75
+ ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true,
76
+ ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true,
77
+ ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false,
78
+ coda
79
+ end
80
+
81
+ __END__
82
+
83
+ class Array
84
+ def same_counts other
85
+ sort == other.sort
86
+ end
87
+ end
88
+ RUBY
89
+ "After we run the same command from the terminal, a portion of `CHART.html` should have now changed to this:",
90
+ image("Simple example step 2", "spec/tutorial_2.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
91
+ "The “Unimplemented” tag is gone, and the method is shown with a bullet that it is defined in `test.rb:14`. The three tests have succeeded.",
92
+ "Suppose we came up with another implementation of this method. If we name it with a prefix `same_counts__`, then it will be automatically recognized by Manager as an alternative implementation of the method `same_counts`. The program code can now look like:",
93
+ <<~'RUBY'.code,
94
+ class Array
95
+ def same_counts other
96
+ sort == other.sort
97
+ end
98
+ def same_counts__using_hash other
99
+ inject(Hash.new(0)){|h, e| h[e] += 1} == other.inject(Hash.new(0)){|h, e| h[e] += 1}
100
+ end
101
+ end
102
+ RUBY
103
+ "Let us run the command again, and see what happened.",
104
+ image("Simple example step 3", "spec/tutorial_3.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
105
+ "Oops, it looks like I forgot to add the memo object in the block of the definition. We can tell this from the backtrace displayed in the report. Let us fix this, and also implement another version:",
106
+ <<~'RUBY'.code,
107
+ class Array
108
+ def same_counts other
109
+ sort == other.sort
110
+ end
111
+ def same_counts__using_hash other
112
+ inject(Hash.new(0)){|h, e| h[e] += 1; h} == other.inject(Hash.new(0)){|h, e| h[e] += 1; h}
113
+ end
114
+ def same_counts__using_group_by other
115
+ group_by(&:itself) == other.group_by(&:itself)
116
+ end
117
+ end
118
+ RUBY
119
+ "And then we run Manager again:",
120
+ image("Simple example step 4", "spec/tutorial_4.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
121
+ "All three implementations have passed the tests given. Now, we might be interested in comparing the performance of these three implementations. That is done by using the benchmark method `BM`. Using the same objects that we used for the unit tests, We should add some lines to the spec portion of the code to make it look like:",
122
+ <<~'RUBY'.code,
123
+ class Array
124
+ spec "#same_counts",
125
+ ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true,
126
+ ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true,
127
+ ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false,
128
+ ["a", "b", "b", "c", "c", "c"].BM(["a", "b", "b", "c", "c", "c"]),
129
+ ["a", "b", "b", "c", "c", "c"].BM(["c", "b", "c", "b", "c", "a"]),
130
+ ["a", "b", "c"].BM(["a", "b", "b", "c", "c", "c"]),
131
+ coda
132
+ end
133
+ RUBY
134
+ "Running Manger will take more time because of the benchmark tests performed. Now we see benchmark reports under the unit test reports:",
135
+ image("Simple example step 5", "spec/tutorial_5.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
136
+ "In all test cases, it looks like the first implementation `same_counts` is the most efficient. If this were production code, we might want to decide to use this implementation.",
137
+ "So far, we have been ignoring the tag “Missing Doc” with a message. We have been looking at `CHART.html`, which should be used by the developer of the program, but opening the other file `MANUAL.html`,",
138
+ image("Simple example step 6", "spec/tutorial_6.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
139
+ "we see that its content is nearly blank. This file is a manual for the users of the program. The message in `CHART.html` was warning that the this part of `MANUAL.html` does not have any descriptive content. Manager encourages a developer to properly do testing as well as documentation. When something is missing, it alerts the developer with such tags. So we can add a description to the spec portion:",
140
+ <<~'RUBY'.code,
141
+ class Array
142
+ spec "#same_counts",
143
+ "Takes another array, and compares it with `self` whether the counts of all
144
+ elements are the same.",
145
+ ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true,
146
+ ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true,
147
+ ...
148
+ coda
149
+ end
150
+ RUBY
151
+ "The description then appears in `CHART.html` as well as in `MANUAL.html`, but we now have a different tag alerting that no method signature is given:",
152
+ image("Simple example step 7", "spec/tutorial_7.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
153
+ "A method signature is expressed as a hash. We should add a hash like this before the description we have just added:",
154
+ <<~'RUBY'.code,
155
+ spec "#same_counts",
156
+ {"(other)" => value(true) | value(false)},
157
+ "Takes another array, and compares it with `self` whether the counts of all
158
+ elements are the same.",
159
+ ...
160
+ RUBY
161
+ "After running Manager, we can see that the warning is gone from `CHART.html`, and for `MANUAL.html`, by clicking the header “Array#same_counts”, we can see that we now have documentation for the user:",
162
+ image("Simple example step 8", "spec/tutorial_8.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
163
+ "For more examples, please see the spec files of the following gems: manager, dom, pretty_debug, compact_time, and the spec files under the directory `manager/examples`.",
164
+ coda
165
+
166
+ spec "==Release Notes",
167
+ "Manager follows semantic versioning, i.e., the first number section of a version (major) corresponds to backwards incompatible API changes, the second number (minor) describes backwards compatible API changes, such as new functionality/features, and the third number (teeny) describes implementation level detail changes, such as small bug fixes.",
168
+ "* 0.0.0",
169
+ "** Initial release.",
170
+ coda
171
+
172
+ spec "=Viewing The Files",
173
+ "Manager generates two HTML format files: a *user's manual* and a *developer's chart*. They are to be viewed on a web browser.",
174
+ coda
175
+
176
+ spec "==User's Manual",
177
+ "User's manual includes *documentation* about described or implemented modules and features. It is intended to be read by the users of the software.",
178
+ coda
179
+
180
+ spec "===Main Information",
181
+ "The *main information* is displayed in three levels:",
182
+ "* *Modules* (including classes)",
183
+ "* *Features* (i.e., constants, singleton methods, instance methods, see {=Feature}) and *section headers* (see {=Section Header})",
184
+ "* *Items* (contents)",
185
+ "Features described in the spec as well as features implemented within the listed files (see {=Reference to Program Code}) will be displayed by modules. Features in the spec file will be placed first and in the order they are described in the spec. However, if the module bodies of a single module (which include feature descriptions) are scattered across multiple locations in the spec file, then they are merged together in the first location. Features implemented but not described in the spec will follow the described ones within their owner modules. They will be described with their original name in case of an alias method.",
186
+ "Modules, features, and description sections are clickable unless their content is empty. Each one toggles elements that are more detailed than itself between hidden/displayed states. Each feature has a *visibility* tag (valued “*Unimplemented*”, “*protected*”, “*private*”, or “*public*”) and a numbered *type* tag (distinguished by “*Constant*”, “*Singleton method*”, or “*Instance method*”) except for constants that are modules.",
187
+ "Modules (including classes) within the name space of a module other than `Object` are only displayed under the (constant) feature section with a link to where they are displayed as an independent module section. The `Object` class is displayed only if it has a feature described in the spec file or if a non-module feature of `Object` is implemented in the program code.",
188
+ coda
189
+
190
+ spec "===Top Bar",
191
+ "At the top of the user's manual is the *top bar*, which has buttons and information to help users navigate through the file.",
192
+ "* “Modules”, “Features”, and “Full” buttons: Reset the level of detail to be displayed. “Module” button only displays the module names. “Feature” button displays up to the inventory of features as well as the headers of description sections. “Full” button displays to the full detail. Features and items can be further hidden/displayed independently or in groups by other buttons, as to be explained below; the three buttons are only responsible for the level of detail right after they are clicked.",
193
+ "* Visibility buttons: Each button toggles between showing/hiding states of the features with the corresponding visibility. The digits beside each button represent the number of features with the visibility, and remain constant even if any of them become hidden. Buttons with numbers are omitted if the number of features is zero.",
194
+ "* Type buttons: Each button toggles between showing/hiding states of the features of the corresponding type. The digits beside each button represent the number of features of the type, and remain constant even if any of them become hidden. Buttons with numbers are omitted if the number of features is zero.",
195
+ "The *left/right arrows* on the right of a group of buttons allow navigation through features. The numerator on the right side describes the position that was last navigated using the arrow buttons (or “1” by default). If the last navigation was done using the associated arrow buttons, then the number is displayed with emphasis. The denominator describes the number of displayed (i.e. not hidden) features. The number following the plus sign tells the user how many features they are implicitly skipping during navigation; it indicates the number of features hidden for reasons other than any property in the group being hidden. For example, the following situation:",
196
+ image("Navigation", "spec/navigation.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
197
+ "shows that the arrow keys next to the “Unimplemented”/“protected”/“private”/“public” group were the ones last clicked for navigation, and that the current scroll position points to the third feature among the 140 visible ones (features that are not private and not constant) unless the user has further scrolled without using any navigation buttons. As the sum of 11 (Unimplemented), 10 (protected), and 126 (public) is 147, 147 features would be expected to be displayed if nothing but private features were hidden. However, the features are hidden not only because they are private. Since constants are hidden, Unimplemented/protected/public features that are constants are hidden. The number seven after the plus sign in the first group indicates this amount. Similarly, 58 is the number of singleton/instance methods hidden because they are private.",
198
+ coda
199
+
200
+ spec "==Developer's Chart",
201
+ "The contents of the developer's chart is a superset of that of the user's manual. It includes annotations, test reports, and warnings. It is intended to be utilized by the developers of the software.",
202
+ coda
203
+
204
+ spec "===Left Panel",
205
+ "“Gem Spec” displays information given in a gem spec file if the object of Manager is a gem, and a {<<::#gemspec} command is called in the spec (see below, {=Reference to Program Code}).",
206
+ "The *base directory* is the directory in which the spec file resides. Throughout Manager, file names are displayed either as a path relative to the base directory or as an absolute path, whichever is shorter.",
207
+ "The “Files” table lists information about the relevant files.",
208
+ "* “Category”: Files are classified into the following.",
209
+ "** “Depended”: Pieces of software that are loaded during Manager run, but are neither the subject nor the object of documentation or test. In particular, the Ruby implementation that is used to run Manager, and any gems that are loaded.",
210
+ "** “Specification”: The subject of description/tests. Also called *spec* in this manual/chart.",
211
+ "** “Program”: Files that are the object of analysis/tests. They are described with the percentage of the number of lines covered in tests against the number of all lines in the file.",
212
+ # "* “Listed”: (**Not implemented yet**) The checkbox toggles the file's *listed* status, i.e. whether they are hidden or displayed in backtraces of test reports.",
213
+ "* “Version”: Version is described by the version number for depended files and by the last modified time for specification and program code files. Among the last modified times, the latest one is displayed with emphasis. This may give a hint on which file has mainly undergone modification right before the Manager was run, so that the developer can focus on reading the relevant contents.",
214
+ coda
215
+
216
+ spec "===Main Information",
217
+ "Features in the developer's chart, unlike those in the user's manual, may be tagged by one of the followings that describe *documentation status*. They are exclusive (i.e. no feature may be tagged by more than one of them), and are not complementary (i.e. a feature may be tagged by none of them). Among them, “Undocumented” and “Misplaced” are warnings, and “hidden” or “moved” indicate markings in the spec file (see {=Feature}).",
218
+ "* “Undocumented”: The feature is implemented in the program code, but is not described in the spec file.",
219
+ "* “hidden”: The feature is marked in the spec file as to be hidden in the user's manual. This is intended to be used for features that are internal implementations, and are not meant to be public API.",
220
+ "* “Misplaced”: The feature is under one or more of the following three occasions. This tag is suppressed when the feature is marked as “moved”.",
221
+ "** The feature (method) is implemented within a file that is not listed (see {=Reference to Program Code} on how to list a feature).",
222
+ "** The feature is implemented on an ancestor module rather than in the module in which it is described in the spec file.",
223
+ "** The feature (method) is described in the spec file, but is implemented as an alias of another method.",
224
+ "* “moved”: The feature is marked as to be described in a manner different from its implementation. Suppresses “Misplaced” tag.",
225
+ coda
226
+
227
+ spec "====Module Diagrams",
228
+ "Each module section has two *module diagrams*. For example, the following may appear under the section of a class `B`:",
229
+ image("Module diagrams", "spec/module_diagram.png", border: "1px solid hsl(0, 0%, 80%)"),
230
+ "The first diagram represents mixins and ancestors related to `B` itself, and the second one is for its singleton class `<<B`. Each bordered area includes exactly one class and zero or more modules it has mixin relations with. The classes are vertically aligned at the same height. A mixin relation is expressed with the character `\u250a`; modules located below the class are the prepended ones, those located above are the included modules. They are ordered so that a lower module/class has priority over a higher one in method call. Thus, the diagram tells that module `C` was prepended to class `B`, and module `D` was included into `B`, after which module `E` was included into `B`. The bordered areas are related with the character `<`, which expresses a minimum subclass relation between the respective classes in the bordered areas. The diagram tells that class `B` has class `A` as its parent. Note that the entire method search path (priority) can be followed by going from the bottom to top, left to right: `C` \u2192 `B` \u2192 `E` \u2192 `D` \u2192 `A` \u2192 `Object` \u2192 `Kernel` \u2192 `BasicObject`. Links are available for navigation to the module sections that are displayed.",
231
+ coda
232
+
233
+ spec "====Alternative Implementations",
234
+ "The developer's chart displays information about *alternative implementations*. This functionality is intended to help developers test and compare different implementations of a feature by following a convention. Singleton or instance methods implemented in the program code and whose name includes exactly one occurrence of the sequence `__` will be considered alternative implementations of the *main implementation*, whose name corresponds to the sequence preceding `__`. The (possibly empty) sequence following `__` is intended to be used as a label for distinguishing each alternative; perhaps a serial number, a few-word summary of the implementation detail, the developer's name, or implementation date can be used. For example, the following methods are all considered alternatives of the main implementation `foo`:",
235
+ <<~'RUBY'.code,
236
+ foo__
237
+ foo__1
238
+ foo__2
239
+ foo__map
240
+ foo__without_map
241
+ foo__mary
242
+ foo__john
243
+ foo__20151201
244
+ foo__20160101
245
+ RUBY
246
+ "To avoid conflict or confusion with (mostly Ruby core) methods or keywords with balanced `__` (such as `__FILE__` or `__send__`), a method that has more than one `__` (such as `foo__map__1`) is not considered an alternative implementation.",
247
+ "Alternative (as well as main) implementations are detected and displayed together after bullets with the title “Implementation candidates” under the feature named for the main implementation. As such, an alternative implementation cannot be described in the spec file as an independent feature. It is possible that alternatives are implemented without the main one being implemented. In such case, the feature header will have the visibility “unimplemented”. Each alternative will have the visibility described within parentheses if the value differs from the main implementation. In this example,",
248
+ image("Alternatives with unimplemented main", "spec/alternatives_unimplemented.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
249
+ "instance methods `A#foo__1` and `A#foo__2` are implemented in the ;program code as private and public methods respectively, but the corresponding main method `A#foo` is not implemented. So the feature header has a tag “unimplemented”. In this example,",
250
+ image("Alternatives with implemented main", "spec/alternatives_implemented.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
251
+ "three instance methods `A#bar`, `A#bar__1`, and `A#bar__2` are implemented in the program code. The “public” tag indicates that the main implementation `A#bar` is public. `A#bar__1` is implemented as a protected method, and the absence of visibility description for `A#bar__2` indicates that it is the same as the main implementation, i.e. public.",
252
+ "In order for alternative implementations to pursue their purpose, their visibility should match that of the main implementation (if implemented). Thus, visibility description in parentheses is indication that the implementation is wrong; the description is intended to work as a warning to the developer.",
253
+ "On the right side, the source location (i.e. file name and line number) of each implementation is described if it is detectable.",
254
+ "In one use of this functionality, a main method has already been implemented in a program code file, and the developer wants to test its alternative implementations that appeared afterwards. In such case, it may be better to implement the alternatives in a file different from the main program file or in the scratch code (see {=Reference to Program Code}) in order not to mess the original code file.",
255
+ coda
256
+
257
+ spec "====Documentation Reports",
258
+ "Items in the developer's chart is marked with various *warnings*. Appropriate fix should be made when a warning is displayed.",
259
+ "* “Missing”: There are items that a feature described in the spec file should minimally have. If any of the following (see {=Spec File}) is missing in a feature documented in the spec file, this tag is displayed. These warnings are suppressed when the feature is marked as “hidden” or “moved”.",
260
+ "** The feature does not include any unit test.",
261
+ "** The feature does not have any paragraph for the user.",
262
+ "** The feature (method) has no description about its method signature.",
263
+ "* Spell errors are shown in distinct type faces (If `spell_check` option is on, see {=Spell Check (spell_check, spell-check-list, spell-check-filter, case_sensitive, case_insensitive)}).",
264
+ "* Bad links (see {=Link}) within annotation, test description, list item, citation, or paragraph are shown in distinct type faces. This is also shown in user's manual.",
265
+ coda
266
+
267
+ spec "=====Annotations and Editorial Markings",
268
+ "Whereas documentation describes the Application Programming Interface (API), it is sometimes helpful to have information on the implementation level details of the software. Manager detects *annotations* in the spec file and the program code. It also detects comments in the program code that follow a certain format (traces of temporal edits) and regards them as *editorial markings*. While documentation should appear in the user's manual, annotations and editorial markings should be hidden from the users and only be displayed in the developer's chart.",
269
+ "Among annotations and editorial markings, there are two types:",
270
+ "* *Agenda*: Those that should be kept temporarily and should be aimed to be resolved after some time, such as markings on incomplete or buggy parts of the program code, or commented out parts that are undergoing changes by the developers",
271
+ "* *Log*: Those that should be kept permanently or for a relatively long time for future reference, such as remarks on the reason a certain algorithm was chosen in the implementation, or the intent of a certain routine",
272
+ "Different annotations written in the spec file or the program code may be *related* by a *tag* (see {=Annotation}). For example, a single remark may be made for multiple locations in a program code file, and one may not want to repeat writing the same annotation in each location. Or one may want to annotate something in the spec file about a certain portion of the program code at some location. An annotation tag either has a *file scope* or a *method-body scope*, and annotations with the same tag within the same scope consist a *set* of related annotations and are displayed as one; their contents are joined with a space character, the file names and line numbers are compacted, and the line numbers where the content is taken from are emphasized. A tag is displayed after the locations. Similarly, same types of editorial markings are related per file, and are displayed chart together. Annotations with a file scope are displayed in the main context.",
273
+ "An assumption made about the development cycle is that annotations can be first written in isolation and free style as temporal annotation (i.e. agenda). Then, as it turns out to be organized information that should be recorded (i.e. log), they should be tagged, and the content should be moved into the spec file, with a tag relating the original location in the program code and the corresponding annotation in the spec file. Based on this, annotations and editorial markings are categorized as agenda or log by the following rule:",
274
+ "* Un-tagged annotations are log.",
275
+ "* Tagged annotations are agenda.",
276
+ "* Editorial markings are agenda.",
277
+ coda
278
+
279
+ spec "====Test Reports",
280
+ "A feature can have *tests* (unit tests or benchmarks). A unit test is run against each alternative implementation of the feature. A benchmark is run together for the alternative implementations of the feature. Each test results in one of the following in the order of priority given below (for terminology and {::#expr}, see {=Testing}):",
281
+ "# If the string argument of `expr` cannot be parsed as Ruby code, then the result is “Bad test”.",
282
+ "# If the string argument of `expr` within the exercise receiver or arguments of a test cannot be evaluated in the given context, then the result is “Bad test”.",
283
+ "# If the exercise receiver of a test is not an instance (of a subclass) of the module that the feature is described under, then the result is “Untestable”.",
284
+ "# If the feature to be tested is not defined on the exercise receiver, then the result is “Untestable” (if there is an alternative implementation, then the feature is regarded as defined with respect to the test even if the main implementation is not defined).",
285
+ "# If calling the feature on the exercise receiver and arguments raises an error, then the result is “Bug”.",
286
+ "# If the string argument of `expr` within the verifying arguments of a test cannot be evaluated in the given context, then the result is “Bad test”.",
287
+ "# If a unit test contingent on another one is not preceded by a successfully exercised unit test, then the result is “Untestable”.",
288
+ "# If calling the tested feature on the exercise result and the verifying arguments raises an unexpected error, then the result is “Bug”.",
289
+ "# If the test is a unit test, and the verification result is falsy, then the result “Bug”.",
290
+ "# The result is “Success”.",
291
+ "Tests are reported per feature in the following way:",
292
+ "* If a test results in “Bad test” or “Untestable” (for an implementation of the feature), then its report for the entire feature will be as such (“Bad test” or “Untestable”), with a common message.",
293
+ "* If a test results in “Bug” for any implementation of the feature, then its report for the entire feature will be “Bug”. If the test is a unit test, a message is shown under a bullet for each implementation that resulted in “Bug”. If it is a benchmark, a message is shown only for the first implementation that resulted in “Bug”.",
294
+ "* If a test results in “Success” for all implementations of the feature, then “Success” is displayed if it is a unit test, or a graph is shown if it is a benchmark.",
295
+ coda
296
+
297
+ spec "===Top Bar",
298
+ "The top bar in developer's chart is a superset of that of the user's manual. The numbers displayed aside visibility buttons and type buttons may be greater than the corresponding number displayed in the user's manual. This is due to some features being marked as “hidden” (see {=Feature}). In addition to the functionality in the user's manual, the top bar has the following.",
299
+ "* “User Items” button: Toggles *user items*, i.e. items that are primarily for the user's manual. Relevant items are in distinct background color when displayed.",
300
+ "* Documentation status buttons (“Undocumented”, “Hidden”, “Misplaced”, “Moved”), documentation report buttons (“Missing doc”, “Bad doc”, “Agenda”, “Log”), and test report buttons (“Missing test”, “Bad test”, “Untestable”, “Bug”, “Success”, “Benchmark”): They work similarly to visibility buttons and type buttons.",
301
+ coda
302
+
303
+ spec "=Running Manager",
304
+ "Manager can either be run as a command on the terminal, or be called within a Ruby code.",
305
+ "* To run Manager on the terminal, execute the command `manager` with the path to the spec file as an argument. To skip a time-consuming test, type Ctrl+C during the test.",
306
+ <<~'BASH'.code(:bash),
307
+ $ manager some_directory/spec_file
308
+ BASH
309
+ "* To run Manager within in a Ruby code, execute the method {Manager.new} with the path to the spec file as an argument.",
310
+ <<~'RUBY'.code,
311
+ Manager.new("some_directory/spec_file")
312
+ RUBY
313
+ coda
314
+
315
+ spec "==Customization and Options",
316
+ "Customization is done either through a command line option using a double hyphen `--` before a key, possibly followed by the value, or through the {Manager.config} method in the spec file with a symbol key and a value. The options in this section are given for `Manager.config`. To use them in the command line, read underscores `_` in them as hyphens `-`. Multiple options can be specified with a single command/method. When an option is specified in both ways, the command line option overrides the `config` method.",
317
+ coda
318
+
319
+ spec "===Spell Check (spell_check, spell-check-list, spell-check-filter, case_sensitive, case_insensitive)",
320
+ "Spell checking is available if the `ffi-aspell` gem and the relevant natural language component are installed. The inventory of available languages is shown in the command line by the `--spell-check-list` option:",
321
+ <<~'BASH'.code(:bash),
322
+ $ manager --spell-check-list
323
+ BASH
324
+ "The `spell_check` option sets the language to be used for spell checking the text. When set to `nil`, spell check is turned off. The default is `nil`.",
325
+ <<~'BASH'.code(:bash),
326
+ $ manager some-directory/spec-file --spell-check en
327
+ BASH
328
+ "or",
329
+ <<~'RUBY'.code,
330
+ Manager.config spell_check: "en"
331
+ RUBY
332
+ "To check which words among a short list are not in the dictionary, run in the command line with the `spell-check-filter` option followed by the dictionary and the words to check.",
333
+ <<~'RUBY'.code(:bash),
334
+ $ manager --spell-check-filter en prefix prepend affix append
335
+ The following words are not in the dictionary `en`:
336
+ prepend
337
+ RUBY
338
+ "To exempt words that are not listed in the dictionary from spell errors, list them in an array and pass them with `case_sensitive` and `case_insensitive` options respectively, depending on whether the case matters.",
339
+ <<~'RUBY'.code,
340
+ Manager.config case_sensitive: %w[
341
+ API
342
+ CSS
343
+ ...
344
+ ]
345
+ Manager.config case_insensitive: %w[
346
+ falsy
347
+ matchers
348
+ ...
349
+ ]
350
+ RUBY
351
+ coda
352
+
353
+ =begin
354
+ spec "===Timeout (timeout)",
355
+ "This option sets the maximum time allowed for a unit test or benchmark to run. Anything that exceeds this amount will be terminated."
356
+ <<~'BASH'.code(:bash),
357
+ $ manager some-directory/spec-file --timeout 1
358
+ BASH
359
+ "or",
360
+ <<~'RUBY'.code,
361
+ Manager.config timeout: 1
362
+ RUBY
363
+ coda
364
+ =end
365
+
366
+ spec "===Output Directory (odir)",
367
+ "The output directory is by default the directory of the spec file. To override this, use the `odir` option. Relative path can be used. In such case, it will be expanded relative to the directory of the spec file.",
368
+ <<~'BASH'.code(:bash),
369
+ $ manager some-directory/spec-file --odir ../documents
370
+ BASH
371
+ "or",
372
+ <<~'RUBY'.code,
373
+ Manager.config odir: "../documents"
374
+ RUBY
375
+ coda
376
+
377
+ spec "===User's Manual File Name (user)",
378
+ "The user's manual is written as the file name `MANUAL.html` by default. To override this, use the `user` option.",
379
+ <<~'BASH'.code(:bash),
380
+ $ manager some-directory/spec-file --user README.html
381
+ BASH
382
+ "or",
383
+ <<~'RUBY'.code,
384
+ Manager.config user: "README.html"
385
+ RUBY
386
+ coda
387
+
388
+ spec "===Developer's Chart File Name (dev)",
389
+ "The developer's chart is written as the file name `CHART.html` by default. To override this, use the `dev` option.",
390
+ <<~'BASH'.code(:bash),
391
+ $ manager some-directory/spec-file --dev result.html
392
+ BASH
393
+ "or",
394
+ <<~'RUBY'.code,
395
+ Manager.config dev: "result.html"
396
+ RUBY
397
+ coda
398
+
399
+ spec "===Title (title)",
400
+ "A certain string is used as part of the title to describe what the user's manual and developer's chart are about. By default, the directory name of the spec file is used in hope that in many cases, that should describe the software's project name. To override this, use the `title` option.",
401
+ <<~'BASH'.code(:bash),
402
+ $ manager some-directory/spec-file --title Foo\ Software
403
+ BASH
404
+ "or",
405
+ <<~'RUBY'.code,
406
+ Manager.config title: "Foo Software"
407
+ RUBY
408
+ coda
409
+
410
+ spec "===Base Directory (bdir)",
411
+ "The base directory is the reference point for describing relative file names in the output. It does not affect `require`/`load` commands or the like. By default, it is the directory of the spec file. To override this, use the `bdir` option. Relative path can be used. In such case, it will be expanded relative to the directory of the spec file.",
412
+ <<~'BASH'.code(:bash),
413
+ $ manager some-directory/spec-file --bdir ../lib
414
+ BASH
415
+ "or",
416
+ <<~'RUBY'.code,
417
+ Manager.config bdir: "../lib"
418
+ RUBY
419
+ coda
420
+
421
+ spec "===Theme (theme, theme-list)",
422
+ "The design of the output files are controlled by a theme. New themes are occasionally added to Manager gem within version updates. Each theme is described as a CSS format file, and is named after a four digit number describing the publishing year, followed by a letter (starting from `a` and incremented) to distinguish multiple designs in a single year, followed by the `.css` extension. In addition, another CSS format file describes the part of the design related to syntax highlighting of the code blocks (see {=Syntax Highlighting (highlight, highlight-list)}). These files are incorporated as part of the output HTML files. The inventory of available theme files and highlight files can be accessed from the command line by the `theme-list` option.",
423
+ <<~'BASH'.code(:bash),
424
+ $ manager --theme-list
425
+ BASH
426
+ "The default theme is selected among the newest ones included at the time of shipping of a version of Manager. To select a theme, use the `theme` option with the theme file name.",
427
+ <<~'BASH'.code(:bash),
428
+ $ manager some-directory/spec-file --theme 2016a.css
429
+ BASH
430
+ "or",
431
+ <<~'RUBY'.code,
432
+ Manager.config dev: "2016a.css"
433
+ RUBY
434
+ "To add a custom theme, describe the design in CSS format, and place the file within the directory `manager/theme`. Designs for user's manual and developer's chart can be written independently by placing the CSS selector under the id selectors `#user` and `#dev`, respectively. Otherwise, the design is common to both.",
435
+ "* Common design",
436
+ <<~'CSS'.code(:css),
437
+ #main{
438
+ padding-top: 20;
439
+ padding-bottom: 20;
440
+ }
441
+ CSS
442
+ "* Only for user's manual",
443
+ <<~'CSS'.code(:css),
444
+ #user #main{
445
+ padding-right: 240;
446
+ padding-left: 240;
447
+ }
448
+ CSS
449
+ "* Only for developer's chart",
450
+ <<~'CSS'.code(:css),
451
+ #dev #main{
452
+ padding-right: 40;
453
+ padding-left: 20;
454
+ }
455
+ CSS
456
+ "If there is a comment `/* ... */` that starts and ends within the first line of a theme file, then its content (with spaces at the edges stripped) will be displayed in brackets alongside the file name in response to the `theme-list` option.",
457
+ "* Theme file",
458
+ <<~'CSS'.code(:css),
459
+ /* Author: sawa. The default design */
460
+ ...
461
+ CSS
462
+ "* Showing the inventory",
463
+ <<~'BASH'.code(:bash),
464
+ $ manager --theme-list
465
+ 2016a.css [Author: sawa. The default design]
466
+ ...
467
+ BASH
468
+ coda
469
+
470
+ spec "===Syntax Highlighting (highlight, highlight-list)",
471
+ "To select a design for syntax highlighting of code blocks, use the `highlight` option with the highlight file name.",
472
+ <<~'BASH'.code(:bash),
473
+ $ manager some-directory/spec-file --highlight coderay_github.css
474
+ BASH
475
+ "or",
476
+ <<~'RUBY'.code,
477
+ Manager.config highlight: "coderay_github.css"
478
+ RUBY
479
+ "To add a highlight design, place the file within the directory `manager/theme`.",
480
+ "To obtain the list of symbols for the languages that can be highlighted, use the `highlight-list` option (see {=Code Block} for its usage).",
481
+ <<~'RUBY'.code(:bash),
482
+ $ manager --highlight-list
483
+ RUBY
484
+ coda
485
+
486
+ spec "===Debugging of Manager (debug)",
487
+ "This is not intended for normal use. When the `debug` option takes the `true` value as follows,",
488
+ <<~'BASH'.code(:bash),
489
+ $ manager some-directory/spec-file --debug true
490
+ BASH
491
+ "or",
492
+ <<~'RUBY'.code,
493
+ Manager.config debug: true
494
+ RUBY
495
+ "it has the following effects:",
496
+ "# Errors raised by running a spec file and reported on the command line will have backtraces including files internal to Manager. By default, such detailed outputs are suppressed.",
497
+ "# Outputs to standard output and standard error due to running tests are displayed on the command line. By default, they are redirected and are displayed in the output section of test reports.",
498
+ "# Files internal to Manager are displayed in backtraces in test reports when an error or a bug is detected. By default, they are excluded.",
499
+ coda
500
+
501
+ spec "=Spec File",
502
+ "Documentation and Tests are written in a spec file. When the spec file contains some higher level errors, they will be reported in the developer's chart in the manner mentioned in {=Documentation Reports}, but when there is a syntax error or some low level error, Manager will terminate with an error message without generating any file. In order to feed back the source location of a string in case of an error, Manager stores some objects taken from the spec file with the source location. Particularly, it is important to set the strings in the spec file to be mutable in order for Manager to fully function. Especially under future Ruby 3.0, where strings are frozen by default, it is necessary to ensure this by placing a frozen string pragma with the `false` value at the beginning of the spec file:",
503
+ <<~'RUBY'.code,
504
+ # frozen_string_literal: false
505
+ RUBY
506
+ "Specification can be divided in multiple files as long as there is a single main spec file whose name is directly passed as an argument to the `manager` command or {Manager.new} method, which loads the other spec files.",
507
+ coda
508
+
509
+ spec "==Reference to Program Code",
510
+ "Program code can be written in files different from the spec file or in the same file as the (main) spec file as *scratch code*, or in combination of both. A program and spec should be normally written in separate files, but in order to do a quick test with a short snippet of code, using scratch code is convenient.",
511
+ coda
512
+
513
+ spec "===Listing A File",
514
+ "In order for a program code file to be the object of analysis by Manager, it has to be *listed* in the spec file. Listing a file has two effects:",
515
+ "# The file becomes the object of analysis, i.e. constants and methods implemented in the file will be detected by Manager, and will be displayed (even if they are not described in the spec file).",
516
+ "# The file is marked as one of the locations where the constants and methods described in the spec file are expected to reside. Any feature that has an unlisted source location is marked as “Misplaced” (see {=Developer's Chart=Main Information}).",
517
+ "To list a program file, pass its path as an argument to `manage` method in the spec file in a position before the specifications. The file path may be either absolute or be relative to the spec file. A program file `foo.rb` may internally load a subfile `bar.rb`. If `bar.rb` is to be the object of analysis, it has to listed in the spec file (after `foo.rb`). The beginning of a spec file may look like this:",
518
+ <<~'RUBY'.code,
519
+ # frozen_string_literal: false
520
+ manage "foo.rb"
521
+ manage "bar.rb"
522
+ RUBY
523
+ "If a path passed to `manage` is a directory, then all of its (recursive) entry files are listed (and loaded). If a specific loading order should be imposed among them, write the more specific files or directories that should be loaded earlier in an earlier position. Any files that have already been listed would be skipped when they appear within a directory later passed to `manage`. For example, suppose directory `foo` has files `main.rb`, `sub1.rb`, and `sub2.rb`. Writing:",
524
+ <<~'RUBY'.code,
525
+ manage "foo"
526
+ RUBY
527
+ "will list all three files, but will not guarantee any loading order among them. If `sub1.rb` and `sub2.rb` both depend on `main.rb` (but do not depend on each other), then `main.rb` has to be loaded first. This can be described as:",
528
+ <<~'RUBY'.code,
529
+ manage "foo/main.rb"
530
+ manage "foo"
531
+ RUBY
532
+ "and `main.rb` will not be loaded twice.",
533
+ "To avoid “Misplaced” markings on features whose source location is unknown (for example, when it is implemented in C), list unknown source locations by passing `nil` to `manage`:",
534
+ <<~'RUBY'.code,
535
+ manage nil
536
+ RUBY
537
+ coda
538
+
539
+ spec "===Scratch Code",
540
+ "Scratch code should appear within the main spec file after the line `__END__` after the spec code. Writing in this order encourages test driven development (see {=Simple Example} for examples).",
541
+ coda
542
+
543
+ spec "==The `spec` method",
544
+ "Description in Manager is done in units using the method {Module#spec} (This method can appear in the main environment as well as in a module body). The `spec` method is placed in a *context*, i.e, a module (including class) body or the main environment. It takes a *label*, which must be either of the following:",
545
+ "# A string starting with `::`, which represents a constant (including modules, classes)",
546
+ "# A string starting with `.`, which represents a singleton method",
547
+ "# A string starting with `#`, which represents an instance method",
548
+ "# A string starting with one or more consecutive `=`s and has no other `=`, which represents a description with a numbered section header",
549
+ "# `nil`, which represents a description without a section header",
550
+ "followed by an arbitrary number of items (explained in {=Documentation} and {=Testing}), and ends with a pseudo-keyword {Module#coda}. 1–3 above describe a feature, and 4,5 describe a section header. The recommended way of writing descriptions looks like this (empty lines between `coda` and the next `spec` are optional):",
551
+ <<~'RUBY'.code,
552
+ class A
553
+ spec nil,
554
+ "This class provides API for doing foos.",
555
+ coda
556
+
557
+ spec "=How to Create a New Foo",
558
+ coda
559
+
560
+ spec "#foo",
561
+ {"(argument)" => Foo},
562
+ "This method takes a string and converts it into an instance of `Foo`",
563
+ coda
564
+
565
+ spec "::Foo",
566
+ coda
567
+ end
568
+ RUBY
569
+ "which may result in a user's manual that looks like this:",
570
+ image("Example of `spec`", "spec/spec_example.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
571
+ "The `spec` method requires its final argument to be the pseudo-keyword `coda`, which is similar to, but different from, the `end` keyword of Ruby syntax. This design serves two purposes:",
572
+ "# All items passed to the `spec` method uniformly have to be appended by a comma `,`, which makes it easier for writing or modifying a spec file; the writer does not have to think whether or not to put a comma depending on whether the item is the last one.",
573
+ "# Without this requirement, an item may be unintentionally skipped because of a forgotten comma after the preceding item. It makes it easier to detect such mistakes.",
574
+ "The order of the items passed to `spec` is respected in the generated files, but when the same feature is described under multiple calls of the `spec` method, all items will be gathered at the position of the first `spec` call of the feature.",
575
+ coda
576
+
577
+ spec "===Feature",
578
+ "By default, a feature description is expected to have:",
579
+ "* At least one paragraph (see {=Paragraph}) and",
580
+ "* At least one unit test (see {=Unit Test}).",
581
+ "Additionally, a feature that is a method is expected to have:",
582
+ "* At least one method signature (see {=Method Signature}).",
583
+ "If a feature description lacks a paragraph (or a method signature), then by default it is tagged as “Missing doc”. If a feature description lacks a unit test, then by default it is tagged as “Missing test”.",
584
+ "Depending on the implementation and description status, a feature may be marked as follows:",
585
+ "* A feature that is described in the spec file but is not implemented in the program code is marked its visibility as “Unimplemented” (see {=User's Manual=Main Information}).",
586
+ "* A feature that is not described but is implemented is, by default, marked as “Undocumented” (see {=Developer's Chart=Main Information}).",
587
+ "* A feature that is described and is implemented is, by default, expected to confirm to the followings. If it violates any, then by default it is marked as “Misplaced”.",
588
+ "** Be defined within one of the files listed (see {=Listing A File}) or in scratch code (see {=Scratch Code}),",
589
+ "** Belong to the module it is described under (i.e. not by inheritance), and",
590
+ "** Not be an alias method (Aliases are shown with the main method).",
591
+ coda
592
+
593
+ spec "===Hiding or Moving A Feature",
594
+ "Placing the {Module#hide} method before `spec` hides the feature in the user's manual (but not in the developer's chart), and exempts the feature from “Missing doc” and “Missing test” tags.",
595
+ <<~'RUBY'.code,
596
+ hide spec "#foo",
597
+ ...,
598
+ coda
599
+ RUBY
600
+ "Placing either the `hide` method or the {Module#move} method before `spec` indicates that such violation is intended, and exempts the feature from “Undocumented” and “Misplaced” markings.",
601
+ <<~'RUBY'.code,
602
+ move spec "#foo",
603
+ ...,
604
+ coda
605
+ RUBY
606
+ "Since `move` plays a proper subset of the roles of `hide`, using both methods simultaneously on a single feature is redundant and is not valid.",
607
+ "When `hide` or `move` is applied to a method or a constant that is not a module, “Hidden” or “Moved” tag will respectively appear on the features. When they are applied to a module constant, the tag is displayed under the module section, and not on the feature that lists it as a constant. In such case, the effect is inherited by the features and section headers of itself and its (recursive) sub-modules (by they are not tagged). The `hide` method applied to a feature overrides `move` applied to a (less specific) module. For example, the following spec descriptions:",
608
+ <<~'RUBY'.code,
609
+ move spec "::A",
610
+ ...,
611
+ coda
612
+
613
+ class A
614
+ spec "#foo_a",
615
+ ...,
616
+ coda
617
+
618
+ hide spec "#bar_a",
619
+ ...,
620
+ coda
621
+
622
+ class B
623
+ spec "#foo_b",
624
+ ...,
625
+ coda
626
+
627
+ hide spec "bar_b",
628
+ ...,
629
+ coda
630
+ end
631
+ end
632
+ RUBY
633
+ "will make `A`, `A#foo_a`, `B`, and `B#foo_b` moved, and `A#bar_a` and `B#bar_b` hidden.",
634
+ "The methods `hide` and `move` should be used to hide implementation details in the user's manual. If some features are implemented not for the user's use, but for internal use, then they should be hidden from the user by the `hide` method. If some features are implemented under a module `A` aimed for developers, which is included in, or prepended to, some modules `B` and `C` aimed for the users, then `A` should be marked by `hide`, and `B` and `C` should be marked by `move`.",
635
+ "A hidden feature cannot include a user item (Cf. {=Developer's Chart=Top Bar}. See subsections in {=Documentation} for whether an item is a user item. No item under {=Testing} is a user item).",
636
+ coda
637
+
638
+ spec "===Section Header",
639
+ "Section header starts a section about a module or the entire software (if written in the main environment). If the label passed to `spec` is `nil`, then the section does not have an explicit header. If the label starts with `=`, then the header is numbered, and has the content that is the substring following the `=`s. Header numbering has hierarchy; the number of `=` characters describes the depth of the numbering (one, outermost level to fifth, innermost level). If that is more than five, then, it will be set to the fifth depth.",
640
+ "No two numbered headers in a sibling position to each other in the same module can have the same content. This is an example of an invalid heading:",
641
+ <<~'RUBY'.code,
642
+ class A
643
+ spec "=About Foo",
644
+ coda
645
+
646
+ spec "=About Bar",
647
+ coda
648
+
649
+ spec "=About Foo",
650
+ coda
651
+ end
652
+ RUBY
653
+ "This is an example of a valid heading:",
654
+ <<~'RUBY'.code,
655
+ spec "=How to Use It",
656
+ coda
657
+
658
+ class A
659
+ spec "=How to Use It",
660
+ coda
661
+ end
662
+
663
+ class B
664
+ spec "=How to Use It",
665
+ coda
666
+
667
+ spec "=Foo",
668
+ coda
669
+
670
+ spec "==How to Use It",
671
+ coda
672
+
673
+ spec "=Bar",
674
+ coda
675
+
676
+ spec "==How to Use It",
677
+ coda
678
+ end
679
+ RUBY
680
+ "and will be displayed as:",
681
+ image("Valid Heading", "spec/valid_heading.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
682
+ coda
683
+
684
+ spec "===Main Context",
685
+ "In Ruby code, methods and constants defined in the main context are owned by the `Object` class, hence with respect to the ownership (but not for visibility), it would not make a difference if they were defined in an `Object` class body. Manager handles feature descriptions in the same way; describing a feature in the main context is equivalent to describing it within the `Object` class body.",
686
+ "However, Manager distinguishes between describing a section header in the main context and describing it within the `Object` class body. The former should be used to describe the entire software, whereas the latter should be done to describe particularly the `Object` class.",
687
+ "In the following example,",
688
+ <<~'RUBY'.code,
689
+ spec "=About This Software",
690
+ "To install, type `gem install foo`.",
691
+ coda
692
+
693
+ spec "#bar",
694
+ "Some description about the method `bar`.",
695
+ coda
696
+
697
+ class Object
698
+ spec "=Some Notes about The `Object` Class",
699
+ "There are some original methods defined, so be careful.",
700
+ coda
701
+
702
+ spec "#bar",
703
+ "More description about the method `bar`.",
704
+ coda
705
+ end
706
+ RUBY
707
+ "the main context and the `Object` class each has a section header and a feature description. It is rendered as:",
708
+ image("Main context and Object", "spec/main_and_object.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
709
+ "The descriptions for the instance method `bar`, which is scattered between the main context and `Object` in the spec file, have been combined within `Object` under a single feature header `bar`, whereas the section headers in main and `Object` appear separately.",
710
+ coda
711
+
712
+ spec "==Documentation",
713
+ coda
714
+
715
+ spec "===Method Signature",
716
+ "A method signature is a user item.",
717
+ "Method signature is described by a hash, with a string key describing arguments and the value describing a (range of) return value. The return value may be described by a class to which the return value belongs to, or by the {Module#value} method with the argument `obj`, which describes the return value as a particular object `obj`, or by the {Module#error} method with the argument `exception_class` that describes a class of exceptions that may be raised. Multiple types of return values may be joined by the method {Class#|} to express alternatives. (The parentheses around the argument of `value` or `error` should not be omitted, as `|` has stronger associativity than method call.)",
718
+ <<~'RUBY'.code,
719
+ {"(str, *arr)" => value(-1) | value(0) | value(1) | Array | error(ArgumentError)}
720
+ RUBY
721
+ "From the point of view of implementation, a method only has one signature (with possibly optional arguments and varying number of arguments), but form the user's side, it is often helpful to regard some methods as having multiple usages, and to have them documented separately. For example, a method `foo` may be implemented to have an obligatory argument `a` and an optional argument `b`, and an optional block `pr`,",
722
+ <<~'RUBY'.code,
723
+ def foo a, b = nil, &pr
724
+ ...
725
+ end
726
+ RUBY
727
+ "These options can be split into several usages. One documentation might be as follows:",
728
+ <<~'RUBY'.code,
729
+ spec "#foo",
730
+ {"(a)" => Foo, "(a, b)" => Foo | error(RuntimeError)},
731
+ "Without a block, `foo` calculates the foo using the foo algorithm. If an optinal argument `b`
732
+ is given, an error might be raised.",
733
+ ...,
734
+ {"(a, &pr)" => Foo | value(nil)},
735
+ "When a block is given, `foo` passes the foo value to the block.",
736
+ ...,
737
+ coda
738
+ RUBY
739
+ "which would be displayed as:",
740
+ image("Method signature", "spec/signature.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
741
+ coda
742
+
743
+ spec "===Paragraph",
744
+ "A paragraph is described by a string not beginning with `>`, `*`, or `#`, and optionally beginning with `!` (see {=Annotation} for exceptions). A paragraph is a user item if and only if it does not begin with `!`.",
745
+ "The following markups are applied to a paragraph, with the priority in the order given:",
746
+ "# Locally minimum substring surrounded by balanced sequences of one or more backticks `` `...` ``: inline code",
747
+ "# Substring not including `*` surrounded by double asterisks `**...**`: bold face",
748
+ "# Substring not including `*` surrounded by single asterisks `*...*`: italics",
749
+ "# Substring not including `{` and `}` surrounded by a pair of braces `{...}`: link (see {=Link})",
750
+ "In order to mark an inline code that includes backticks, surround it with a pair of identical sequences of backticks that are longer than any consecutive backticks in the inline code.",
751
+ "If an inline code begins and/or ends with a backtick, or if the beginning of a paragraph is to be italicized or bold faced, then insert a space to avoid interference with the notation. Such spaces will be stripped.",
752
+ "Consecutive space characters in a part of string that is not marked up by any of the above are squeezed into single space characters, so line changes can be made in the middle of a string without affecting the output.",
753
+ "For example,",
754
+ <<~'RUBY'.code,
755
+ "The part `* bar *` in `foo * bar * baz` is not interpreted as an italicized word
756
+ because inline code has precedence over bold face in markup.",
757
+ 'Suppose we have this expression: ``` `echo #{string.sub(/``/, "x")}` ```.
758
+ If we run it, it will echo something.',
759
+ " *This is a paragraph that starts with an italicized sentence.*",
760
+ "! This is a non-user (i.e. developer) paragraph.",
761
+ RUBY
762
+ "is rendered in the developer's chart as:",
763
+ image("Markup", "spec/markup.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
764
+ "Note that the developer paragraphs are hidden in the user's manual, and the user's paragraphs have a distinct background in the developer's chart.",
765
+ "As a tip, characters that are difficult to be input from the keyboard can be described by their UTF-16 code in hexadecimal notation after the escape character `\\u` (this is Ruby's feature). For example,",
766
+ <<~RUBY.code,
767
+ "\\u25B8" # => "▸"
768
+ RUBY
769
+ "Non-marked up portion of a string is also subject to spell checking if the option is turned on (see {=Spell Check (spell_check, spell-check-list, spell-check-filter, case_sensitive, case_insensitive)}).",
770
+ coda
771
+
772
+ spec "===Link",
773
+ "Links are expressed in a pair of braces `{` and `}`. No link can have a brace character inside. Depending on the surrounded string, there are three types.",
774
+ coda
775
+
776
+ spec "====External Links",
777
+ "An external link is expressed by a string that includes a URL that includes the scheme `://`. The URL can optionally be preceded by a link name and a comma `,`. The name cannot include `=` (This is to avoid ambiguity. See {=Links to Section Headers}). This example:",
778
+ <<~'RUBY'.code,
779
+ "This is a link without a name: {https://www.ruby-lang.org}, and
780
+ {this, https://www.ruby-lang.org} is a link with a name",
781
+ RUBY
782
+ "results in:",
783
+ image("External link", "spec/external_link.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
784
+ coda
785
+
786
+ spec "====Links to Features",
787
+ "A link to a feature is expressed by a string that has a feature name (which starts with `::`, `#`, or `.`), and is optionally preceded by a module. Absence of a module makes the current context/module be the relevant domain. If a module is given, it is interpreted with the same rule as in Ruby code, relative to the current context. The following are possible links:",
788
+ <<~'RUBY'.code,
789
+ spec "#a",
790
+ "Links in each list item have the same target.",
791
+ "* {#b}, {Object#b}",
792
+ "* {A}, {::A}, {Object::A}",
793
+ coda
794
+
795
+ class A
796
+ spec "#c",
797
+ "* {#d}, {::A#d}, {Object::A#d}",
798
+ coda
799
+ end
800
+ RUBY
801
+ coda
802
+
803
+ spec "====Links to Section Headers",
804
+ "A link to a section header is expressed by a string that starts with a single `=`, followed by the name of the section header, and is optionally preceded by a module.",
805
+ "Omission of a module makes the link refer to the current context. The main context is described as `::`, which has different effect from `Object` when linking to section headers, unlike when linking to features (see {=Main Context}). When the link target belongs to a different context from where it is written, the context is described in parentheses. Suppose we have section headers named “Summary” in the main context, the `Object` class, and another class `A`, and some some links:",
806
+ <<~'RUBY'.code,
807
+ spec "=Summary",
808
+ coda
809
+
810
+ spec "=Links",
811
+ "Here is a link: {::=Summary}.",
812
+ "Here is another: {=Summary}.",
813
+ coda
814
+
815
+ class Object
816
+ spec "=Place Holder 1",
817
+ coda
818
+
819
+ spec "=Summary",
820
+ coda
821
+
822
+ spec "=Links",
823
+ "Here is a link: {::=Summary}.",
824
+ "Here is another: {=Summary}.",
825
+ coda
826
+ end
827
+
828
+ class A
829
+ spec "=Place Holder 1",
830
+ coda
831
+
832
+ spec "=Place Holder 2",
833
+ coda
834
+
835
+ spec "=Summary",
836
+ coda
837
+
838
+ spec "=Links",
839
+ "Here is a link: {::=Summary}.",
840
+ "Here is another: {=Summary}.",
841
+ coda
842
+ end
843
+ RUBY
844
+ image("Context of Module Evaluation", "spec/context_module.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
845
+ "The three links `{::=Summary}` all refer to the same section header in the main context (among which the two that are not written in the main context are displayed with “(main)”), but the three links `{=Summary}` respectively refer to the section header of the context it is written in; only the link `{=Summary}` in the main context has the same target as the `{::=Summary}` links.",
846
+ "Regardless of the depth of numbering on the target header, the section header name in a link should be prefixed by exactly one `=`. Multiple section headers with the same name within a module can be disambiguated by sufficient levels of ancestor header names joined by `=`, put in front of the `=` before the target header name. Among the matching candidates, the one with the least depth will be referred to. Given a section structure:",
847
+ <<~'RUBY'.code,
848
+ spec "=Summary",
849
+ coda
850
+
851
+ spec "=Foo",
852
+ coda
853
+
854
+ spec "==Summary",
855
+ coda
856
+
857
+ spec "=Bar",
858
+ coda
859
+
860
+ spec "==Summary",
861
+ coda
862
+
863
+ spec "==Bar",
864
+ coda
865
+
866
+ spec "===Summary",
867
+ coda
868
+ RUBY
869
+ "rendered as:",
870
+ image("Nested Section Headers", "spec/nested_section_headers.png", width: "100%;", border: "1px solid hsl(0, 0%, 80%);"),
871
+ "the four section headers named “Summary” can be unambiguously targeted by the links:",
872
+ <<~'RUBY'.code,
873
+ "{=Summary}, {=Foo=Summary}, {=Bar=Summary}, {=Bar=Bar=Summary}"
874
+ RUBY
875
+ "which will be rendered as:",
876
+ image("Unambiguous Links", "spec/unambiguous_links.png", width: "100%;", border: "1px solid hsl(0, 0%, 80%);"),
877
+ coda
878
+
879
+ spec "===Annotation",
880
+ "Annotations are not user items.",
881
+ "A string item in the spec file that starts with with `!!` or `!`, followed by an optional space, an *annotation tag* consisting of non-space characters, and a colon `:` is an annotation, particularly a log item (rather than a developer's paragraph). A spec file may look like:",
882
+ <<~'RUBY'.code,
883
+ manage "sample.rb"
884
+
885
+ spec nil,
886
+ "!! todo_by_summer: This must be done by the end of summer.",
887
+ "!! foo_algorithm: This code chunk implements the foo algorithm.
888
+ It assumes blah blah blah, then calculates blah blah.",
889
+ coda
890
+
891
+ spec "#foo",
892
+ "! nil_proof: This routine takes care of the cases when `bar` is `nil`.",
893
+ "! This is not an annotation but is a developer's paragraph since it does not have a tag.",
894
+ ...
895
+ coda
896
+ RUBY
897
+ "Comment inside a method body in a program code starting with `#!!` or `#!`, and comment lines immediately following such lines are also annotations. If an annotations in a program code has an annotation tag (followed by a colon `:`), the annotation is a log, and is related to other logs extracted from the program code and spec file. If it does not have a tag, then the annotation is an agenda. A method definition in the program code may have annotations like this:",
898
+ <<~'RUBY'.code,
899
+ def foo
900
+ ...
901
+ ... #! nil_proof:
902
+ ...
903
+ #!! foo_algorithm:
904
+ ...
905
+ end
906
+ def bar
907
+ ... #!! todo_by_summer:
908
+ #!! foo_algorithm:
909
+ ...
910
+ #! Somehow this routine is required.
911
+ # But I don't know why.
912
+ end
913
+ RUBY
914
+ "The purpose of a tag is to relate different annotations. Annotations in the same scope with the same tag are concatenated and displayed as one (see {=Annotations and Editorial Markings}). The number of exclamation marks at the beginning of an annotation expresses the scope:",
915
+ "* `#!!` in the spec file or `!!` in a program code expresses file scope; all annotations with the same tag within the same program file are concatenated together.",
916
+ "* `#!` in the spec file or `!` in a program code expresses method-body scope; all annotations with the same tag within the body of the same method in a program code are concatenated together.",
917
+ "Running Manager with the spec file above and the program code above (named as `sample.rb`) will result in the following (with irrelevant content removed):",
918
+ image("Annotations", "spec/annotations.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
919
+ "An annotation in the spec file with a file-scope tag can only appear in the main context with an implicit or an explicit section header (i.e. not with a constant or a method). The following spec file is invalid.",
920
+ <<~'RUBY'.code,
921
+ manage "sample.rb"
922
+
923
+ spec "#foo",
924
+ "!! todo_by_summer: This must be done by the end of summer.",
925
+ coda
926
+
927
+ class A
928
+ spec nil,
929
+ "!! foo_algorithm: This code chunk implements the foo algorithm.
930
+ It assumes blah blah blah, then calculates blah blah.",
931
+ coda
932
+ end
933
+ RUBY
934
+ coda
935
+
936
+ spec "===Citation",
937
+ "Citation is described by a string starting with `>`. If the string has `!` right after it, then it is not a user item, otherwise, it is a user item.",
938
+ <<~'RUBY'.code,
939
+ "> This is user citation",
940
+ ">! This is developer citation",
941
+ RUBY
942
+ "These citations are displayed in the developer's chart as:",
943
+ image("Citations", "spec/citation.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
944
+ coda
945
+
946
+ spec "===List",
947
+ "A list item is described by a string starting with a sequence of one or more `*` or `#`. If the string has `!` right after it, then it is not a user item, otherwise it is a user item.",
948
+ "Consecutive list items constitute a list. Each `*` or `#` at the beginning of an item represents an un-numbered or numbered item respectively at the corresponding level. No other type of items may intervene list items in order for the list items to be recognized as a single list. This is important for lists that have numbered items, as the numbers will be reset by interruption. The following spec file:",
949
+ <<~'RUBY'.code,
950
+ "# user item",
951
+ "## user item",
952
+ "##* user item",
953
+ "## user item",
954
+ "# user item",
955
+ "#! developer item",
956
+ "##! developer item",
957
+ "#! developer item",
958
+ "! Some intervening developer's paragraph",
959
+ "#! developer item",
960
+ "##! developer item",
961
+ "##*! developer item",
962
+ "#! developer item",
963
+ RUBY
964
+ "is displayed in the developer's chart as:",
965
+ image("List", "spec/list.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
966
+ coda
967
+
968
+ spec "===Line",
969
+ "A string that starts with one or more consecutive `-` describes a line. It can be used to split units that are larger than paragraphs. If it is followed by `!`, then it is not a user item. Otherwise, it is a user item.",
970
+ coda
971
+
972
+ spec "===Code Block",
973
+ "A code block is expressed by a string followed by the method {String#code} or {String#code!}. These methods take an optional symbol argument in lower case, which describes the programming language in which syntax highlighting takes place (see {=Syntax Highlighting (highlight, highlight-list)}). By default, this is `:ruby`. Method `code` is for user item, and `code!` is for developer item. The recommended practice is to use a here document with an identifier of the form `~'RUBY'`.",
974
+ "* The `~` un-indents the code block to its least indented level.",
975
+ "* The single quotes `'...'` assure verbatim interpretation of the code block.",
976
+ "* A string expressing the language in capital, like `RUBY`, is recognized by many text editors so that syntax highlighting inside the code string will be done in that language.",
977
+ "!This could not take the form of `code \"some string ...\"` because `code \"some string\", `, followed by a comma is not syntactic in Ruby.",
978
+ "The following example:",
979
+ <<~'_'.code,
980
+ "A Ruby example for users:",
981
+ <<~'RUBY'.code,
982
+ def foo
983
+ puts "This is Ruby code."
984
+ end
985
+ RUBY
986
+ "! and a CSS code for developers:",
987
+ <<~'CSS'.code!(:css),
988
+ #main{
989
+ background-color: green;
990
+ }
991
+ CSS
992
+ _
993
+ "is displayed in the developer's chart as:",
994
+ image("Code block", "spec/code_block.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
995
+ coda
996
+
997
+ spec "===Image",
998
+ "An image is expressed by the method {Module#image} or {Module#image!}. Method `image` is for user item, and `image!` is for developer item. These methods take an obligatory string argument expressing the caption, an obligatory string argument describing the path of the image file (either absolute or relative to the output directory; see {=Output Directory (odir)}), and an optional hash of symbol keys and string or numeral values, which will be expanded into CSS commands in the generated HTML files.",
999
+ "The following example:",
1000
+ <<~'RUBY'.code,
1001
+ "For users",
1002
+ image("Ruby logo (Copyright © 2006, Yukihiro Matsumoto)", "ruby.png", width: 200),
1003
+ "! For developers",
1004
+ image!("Ruby logo (Copyright © 2006, Yukihiro Matsumoto)", "ruby.png", width: 100,
1005
+ border: "1px solid hsl(0, 0%, 80%)"),
1006
+ RUBY
1007
+ "is displayed in the developer's chart as:",
1008
+ image("Image", "spec/image.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1009
+ coda
1010
+
1011
+ spec "===Table",
1012
+ "Tables are user items. They are described by an array of arrays, each of which expresses a row, whose elements are strings, each of which expresses a cell.",
1013
+ "Text alignment of each cell is controlled by initial/final spacing in the string. Depending on whether the string has only a final space, only an initial space, or both, the cell will be left aligned, right aligned, or center aligned, respectively. Otherwise, the string will be left aligned by default. This item:",
1014
+ <<~'RUBY'.code,
1015
+ [
1016
+ [" Right aligned.", "A very very very very very long cell."],
1017
+ ["Another cell that is quite loooooooooooooooooong.", " This is centered. "]
1018
+ ],
1019
+ RUBY
1020
+ "is rendered as a table like this:",
1021
+ image("Table", "spec/table.png", border: "1px solid hsl(0, 0%, 80%)"),
1022
+ coda
1023
+
1024
+ spec "==Testing",
1025
+ "Manager can perform two types of tests: *unit test* and *benchmark*.",
1026
+ coda
1027
+
1028
+ spec "===Unit Test",
1029
+ "A unit test can be defined by the following steps:",
1030
+ "# *Setup*: Prepare objects and states required for the test. Often described by the term `Given` in conventional test frameworks.",
1031
+ "# *Exercise*: Perform the behavior that is the object of test. Often described by `When`.",
1032
+ "# *Verification*: Check the result of exercise. Often described by `Then`.",
1033
+ "# Additional verification: In required, do more checks on the result or on any change that is caused by the exercise. Often described by `And` after `Then`.",
1034
+ "In unit test in Manager, each step is described in the following way:",
1035
+ "# Setup: Describe the objects using Ruby literal expressions, or using {String#setup} and {::#expr} methods (see {=The `expr` Method}, {=Setup and Teardown}).",
1036
+ "# Exercise: Describe the Ruby expression that should be performed using the objects prepared by setup, but substitute the relevant feature (method or constant) call with the placeholder {BasicObject#UT}.",
1037
+ "# Verification: After the exercise, chain method call(s) that should be evaluated to a truthy value. (For this step, a methods named `initialize` and `method_missing` cannot be used. But this should rarely be a problem.)",
1038
+ "# Additional verification: Describe in a similar way as verification, but substitute the return value of exercise and the original receiver of exercise with the placeholders `RETURN` and `RECEIVER`, respectively.",
1039
+ "Every time `UT` is called, the value of `RETURN` and `RECEIVER` are reset.",
1040
+ "The following is an example of testing the `Array#push` method.",
1041
+ <<~'RUBY'.code,
1042
+ class Array
1043
+ spec "#push",
1044
+ [].UT("a") == ["a"],
1045
+ RETURN.length == 1,
1046
+ RECEIVER == ["a"],
1047
+ [].UT("a", "b") == ["a", "b"],
1048
+ RETURN.length == 2,
1049
+ RECEIVER == ["a", "b"],
1050
+ coda
1051
+ end
1052
+ RUBY
1053
+ "The first instance of `RETURN` and `RECEIVER` refer to the result of the first exercise (expressed by `UT`) and its original array receiver. By the second `UT` call, they are reset, so that the second instance of `RETURN` and `RECEIVER` refer to the new return value and the new receiver array.",
1054
+ "The result is as follows:",
1055
+ image("Unit test succeeded", "spec/unit_test_success.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1056
+ "An example of test failure is as follows:",
1057
+ <<~'RUBY'.code,
1058
+ class String
1059
+ spec "#concat",
1060
+ "".UT(:a) == "a",
1061
+ RETURN.length == 1,
1062
+ RECEIVER == "a",
1063
+ coda
1064
+ end
1065
+ RUBY
1066
+ image("Unit test failed", "spec/unit_test_failure.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1067
+ "After the first exercise is reported as resulting in a bug, the two verifications following (using `RETURN` and `RECEIVER`) are reported as untestable. They are reported as success or bug only when there is a preceding exercise that has succeeded. There are some other possibilities for the result of a unit test. See {=Test Reports} for detail.",
1068
+ "In order to repeat the same test schema over data, a tip is to use `map` with splat `*`.",
1069
+ <<~'RUBY'.code,
1070
+ spec "#foo",
1071
+ *[
1072
+ ["a", 1],
1073
+ ["b", 2],
1074
+ ["c", 3],
1075
+ ].map{|receiver, argument| receiver.UT(argument)},
1076
+ coda
1077
+ RUBY
1078
+ coda
1079
+
1080
+ spec "====Testing with `succeed?`, `raise?`, and `throw?`",
1081
+ "Sometimes, it is necessary to only verify that examination was performed without raising an exception. In such cases, use the method `succeed?`. Tests using this method will report success whenever examination was performed without an exception, without particular verification on the return value.",
1082
+ <<~'RUBY'.code,
1083
+ class String
1084
+ spec "#concat",
1085
+ "".UT(:a).succeed?,
1086
+ "".UT("a").succeed?,
1087
+ coda
1088
+ end
1089
+ RUBY
1090
+ image("Unit test checking successful examination", "spec/unit_test_succeed.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1091
+ "This method is useful also when one is interested in verifying the receiver of the feature, without particular expectation on the return value.",
1092
+ <<~'RUBY'.code,
1093
+ class String
1094
+ spec "#concat",
1095
+ "".UT("a").succeed?,
1096
+ RECEIVER == "a",
1097
+ coda
1098
+ end
1099
+ RUBY
1100
+ image("Unit test verifying the receiver", "spec/unit_test_receiver.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1101
+ "On the contrary, to unit test with the expectation that an exception be raised, use the `raise?(exception = StandardError, include_subclass = true, message: nil)` method as verification. When `include_subclass` is truthy (as in default), an instance of `exception` should be raised for the unit test to succeed. Otherwise, a kind of `exception` class should be raised. When `message` (assumed to be an instance of a `String` or `RegExp`) is given, `message ===` will be evaluated with the message string as argument. It must return a truthy value for the unit test to succeed. An example is this:",
1102
+ <<~'RUBY'.code,
1103
+ class String
1104
+ spec "#concat",
1105
+ "".UT(:a).raise?(TypeError, message: /no implicit conversion/i),
1106
+ "".UT(:a).raise?(StandardError, false),
1107
+ coda
1108
+ end
1109
+ RUBY
1110
+ image("Unit test expecting an exception", "spec/unit_test_raise.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1111
+ "The first test succeeded with the raise exception class being `TypeError`, and its message matching the pattern. The second test failed because, although `StandardError` is an ancestor of `TypeError`, the `include_subclass` option was set to `false`.",
1112
+ "Similarly, to check that examining the feature throws an object, use the `throw?(tag [,obj])` method. Suppose the following method is defined:",
1113
+ <<~'RUBY'.code,
1114
+ def throw_check
1115
+ throw(:yes, "correct")
1116
+ end
1117
+ RUBY
1118
+ "Under such situation, the thrown tag and the returned object can be checked as follows:",
1119
+ <<~'RUBY'.code,
1120
+ spec "#throw_check",
1121
+ UT.throw?(:yes),
1122
+ UT.throw?(:no),
1123
+ UT.throw?(:yes, value: "correct"),
1124
+ UT.throw?(:yes, value: "wrong"),
1125
+ coda
1126
+ RUBY
1127
+ image("Unit test expecting throwing", "spec/unit_test_throw.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1128
+ coda
1129
+
1130
+ spec "===Benchmark",
1131
+ "Benchmark is described similarly to unit test, but it only has the setup and exercise steps. For the exercise step, use the placeholder {BasicObject#BM}. Benchmark is meaningful only when there are alternative implementations for the method in question (see {=Alternative Implementations}). Suppose the following three alternative implementations are examined for a feature that parses an integer from a string:",
1132
+ <<~'RUBY'.code,
1133
+ class String
1134
+ def check_int__1
1135
+ Integer(self) rescue false
1136
+ end
1137
+ def check_int__2
1138
+ to_i.to_s == self
1139
+ end
1140
+ def check_int__3
1141
+ self =~ /\A\d+\z/
1142
+ end
1143
+ end
1144
+ RUBY
1145
+ "Under such situation, the following benchmark test:",
1146
+ <<~'RUBY'.code,
1147
+ class String
1148
+ spec "#check_int",
1149
+ "220000".BM,
1150
+ "22.to.2".BM,
1151
+ coda
1152
+ end
1153
+ RUBY
1154
+ "can have a result like:",
1155
+ image("Benchmark test", "spec/benchmark_test.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1156
+ "Based on the result, one may discuss that `check_int__1` performs the best with string expressing valid integers like `\"220000\"`, but its performance is not stable, as can be seen in the slowness with invalid strings like `\"22.to.2\"`.",
1157
+ coda
1158
+
1159
+ spec "===Test Header",
1160
+ "Test items can be grouped into a hierarchy, numbered, and named using *test headers*. A test header is described as a string that starts with one or more question mark characters `?`. Each header is numbered at the depth represented by the number of `?` characters at the beginning. Examples are shown below:",
1161
+ <<~'RUBY'.code,
1162
+ class String
1163
+ spec "#capitalize!",
1164
+ "? When `self` is not capitalized, it should return the capitalized string.",
1165
+ "foo".UT == "Foo",
1166
+ "? When there is nothing to capitalize, it should return `nil`.",
1167
+ "?? When `self` is already capitalized",
1168
+ "Foo".UT == nil,
1169
+ "?? When `self` does not start with a letter",
1170
+ "@foo".UT == nil,
1171
+ coda
1172
+ end
1173
+ RUBY
1174
+ "The result may be displayed as follows:",
1175
+ image("Test header", "spec/test_header.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1176
+ "In future versions of Manager, test header is planned to play a role in doing test setups in hierarchy (cf. nested setup syntax in conventional test frameworks), but its specification is not decided yet. Feedback is welcome.",
1177
+ coda
1178
+
1179
+ spec "===The `expr` Method",
1180
+ "Besides being directly described as objects, the receiver and arguments of tests can be described using the method {Object#expr}, which takes a string argument. The string is evaluated at the timing of the relevant test step. The `expr` method has three purposes:",
1181
+ "# Reset the object in case performing the tests modifies the objects involved",
1182
+ "# Control the inspection form that is displayed in the test reports",
1183
+ "# Refer to objects in the context of the test (such as is defined in a setup, see {=Setup and Teardown})",
1184
+ "As an example of the first purpose, suppose some alternative implementations of a destructive method are defined:",
1185
+ <<~'RUBY'.code,
1186
+ class Array
1187
+ def push_a
1188
+ push("a")
1189
+ end
1190
+ def push_a__length
1191
+ self[length] = "a"
1192
+ self
1193
+ end
1194
+ end
1195
+ RUBY
1196
+ "and they are to be tested. A test simply written with an array literal like this:",
1197
+ <<~'RUBY'.code,
1198
+ class Array
1199
+ spec "#push_a",
1200
+ [].UT == ["a"],
1201
+ coda
1202
+ end
1203
+ RUBY
1204
+ "succeeds for the main implementation `push_a`, but will fail for the alternative implementation `push_a__length` because the same array object described by a literal expression in the spec file is modified during the exercise of `push_a`, then is further used in exercising `push_a__length`:",
1205
+ image("Problem with testing destructive method", "spec/without_expr.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1206
+ "Such problem is avoided by using the `expr` method with the relevant expression. Doing as follows:",
1207
+ <<~'RUBY'.code,
1208
+ class Array
1209
+ spec "#push_a",
1210
+ expr("[]").UT == ["a"],
1211
+ coda
1212
+ end
1213
+ RUBY
1214
+ "makes the test succeed:",
1215
+ image("Using `expr` with destructive method", "spec/with_expr.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1216
+ "The `expr` method can be used:",
1217
+ "* At the receiver and the argument positions of `UT`, `BM`,",
1218
+ <<~'RUBY'.code,
1219
+ expr("[]").UT(expr("{a: 1}")).foo?,
1220
+ expr("Array.new(3)").BM(expr("foo")),
1221
+ RUBY
1222
+ "* At the argument positions of a method within a method chain of verification,",
1223
+ <<~'RUBY'.code,
1224
+ ["a"].UT(:foo).foo?(expr("[\"a\"]")).bar == expr("bar"),
1225
+ RUBY
1226
+ "* At the argument positions of a method within a method chain following `expr`,",
1227
+ <<~'RUBY'.code,
1228
+ expr("[]").expr("aa(1)").UT(expr("{b: :x}").baz.expr("[\"a\"]")).foo?,
1229
+ RECEIVER.foo?(expr("[\"a\"]").bar.expr("baz")),
1230
+ RUBY
1231
+ "* At the argument positions of `expr`,",
1232
+ <<~'RUBY'.code,
1233
+ RETURN.bar?(expr(expr("baz(foo)"), 2)),
1234
+ RUBY
1235
+ "* At the block positions of any of the above. (It should be in the form `&expr(\"{...}\")`.)",
1236
+ <<~'RUBY'.code,
1237
+ [].UT(a: 1, &expr("{|s| s.upcase}")),
1238
+ ["a"].UT(:foo).foo?(["a"], &expr("{|x, y| x + y}")).bar,
1239
+ RECEIVER.foo?(expr("[\"a\"]").bar(5, &expr("{|e| e.baz}"))),
1240
+ RETURN.bar?(expr("baz(foo)", &expr("{|e| e + 2}"))),
1241
+ RUBY
1242
+ "If `expr` appears in other positions, i.e. in an embedded position of these positions, it may not work correctly. The following are illicit uses of `expr`.",
1243
+ <<~'RUBY'.code,
1244
+ ["a", expr("foo")].UT(bar({a: 1})).foo?,
1245
+ ["a", "foo"].UT(bar(expr("{a: 1}"))).foo?,
1246
+ RUBY
1247
+ "As an example of the second purpose, suppose the receiver for test examination is a huge array. The result of a test like:",
1248
+ <<~'RUBY'.code,
1249
+ Array.new(1000){"a"}.UT("b") == Array.new(1000){"a"} + Array.new(1000){"b"},
1250
+ RUBY
1251
+ "would have huge arrays displayed in their inspection form like this:",
1252
+ image("Long inspected expression", "spec/long.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1253
+ "When you do not want an object to be displayed in its expanded and inspected form because it is too long as in this case, or when it would not be readable, you can use the `expr` method. Wrapping the long arrays in the example above with `expr` as in:",
1254
+ <<~'RUBY'.code,
1255
+ expr("Array.new(1000){\"a\"}").UT("b") ==
1256
+ expr("Array.new(1000){\"a\"} + Array.new(1000){\"b\"}"),
1257
+ RUBY
1258
+ "would make the displayed result shorter and understandable. The parts of the expression using `expr` are displayed in the form of the string passed:",
1259
+ image("Display made short with `expr`", "spec/short.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1260
+ coda
1261
+
1262
+ spec "===Setup and Teardown",
1263
+ "The third purpose of using `expr` in tests is to refer to variables and methods that have been set up in advance. A setup can be described as a string followed by the {String#setup} method. The recommended way is to write this as a here document, as with code blocks ({=Code Block}). Setups are accumulated over the spec file, and is evaluated for each exercise step in a test. The {Module#teardown} item will reset all preceding setups. Following is an example of using these methods.",
1264
+ <<~'_'.code,
1265
+ class Array
1266
+ spec "#flatten!",
1267
+ <<~'RUBY'.setup,
1268
+ a1 = [1, 2, 3]
1269
+ a2 = [4, [5, 6]]
1270
+ a3 = [a1, [], a2]
1271
+ RUBY
1272
+ expr('[a1, a2]').UT == [1, 2, 3, 4, 5, 6],
1273
+ RECEIVER == [1, 2, 3, 4, 5, 6],
1274
+ expr('a3').UT == [1, 2, 3, 4, 5, 6],
1275
+ expr('a3').UT(0).nil?,
1276
+ RECEIVER == expr('[a1, [], a2]'),
1277
+ teardown,
1278
+ <<~'RUBY'.setup,
1279
+ a1 = []
1280
+ RUBY
1281
+ expr('a1').UT.nil?,
1282
+ coda
1283
+ end
1284
+ _
1285
+ "This will result in the following:",
1286
+ image("Setup and teardown", "spec/setup_teardown.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"),
1287
+ "Any changes made by `setup` to global variables are not reset by `teardown`, and needs to be undone manually. Not doing so properly can result in Manager not running correctly.",
1288
+ "In future versions of Manager, test setups are planned to be more structured, i.e. in hierarchy as in nested setup syntax in conventional test frameworks, but its specification is not decided yet. Feedback is welcome.",
1289
+ coda