|
2 | 2 |
|
3 | 3 | ## **Do you want to report a bug?** |
4 | 4 |
|
5 | | -- **Ensure the bug was not already reported** by searching the [forum](https://forum.exercism.org/c/programming/moonscript). |
| 5 | +- **Ensure the bug was not already reported** by searching the [forum][forum]. |
6 | 6 |
|
7 | | -- If you're unable to find an open conversation addressing the problem, [open a new one](https://forum.exercism.org/new-topic?category=moonscript). Be sure to include a **title and clear description**, as much relevant information as possible, and (when possible) a **code sample**. |
| 7 | +- If you're unable to find an open conversation addressing the problem, [open a new one][forum-new-topic]. |
| 8 | + Be sure to include a **title and clear description**, as much relevant information as possible, and (when possible) a **code sample**. |
8 | 9 |
|
9 | 10 | ## **Do you want to fix a bug?** |
10 | 11 |
|
11 | | -- **Ensure that the bug is [reported](#do-you-want-to-report-a-bug)**. |
| 12 | +- **Ensure that the bug is reported (see above)**. |
12 | 13 | Only start fixing the bug when there is agreement on whether (and how) it should be fixed. |
13 | 14 |
|
14 | | -- Fix the bug and [submit a Pull Request](https://exercism.org/docs/building/github/contributors-pull-request-guide) to this repository. |
| 15 | +- Fix the bug and [submit a Pull Request][pr-guide] to this repository. |
15 | 16 |
|
16 | 17 | - Ensure the PR description clearly describes the problem and solution. |
17 | 18 | Include a link to the bug's corresponding forum conversation. |
18 | 19 |
|
19 | | -- Before submitting, please read the [Contributors Pull Request Guide](https://exercism.org/docs/building/github/contributors-pull-request-guide) and [Pull Request Guide](https://exercism.org/docs/community/being-a-good-community-member/pull-requests). |
| 20 | +- Before submitting, please read the [Contributors Pull Request Guide][pr-guide] and [Pull Request Guide][pr-other-guide]. |
20 | 21 |
|
21 | 22 | ## **Do you intend to add a new feature or change an existing one?** |
22 | 23 |
|
23 | | -- **Ensure that the feature or change is discussed on the [forum](https://forum.exercism.org/c/programming/moonscript).** |
| 24 | +- **Ensure that the feature or change is discussed on the [forum][forum].** |
24 | 25 | Only start adding the feature or change when there is agreement on whether (and how) it should be added or changed. |
25 | 26 |
|
26 | | -- Add the feature or change and [submit a Pull Request](https://exercism.org/docs/building/github/contributors-pull-request-guide) to this repository. |
| 27 | +- Add the feature or change and [submit a Pull Request][pr-guide] to this repository. |
27 | 28 |
|
28 | 29 | - Ensure the PR description clearly describes the problem and solution. |
29 | 30 | Include a link to the bug's corresponding forum conversation. |
30 | 31 |
|
31 | | -- Before submitting, please read the [Contributors Pull Request Guide](https://exercism.org/docs/building/github/contributors-pull-request-guide) and [Pull Request Guide](https://exercism.org/docs/community/being-a-good-community-member/pull-requests). |
| 32 | +- Before submitting, please read the [Contributors Pull Request Guide][pr-guide] and [Pull Request Guide][pr-other-guide]. |
32 | 33 |
|
33 | 34 | ## **Do you want to add an exercise?** |
34 | 35 |
|
35 | 36 | - **Ensure that someone else isn't already adding it** |
36 | | - - start with the [Practice exercises to implement](https://github.com/exercism/moonscript/issues/102) issue. |
37 | | - - also search the [forum](https://forum.exercism.org/c/programming/moonscript) and the repository's [issues](https://github.com/exercism/moonscript/issues) and [pull requests](https://github.com/exercism/moonscript/pulls). |
| 37 | + - start with the [Practice exercises to implement][exercise-list] issue. |
| 38 | + - also search the [forum][forum] and the repository's [issues][gh-issues] and [pull requests][gh-pulls]. |
38 | 39 |
|
39 | | -- If nobody is yet adding the exercise, [open a conversation](https://forum.exercism.org/c/programming/moonscript) and indicate you'd like to add the exercise. |
| 40 | +- If nobody is yet adding the exercise, [open a conversation][forum] and indicate you'd like to add the exercise. |
40 | 41 |
|
41 | 42 | ### Creating a new Practice Exercise |
42 | 43 |
|
|
67 | 68 |
|
68 | 69 | - If yes: |
69 | 70 |
|
70 | | - 1. Edit the spec_generator. |
71 | | -
|
72 | | - This is a MoonScript module that returns a table with 2 or 3 elements: |
73 | | -
|
74 | | - - (required) _one_ of the following |
75 | | - - `module_name`: (string) this is the left-hand side of `${module_name} = require '${slug_name}'` in the test file |
76 | | - - `module_imports`: (list of strings) this is the list of names that appear in `import ${names} from require '${slug_name}'` in the test file |
77 | | - - (required) `generate_test`: this is a function that creates the body of each test case. It takes 2 parameters: |
78 | | - - `case` is the Lua object for the test case |
79 | | - - `level`, default value 2, is the indentation level of the body. |
80 | | - - (optional) `test_helpers`: (string) a block of code that gets added at the top of the top-level `describe` block. |
81 | | - - (optional) `exclusions`: (list of tables) identifies things from the canonical data to exclude. |
82 | | - - See `simple-linked-list` and `gigasecond` spec generators for examples. |
83 | | -
|
84 | | - Look to see how it's implemented for other exercises. |
85 | | - The `space-age` one is interesting: it uses the test_helpers block to register a custom assertion, and has expected errors. |
86 | | - |
| 71 | + 1. Edit the spec_generator -- see below for more details. |
87 | 72 |
|
88 | 73 | 2. Run the generator script and review the new test suite. |
89 | 74 |
|
|
95 | 80 |
|
96 | 81 | 1. Create the example solution: `.meta/example.moon` |
97 | 82 |
|
98 | | - * follow the [style guide](./STYLE.md). |
| 83 | + * follow the [style guide][style]. |
99 | 84 |
|
100 | 85 | 1. Test it with |
101 | 86 |
|
|
113 | 98 | 1. Revisit the exercise difficulty in config.json if the implementation was harder/easier than expected. |
114 | 99 |
|
115 | 100 | 1. Run `bin/configlet lint` to ensure that the new exercise conforms to Exercism standards. |
| 101 | + |
| 102 | +### The spec_generator |
| 103 | + |
| 104 | +The spec_generator.moon file is a MoonScript module containing the functions necessary to generate the test suite for practice exercises. |
| 105 | + |
| 106 | +It is located in `exercises/practice/${slug_name}/.meta/` directory. |
| 107 | + |
| 108 | +It's a module that returns a table. |
| 109 | +
|
| 110 | +#### Elements of returned table |
| 111 | +
|
| 112 | +- (required) _one_ of the following |
| 113 | + - `module_name`: (string) this is the left-hand side of `${module_name} = require '${slug_name}'` in the test file |
| 114 | + - `module_imports`: (list of strings) this is the list of names that appear in `import ${names} from require '${slug_name}'` in the test file |
| 115 | +- (required) `generate_test`: this is a function that creates the body of each test case. It takes 2 parameters: |
| 116 | + - `case` is the Lua object for one test case taken from the canonical data. |
| 117 | + - `level`, default value 2, is the indentation level of the body. |
| 118 | + MoonScript is a whitespace-sensitive language, so we need to be careful about indenting the test functions properly. |
| 119 | +- (optional) `test_helpers`: (string) a block of code that gets added at the top of the top-level `describe` block. |
| 120 | + This might include required modules used in the tests, or helper functions, or (more interstingly) custom assertions, or even just helpful comments for students. |
| 121 | +- (optional) `exclusions`: (list of tables) identifies things from the canonical data to exclude. |
| 122 | + Normally, setting `include = false` in the tests.toml file is the preferred way to exclude individual tests, but sometimes you want to exclude a whole block of tests. |
| 123 | + - Examples: [`simple-linked-list`][simple-linked-list], [`gigasecond`][gigasecond] |
| 124 | +- (optional) `bonus`: (string) bonus tests to add to the spec file as extra challenge for the students. |
| 125 | + These tests are not executed by the test runner. |
| 126 | + - Examples: [`robot-name`][robot-name], [`custom-set`][custom-set] |
| 127 | +
|
| 128 | +##### Examples of custom assertions |
| 129 | +
|
| 130 | +- [`dnd-character`][dnd-character] -- `assert.between value, min, max` |
| 131 | +- [`space-age`][space-age] -- `assert.approx_equal #{case.expected}, result` |
| 132 | +- [`word-count`][word-count] -- `assert.has.same_kv result, expected` |
| 133 | +
|
| 134 | +#### Helper functions for formatting test cases |
| 135 | +
|
| 136 | +##### Functions in bin/generate-spec |
| 137 | +
|
| 138 | +These functions are exported from [`bin/generate-spec`][generate-spec-exported] for use in spec_generator modules |
| 139 | +
|
| 140 | +- `indent(text, level)` -- provide leading whitespace to the appropriate level. |
| 141 | +- `quote(str)` -- add quotation marks to the string, single or double as appropriate. |
| 142 | +- `is_empty(tbl)` -- predicate: is the table empty |
| 143 | +- `is_json_null(value)` -- predicate: is the value `json.null` from dkjson |
| 144 | +- `contains(tbl, value)` -- predicate: does the table contain the value (uses `==`) |
| 145 | +
|
| 146 | +##### Helper functions |
| 147 | +
|
| 148 | +We have a library of helper functions, useful for generating pretty tables mostly. |
| 149 | +
|
| 150 | +For example, to nicely format a list of words, or a list of strings over multiple lines, or to recursively format nested tables. |
| 151 | +
|
| 152 | +**Look in [`lib/test_helpers.moon`][test-helpers].** |
| 153 | +
|
| 154 | +Example: |
| 155 | +
|
| 156 | +```moonscript |
| 157 | +import int_list, string_list from require 'test_helpers' |
| 158 | +... |
| 159 | +{ |
| 160 | + generate_test: (case, level) -> |
| 161 | + lines = { |
| 162 | + "input = #{int_list case.input.numbers}" |
| 163 | + "expected = #{string_list case.expected, level}" |
| 164 | + ... |
| 165 | +``` |
| 166 | +
|
| 167 | +##### Comparing tables deeply |
| 168 | +
|
| 169 | +The `assert.are.same t1, t2` assertion is used to compare tables deeply. |
| 170 | +
|
| 171 | +<details><summary> |
| 172 | +However when they don't match, by default busted does not show the whole object, which makes it hard for the student to see the difference. |
| 173 | +</summary> |
| 174 | + |
| 175 | +Consider this `busted` output where something is different in the `...more` sections. |
| 176 | + |
| 177 | +```none |
| 178 | +Failure → ./rest_api_spec.moon @ 251 |
| 179 | +rest-api iou lender owes borrower less than new loan |
| 180 | +...uarocks/lib/luarocks/rocks-5.4/busted/2.3.0-1/bin/busted:7: Expected objects to be the same. |
| 181 | +Passed in: |
| 182 | +(table: 0x641e0548a540) { |
| 183 | + *[users] = { |
| 184 | + [1] = { |
| 185 | + [balance] = 1.0 |
| 186 | + [name] = 'Adam' |
| 187 | + [owed_by] = { ... more } |
| 188 | + [owes] = { } } |
| 189 | + *[2] = { |
| 190 | + [balance] = -1.0 |
| 191 | + [name] = 'Bob' |
| 192 | + [owed_by] = { } |
| 193 | + *[owes] = { ... more } } } } |
| 194 | +Expected: |
| 195 | +(table: 0x641e0548a620) { |
| 196 | + *[users] = { |
| 197 | + [1] = { |
| 198 | + [balance] = 1.0 |
| 199 | + [name] = 'Adam' |
| 200 | + [owed_by] = { ... more } |
| 201 | + [owes] = { } } |
| 202 | + *[2] = { |
| 203 | + [balance] = -1.0 |
| 204 | + [name] = 'Bob' |
| 205 | + [owed_by] = { } |
| 206 | + *[owes] = { ... more } } } } |
| 207 | +``` |
| 208 | +</details> |
| 209 | +
|
| 210 | +The solution here is to configure the `assert` object,. |
| 211 | +In the spec_generator.moon module, add this: |
| 212 | +
|
| 213 | +```none |
| 214 | + -- we have deep tables to compare, display it all when not the same |
| 215 | + test_helpers: [[ |
| 216 | + assert\set_parameter "TableFormatLevel", 4 |
| 217 | +]] |
| 218 | +``` |
| 219 | +
|
| 220 | +Here, the value `4` was chosen to reflect the max depth of the expected value: |
| 221 | +
|
| 222 | +```none |
| 223 | +...1...2...3...4 |
| 224 | +{ |
| 225 | + users: { |
| 226 | + { |
| 227 | + owed_by: { |
| 228 | + Bob: 3.0 |
| 229 | + } |
| 230 | + balance: 3.0 |
| 231 | + owes: {} |
| 232 | + name: "Adam" |
| 233 | + }, |
| 234 | + ... |
| 235 | +``` |
| 236 | +
|
| 237 | +
|
| 238 | +[forum]: https://forum.exercism.org/c/programming/moonscript |
| 239 | +[forum-new-topic]: https://forum.exercism.org/new-topic?category=moonscript |
| 240 | +[pr-guide]: https://exercism.org/docs/building/github/contributors-pull-request-guide |
| 241 | +[pr-other-guide]: https://exercism.org/docs/community/being-a-good-community-member/pull-requests |
| 242 | +[exercise-list]: https://github.com/exercism/moonscript/issues/102 |
| 243 | +[gh-issues]: https://github.com/exercism/moonscript/issues |
| 244 | +[gh-pulls]: https://github.com/exercism/moonscript/pulls |
| 245 | +[style]: ./STYLE.md |
| 246 | +[generate-spec-exported]: ./bin/generate-spec#L51 |
| 247 | +[test-helpers]: ./lib/test_helpers.moon |
| 248 | +[space-age]: ./exercises/practice/space-age/.meta/spec_generator.moon |
| 249 | +[word-count]: ./exercises/practice/word-count/.meta/spec_generator.moon |
| 250 | +[dnd-character]: ./exercises/practice/dnd-character/.meta/spec_generator.moon |
| 251 | +[gigasecond]: ./exercises/practice/gigasecond/.meta/spec_generator.moon |
| 252 | +[simple-linked-list]: ./exercises/practice/simple-linked-list/.meta/spec_generator.moon |
| 253 | +[custom-set]: ./exercises/practice/custom-set/.meta/spec_generator.moon#L42 |
| 254 | +[robot-name]: ./exercises/practice/robot-name/robot_name_spec.moon#L59 |
0 commit comments