# frozen_string_literal: false spec "=License", "The MIT License (MIT)", "Copyright (c) 2014–2016 sawa", "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:", "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", "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.", coda spec "=Overview", "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.", "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.", "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.", "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.", "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.", "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).", coda spec "==Simple Example", "Suppose we have the following problem:", "> 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`.", "Let us start by writing a test. Create a new file named `test.rb`, and write the following line at the top:", <<~'RUBY'.code, # frozen_string_literal: false RUBY "Under it, we shall describe a specification for the instance method `same_counts` on `Array`. We write the following:", <<~'RUBY'.code, class Array spec "#same_counts", coda end RUBY "Now we are ready to fill in some unit tests inside the `spec` ... `coda`. The first test may be a trivial case:", <<~'RUBY'.code, ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true, RUBY "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:", <<~'RUBY'.code, ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true, RUBY "To make sure that not only the inventory of the elements but also the number of them matters, we can a test like this:", <<~'RUBY'.code, ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false, RUBY "Now, the file `test.rb` should look like:", <<~'RUBY'.code, # frozen_string_literal: false class Array spec "#same_counts", ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true, ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true, ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false, coda end RUBY "Let us run this on Manager. On the command line, move to the directory we wrote the `test.rb` file, and type:", <<~'BASH'.code(:bash), manager test.rb BASH "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:", image("Simple example step 1", "spec/tutorial_1.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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.", "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:", <<~'RUBY'.code, __END__ RUBY "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:", <<~'RUBY'.code, # frozen_string_literal: false class Array spec "#same_counts", ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true, ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true, ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false, coda end __END__ class Array def same_counts other sort == other.sort end end RUBY "After we run the same command from the terminal, a portion of `CHART.html` should have now changed to this:", image("Simple example step 2", "spec/tutorial_2.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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.", "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:", <<~'RUBY'.code, class Array def same_counts other sort == other.sort end def same_counts__using_hash other inject(Hash.new(0)){|h, e| h[e] += 1} == other.inject(Hash.new(0)){|h, e| h[e] += 1} end end RUBY "Let us run the command again, and see what happened.", image("Simple example step 3", "spec/tutorial_3.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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:", <<~'RUBY'.code, class Array def same_counts other sort == other.sort end def same_counts__using_hash other inject(Hash.new(0)){|h, e| h[e] += 1; h} == other.inject(Hash.new(0)){|h, e| h[e] += 1; h} end def same_counts__using_group_by other group_by(&:itself) == other.group_by(&:itself) end end RUBY "And then we run Manager again:", image("Simple example step 4", "spec/tutorial_4.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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:", <<~'RUBY'.code, class Array spec "#same_counts", ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true, ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true, ["a", "b", "c"].UT(["a", "b", "b", "c", "c", "c"]) == false, ["a", "b", "b", "c", "c", "c"].BM(["a", "b", "b", "c", "c", "c"]), ["a", "b", "b", "c", "c", "c"].BM(["c", "b", "c", "b", "c", "a"]), ["a", "b", "c"].BM(["a", "b", "b", "c", "c", "c"]), coda end RUBY "Running Manger will take more time because of the benchmark tests performed. Now we see benchmark reports under the unit test reports:", image("Simple example step 5", "spec/tutorial_5.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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.", "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`,", image("Simple example step 6", "spec/tutorial_6.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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:", <<~'RUBY'.code, class Array spec "#same_counts", "Takes another array, and compares it with `self` whether the counts of all elements are the same.", ["a", "b", "b", "c", "c", "c"].UT(["a", "b", "b", "c", "c", "c"]) == true, ["a", "b", "b", "c", "c", "c"].UT(["c", "b", "c", "b", "c", "a"]) == true, ... coda end RUBY "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:", image("Simple example step 7", "spec/tutorial_7.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "A method signature is expressed as a hash. We should add a hash like this before the description we have just added:", <<~'RUBY'.code, spec "#same_counts", {"(other)" => value(true) | value(false)}, "Takes another array, and compares it with `self` whether the counts of all elements are the same.", ... RUBY "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:", image("Simple example step 8", "spec/tutorial_8.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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`.", coda spec "==Download", "To install Manager, type the following:", <<~'BASH'.code(:bash), $ sudo gem install manager BASH "To access the source, goto: {https://github.com/sawa/manager}", "Your contribution is welcome. Especially, the followings are wanted:", "* A professional quality theme for Manager (Cf. {=Theme (theme, theme-list)})", "* Opinions on structured setup for tests.", "If you are willing to make a contribution, please post an issue on {https://github.com/sawa/manager}. However, not all posts are accepted. It is up to the desicion of the author.", coda spec "==Release Notes", "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.", "* 0.1.0", "** Initial release.", "* 0.1.1", "** Changed homepage, etc.", coda spec "=Viewing The Files", "Manager generates two HTML format files: a *user's manual* and a *developer's chart*. They are to be viewed on a web browser.", coda spec "==User's Manual", "User's manual includes *documentation* about described or implemented modules and features. It is intended to be read by the users of the software.", coda spec "===Main Information", "The *main information* is displayed in three levels:", "* *Modules* (including classes)", "* *Features* (i.e., constants, singleton methods, instance methods, see {=Feature}) and *section headers* (see {=Section Header})", "* *Items* (contents)", "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.", "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.", "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.", coda spec "===Top Bar", "At the top of the user's manual is the *top bar*, which has buttons and information to help users navigate through the file.", "* “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.", "* 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.", "* 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.", "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:", image("Navigation", "spec/navigation.png", width: "100%", border: "1px solid hsl(0, 0%, 80%)"), "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.", coda spec "==Developer's Chart", "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.", coda spec "===Left Panel", "“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}).", "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.", "The “Files” table lists information about the relevant files.", "* “Category”: Files are classified into the following.", "** “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.", "** “Specification”: The subject of description/tests. Also called *spec* in this manual/chart.", "** “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.", # "* “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.", "* “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.", coda spec "===Main Information", "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}).", "* “Undocumented”: The feature is implemented in the program code, but is not described in the spec file.", "* “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.", "* “Misplaced”: The feature is under one or more of the following three occasions. This tag is suppressed when the feature is marked as “moved”.", "** The feature (method) is implemented within a file that is not listed (see {=Reference to Program Code} on how to list a feature).", "** The feature is implemented on an ancestor module rather than in the module in which it is described in the spec file.", "** The feature (method) is described in the spec file, but is implemented as an alias of another method.", "* “moved”: The feature is marked as to be described in a manner different from its implementation. Suppresses “Misplaced” tag.", coda spec "====Module Diagrams", "Each module section has two *module diagrams*. For example, the following may appear under the section of a class `B`:", image("Module diagrams", "spec/module_diagram.png", border: "1px solid hsl(0, 0%, 80%)"), "The first diagram represents mixins and ancestors related to `B` itself, and the second one is for its singleton class `<