trackler 2.2.1.43 → 2.2.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/lens-person/description.md +1 -1
  4. data/problem-specifications/exercises/palindrome-products/description.md +5 -12
  5. data/problem-specifications/exercises/pascals-triangle/canonical-data.json +1 -7
  6. data/tracks/c/exercises/meetup/src/example.c +64 -60
  7. data/tracks/c/exercises/meetup/src/example.h +2 -2
  8. data/tracks/c/exercises/meetup/test/test_meetup.c +4 -4
  9. data/tracks/cfml/config.json +179 -169
  10. data/tracks/cfml/exercises/markdown/.meta/HINTS.md +0 -0
  11. data/tracks/cfml/exercises/markdown/Markdown.cfc +121 -0
  12. data/tracks/cfml/exercises/markdown/MarkdownTest.cfc +51 -0
  13. data/tracks/cfml/exercises/markdown/Solution.cfc +204 -0
  14. data/tracks/cfml/exercises/markdown/SolutionTest.cfc +7 -0
  15. data/tracks/cfml/exercises/markdown/TestRunner.cfc +103 -0
  16. data/tracks/cfml/exercises/markdown/box.json +8 -0
  17. data/tracks/cfml/exercises/markdown/index.cfm +37 -0
  18. data/tracks/csharp/exercises/dominoes/DominoesTest.cs +40 -45
  19. data/tracks/csharp/exercises/meetup/MeetupTest.cs +761 -83
  20. data/tracks/csharp/generators/Exercises/Dominoes.cs +33 -0
  21. data/tracks/csharp/generators/Exercises/Meetup.cs +46 -0
  22. data/tracks/csharp/generators/Output/FormattingExtensions.cs +11 -1
  23. data/tracks/csharp/generators/Output/TypesExtensions.cs +23 -0
  24. data/tracks/csharp/generators/Output/ValueFormatter.cs +70 -69
  25. data/tracks/dart/config.json +15 -3
  26. data/tracks/dart/exercises/word-count/README.md +18 -0
  27. data/tracks/dart/exercises/word-count/lib/example.dart +26 -0
  28. data/tracks/dart/exercises/word-count/lib/word_count.dart +3 -0
  29. data/tracks/dart/exercises/word-count/pubspec.lock +293 -0
  30. data/tracks/dart/exercises/word-count/pubspec.yaml +3 -0
  31. data/tracks/dart/exercises/word-count/test/word_count_test.dart +64 -0
  32. data/tracks/ecmascript/docs/INSTALLATION.md +4 -7
  33. data/tracks/elixir/exercises/hello-world/README.md +1 -26
  34. data/tracks/elixir/exercises/hello-world/example.exs +3 -7
  35. data/tracks/elixir/exercises/hello-world/hello_world.exs +3 -25
  36. data/tracks/elixir/exercises/hello-world/hello_world_test.exs +2 -13
  37. data/tracks/elixir/mix.lock +1 -1
  38. data/tracks/elm/config.json +11 -0
  39. data/tracks/elm/exercises/pascals-triangle/README.md +51 -0
  40. data/tracks/elm/exercises/pascals-triangle/Triangle.elm +1 -0
  41. data/tracks/elm/exercises/pascals-triangle/Triangle.example.elm +23 -0
  42. data/tracks/elm/exercises/pascals-triangle/elm-package.json +14 -0
  43. data/tracks/elm/exercises/pascals-triangle/package.json +14 -0
  44. data/tracks/elm/exercises/pascals-triangle/tests/Tests.elm +34 -0
  45. data/tracks/elm/exercises/pascals-triangle/tests/elm-package.json +16 -0
  46. data/tracks/fsharp/exercises/grade-school/GradeSchool.fs +2 -2
  47. data/tracks/fsharp/exercises/grade-school/GradeSchoolTest.fs +3 -2
  48. data/tracks/java/CONTRIBUTING.md +34 -0
  49. data/tracks/java/config.json +42 -6
  50. data/tracks/java/exercises/largest-series-product/src/main/java/LargestSeriesProductCalculator.java +7 -3
  51. data/tracks/java/exercises/matrix/src/main/java/Matrix.java +23 -0
  52. data/tracks/julia/config.json +11 -2
  53. data/tracks/julia/exercises/grains/README.md +33 -0
  54. data/tracks/julia/exercises/grains/example.jl +23 -0
  55. data/tracks/julia/exercises/grains/grains.jl +9 -0
  56. data/tracks/julia/exercises/grains/runtests.jl +27 -0
  57. data/tracks/lua/README.md +1 -1
  58. data/tracks/mips/exercises/octal/README.md +3 -8
  59. data/tracks/ocaml/exercises/palindrome-products/README.md +8 -14
  60. data/tracks/perl6/.travis.yml +7 -3
  61. data/tracks/perl6/bin/exercise-gen.pl6 +5 -1
  62. data/tracks/perl6/exercises/accumulate/README.md +0 -3
  63. data/tracks/perl6/exercises/accumulate/accumulate.t +9 -3
  64. data/tracks/perl6/exercises/all-your-base/README.md +1 -1
  65. data/tracks/perl6/exercises/all-your-base/all-your-base.t +23 -11
  66. data/tracks/perl6/exercises/all-your-base/example.yaml +1 -1
  67. data/tracks/perl6/exercises/allergies/README.md +0 -1
  68. data/tracks/perl6/exercises/allergies/allergies.t +22 -10
  69. data/tracks/perl6/exercises/anagram/anagram.t +22 -10
  70. data/tracks/perl6/exercises/atbash-cipher/README.md +2 -1
  71. data/tracks/perl6/exercises/atbash-cipher/atbash-cipher.t +22 -10
  72. data/tracks/perl6/exercises/binary/README.md +2 -0
  73. data/tracks/perl6/exercises/bob/bob.t +24 -11
  74. data/tracks/perl6/exercises/bob/example.yaml +2 -2
  75. data/tracks/perl6/exercises/clock/clock.t +22 -10
  76. data/tracks/perl6/exercises/flatten-array/README.md +1 -2
  77. data/tracks/perl6/exercises/flatten-array/example.yaml +1 -1
  78. data/tracks/perl6/exercises/flatten-array/flatten-array.t +23 -11
  79. data/tracks/perl6/exercises/grade-school/README.md +0 -1
  80. data/tracks/perl6/exercises/grade-school/grade-school.t +9 -3
  81. data/tracks/perl6/exercises/grains/README.md +0 -1
  82. data/tracks/perl6/exercises/grains/grains.t +22 -10
  83. data/tracks/perl6/exercises/hello-world/example.yaml +3 -3
  84. data/tracks/perl6/exercises/hello-world/hello-world.t +25 -12
  85. data/tracks/perl6/exercises/leap/README.md +1 -1
  86. data/tracks/perl6/exercises/leap/example.yaml +1 -1
  87. data/tracks/perl6/exercises/leap/leap.t +23 -11
  88. data/tracks/perl6/exercises/linked-list/README.md +10 -10
  89. data/tracks/perl6/exercises/linked-list/linked-list.t +9 -3
  90. data/tracks/perl6/exercises/luhn/README.md +7 -7
  91. data/tracks/perl6/exercises/luhn/luhn.t +22 -10
  92. data/tracks/perl6/exercises/phone-number/README.md +3 -2
  93. data/tracks/perl6/exercises/phone-number/phone-number.t +22 -10
  94. data/tracks/perl6/exercises/raindrops/raindrops.t +22 -10
  95. data/tracks/perl6/exercises/rna-transcription/rna-transcription.t +22 -10
  96. data/tracks/perl6/exercises/robot-name/robot-name.t +9 -3
  97. data/tracks/perl6/exercises/scrabble-score/README.md +3 -1
  98. data/tracks/perl6/exercises/scrabble-score/scrabble-score.t +22 -10
  99. data/tracks/perl6/exercises/space-age/README.md +2 -1
  100. data/tracks/perl6/exercises/space-age/space-age.t +22 -10
  101. data/tracks/perl6/exercises/trinary/README.md +1 -1
  102. data/tracks/perl6/exercises/word-count/README.md +1 -2
  103. data/tracks/perl6/exercises/word-count/word-count.t +22 -10
  104. data/tracks/perl6/exercises/wordy/README.md +0 -5
  105. data/tracks/perl6/exercises/wordy/wordy.t +22 -10
  106. data/tracks/perl6/templates/test.mustache +26 -15
  107. data/tracks/python/config.json +10 -0
  108. data/tracks/python/exercises/book-store/book_store.py +1 -1
  109. data/tracks/python/exercises/flatten-array/flatten_array.py +1 -1
  110. data/tracks/python/exercises/matrix/matrix.py +1 -1
  111. data/tracks/python/exercises/palindrome-products/palindrome_products.py +2 -2
  112. data/tracks/python/exercises/perfect-numbers/perfect_numbers.py +2 -2
  113. data/tracks/python/exercises/pythagorean-triplet/pythagorean_triplet.py +3 -3
  114. data/tracks/python/exercises/raindrops/raindrops.py +1 -1
  115. data/tracks/python/exercises/scale-generator/scale_generator.py +1 -1
  116. data/tracks/python/exercises/sieve/sieve.py +1 -1
  117. data/tracks/python/exercises/sublist/sublist.py +1 -1
  118. data/tracks/python/exercises/transpose/transpose.py +1 -1
  119. data/tracks/python/exercises/triangle/triangle.py +4 -1
  120. data/tracks/python/exercises/two-bucket/example.py +59 -0
  121. data/tracks/python/exercises/two-bucket/two_bucket.py +2 -0
  122. data/tracks/python/exercises/two-bucket/two_bucket_test.py +32 -0
  123. data/tracks/scheme/config.json +59 -89
  124. data/tracks/scheme/config/maintainers.json +11 -11
  125. data/tracks/sml/exercises/allergies/README.md +20 -5
  126. data/tracks/sml/exercises/allergies/allergies.sml +11 -14
  127. data/tracks/sml/exercises/allergies/example.sml +9 -9
  128. data/tracks/sml/exercises/allergies/test.sml +65 -129
  129. data/tracks/sml/exercises/allergies/testlib.sml +159 -0
  130. data/tracks/sml/exercises/hamming/README.md +21 -5
  131. data/tracks/sml/exercises/hamming/example.sml +14 -10
  132. data/tracks/sml/exercises/hamming/hamming.sml +4 -3
  133. data/tracks/sml/exercises/hamming/test.sml +55 -64
  134. data/tracks/sml/exercises/hamming/testlib.sml +159 -0
  135. data/tracks/typescript/config.json +15 -0
  136. data/tracks/typescript/exercises/linked-list/linked-list.test.ts +11 -11
  137. data/tracks/typescript/exercises/nth-prime/README.md +36 -0
  138. data/tracks/typescript/exercises/nth-prime/nth-prime.example.ts +28 -0
  139. data/tracks/typescript/exercises/nth-prime/nth-prime.test.ts +25 -0
  140. data/tracks/typescript/exercises/nth-prime/nth-prime.ts +0 -0
  141. data/tracks/typescript/exercises/nth-prime/package.json +36 -0
  142. data/tracks/typescript/exercises/nth-prime/tsconfig.json +22 -0
  143. data/tracks/typescript/exercises/nth-prime/tslint.json +127 -0
  144. data/tracks/typescript/exercises/nth-prime/yarn.lock +2305 -0
  145. data/tracks/typescript/exercises/prime-factors/README.md +1 -1
  146. data/tracks/typescript/exercises/prime-factors/prime-factors.test.ts +11 -11
  147. data/tracks/typescript/exercises/series/README.md +1 -1
  148. data/tracks/typescript/exercises/series/series.test.ts +11 -11
  149. data/tracks/typescript/img/icon.png +0 -0
  150. data/tracks/typescript/img/icon.svg +3 -8
  151. metadata +44 -2
File without changes
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Your implmentation of the Markdown exercise
3
+ */
4
+ component {
5
+
6
+ /**
7
+ * @returns
8
+ */
9
+ function parse(required string input) {
10
+
11
+ var lines = listToArray( arguments.input, chr( 10 ) );
12
+ var output = [];
13
+
14
+ var isInList = false;
15
+
16
+ for (var line in lines) {
17
+ var matches = reMatchNoCase( "^[##]+(.*)", line);
18
+ if ( matches.len() ) {
19
+ var hLevel = reMatchNoCase( "[##]+", matches[1])[1];
20
+ line = "<h#hLevel.len()#>" & trim( replace( matches[1], hLevel, "" ) ) & "</h#hLevel.len()#>";
21
+ }
22
+
23
+ matches = reMatchNoCase( "\*(.*)", line);
24
+ if ( matches.len() ) {
25
+ if ( !isInList ) {
26
+ isInList = true;
27
+ var isBold = false;
28
+ var isItalic = false;
29
+
30
+ var matches2 = reFindNoCase( "(.*)__(.*)__(.*)", matches[1], 0, true );
31
+ if ( matches2.len[1] ) {
32
+ var match1 = mid(line, matches2.pos[2], matches2.len[2]);
33
+ var match2 = mid(line, matches2.pos[3], matches2.len[3]);
34
+ var match3 = mid(line, matches2.pos[4], matches2.len[4]);
35
+ matches[1] = match1 & "<strong>" & match2 & "</strong>" & match3;
36
+ isBold = true;
37
+ }
38
+
39
+ var matches3 = reFindNoCase( "(.*)_(.*)_(.*)", matches[1], 0, true );
40
+ if ( matches3.len[1] ) {
41
+ var match1 = mid(line, matches3.pos[2], matches3.len[2]);
42
+ var match2 = mid(line, matches3.pos[3], matches3.len[3]);
43
+ var match3 = mid(line, matches3.pos[4], matches3.len[4]);
44
+ matches[1] = match1 & "<em>" & match2 & "</em>" & match3;
45
+ isItalic = true;
46
+ }
47
+
48
+ if ( isItalic || isBold ) {
49
+ line = "<ul><li>" & trim( replace( matches[1], "*", "", "all" ) ) & "</li>";
50
+ }
51
+ else {
52
+ line = "<ul><li><p>" & trim( replace( matches[1], "*", "", "all" ) ) & "</p></li>";
53
+ }
54
+
55
+ }
56
+ else {
57
+ var isBold = false;
58
+ var isItalic = false;
59
+
60
+ var matches2 = reFindNoCase( "(.*)__(.*)__(.*)", matches[1], 0, true );
61
+ if ( matches2.len[1] ) {
62
+ var match1 = mid(line, matches2.pos[2], matches2.len[2]);
63
+ var match2 = mid(line, matches2.pos[3], matches2.len[3]);
64
+ var match3 = mid(line, matches2.pos[4], matches2.len[4]);
65
+ matches[1] = match1 & "<strong>" & match2 & "</strong>" & match3;
66
+ isBold = true;
67
+ }
68
+
69
+ var matches3 = reFindNoCase( "(.*)_(.*)_(.*)", matches[1], 0, true );
70
+ if ( matches3.len[1] ) {
71
+ var match1 = mid(line, matches3.pos[2], matches3.len[2]);
72
+ var match2 = mid(line, matches3.pos[3], matches3.len[3]);
73
+ var match3 = mid(line, matches3.pos[4], matches3.len[4]);
74
+ matches[1] = match1 & "<em>" & match2 & "</em>" & match3;
75
+ isItalic = true;
76
+ }
77
+
78
+ if ( isItalic || isBold ) {
79
+ line = "<li>" & trim( replace( matches[1], "*", "", "all" ) ) & "</li>";
80
+ }
81
+ else {
82
+ line = "<li><p>" & trim( replace( matches[1], "*", "", "all" ) ) & "</p></li>";
83
+ }
84
+ }
85
+ }
86
+ else {
87
+ if ( isInList ) {
88
+ line = "</ul>" & line;
89
+ isInList = false;
90
+ }
91
+ }
92
+
93
+ if ( !reMatchNoCase( "<h|<ul|<p|<li", line ).len() ) {
94
+ line = "<p>#line#</p>";
95
+ }
96
+
97
+ var matches = reFindNoCase( "(.*)__(.*)__(.*)", line, 0, true );
98
+ if ( matches.len[1] ) {
99
+ var match1 = mid(line, matches.pos[2], matches.len[2]);
100
+ var match2 = mid(line, matches.pos[3], matches.len[3]);
101
+ var match3 = mid(line, matches.pos[4], matches.len[4]);
102
+ line = match1 & "<strong>" & match2 & "</strong>" & match3;
103
+ }
104
+
105
+ var matches = reFindNoCase( "(.*)_(.*)_(.*)", line, 0, true );
106
+ if ( matches.len[1] ) {
107
+ var match1 = mid(line, matches.pos[2], matches.len[2]);
108
+ var match2 = mid(line, matches.pos[3], matches.len[3]);
109
+ var match3 = mid(line, matches.pos[4], matches.len[4]);
110
+ line = match1 & "<em>" & match2 & "</em>" & match3;
111
+ }
112
+ output.append( line );
113
+ }
114
+ var html = arrayToList( output, "" );
115
+ if ( isInList ) {
116
+ html &= "</ul>";
117
+ }
118
+
119
+ return html;
120
+ }
121
+ }
@@ -0,0 +1,51 @@
1
+ component extends="testbox.system.BaseSpec" {
2
+
3
+ function beforeAll(){
4
+ SUT = createObject( 'Markdown' );
5
+ }
6
+
7
+ function run(){
8
+
9
+ describe( "My Markdown class", function(){
10
+
11
+ it( 'parses normal text as a paragraph', function(){
12
+ expect( SUT.parse( input='This will be a paragraph' ) ).toBe( '<p>This will be a paragraph</p>' );
13
+ });
14
+
15
+ it( 'parsing italics', function(){
16
+ expect( SUT.parse( input='_This will be italic_' ) ).toBe( '<p><em>This will be italic</em></p>' );
17
+ });
18
+
19
+ it( 'parsing bold text', function(){
20
+ expect( SUT.parse( input='__This will be bold__' ) ).toBe( '<p><strong>This will be bold</strong></p>' );
21
+ });
22
+
23
+ it( 'mixed normal, italics and bold text', function(){
24
+ expect( SUT.parse( input='This will _be_ __mixed__' ) ).toBe( '<p>This will <em>be</em> <strong>mixed</strong></p>' );
25
+ });
26
+
27
+ it( 'with h1 header level', function(){
28
+ expect( SUT.parse( input='## This will be an h1' ) ).toBe( '<h1>This will be an h1</h1>' );
29
+ });
30
+
31
+ it( 'with h2 header level', function(){
32
+ expect( SUT.parse( input='#### This will be an h2' ) ).toBe( '<h2>This will be an h2</h2>' );
33
+ });
34
+
35
+ it( 'with h6 header level', function(){
36
+ expect( SUT.parse( input='############ This will be an h6' ) ).toBe( '<h6>This will be an h6</h6>' );
37
+ });
38
+
39
+ it( 'unordered lists', function(){
40
+ expect( SUT.parse( input='* Item 1#chr( 10 )#* Item 2' ) ).toBe( '<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>' );
41
+ });
42
+
43
+ it( 'With a little bit of everything', function(){
44
+ expect( SUT.parse( input='## Header!#chr( 10 )#* __Bold Item__#chr( 10 )#* _Italic Item_' ) ).toBe( '<h1>Header!</h1><ul><li><strong>Bold Item</strong></li><li><em>Italic Item</em></li></ul>' );
45
+ });
46
+
47
+ });
48
+
49
+ }
50
+
51
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Here is an example solution for the Markdown exercise
3
+ */
4
+ component {
5
+
6
+ /**
7
+ * Parse markdown
8
+ *
9
+ * @input.hint markdown to parse
10
+ */
11
+ public string function parse(required string input) {
12
+
13
+ var lines = listToArray( arguments.input, chr( 10 ) );
14
+ var output = [];
15
+ var isInList = false;
16
+
17
+ for (var line in lines) {
18
+ line = parseHeader(line);
19
+ line = parseBold( line );
20
+ line = parseItalic( line );
21
+
22
+ line = parseList( line, isInList );
23
+ /* We are in a list if the line contains a list item and not the unordered list closing tag */
24
+ isInList = ( reFindNoCase("<li>", line ) && !reFindNoCase("</ul>", line ) );
25
+
26
+ line = parseParagraph( line );
27
+ output.append( line );
28
+ }
29
+
30
+ /* Just in case the last line was a list item */
31
+ if ( isInList ) {
32
+ output.append( "</ul>" );
33
+ }
34
+
35
+ var html = arrayToList( output, "" );
36
+ return html;
37
+
38
+ }
39
+
40
+ /**
41
+ *
42
+ * Parse html headers from markdown text
43
+ *
44
+ * @markdown.hint Markdown text to parse
45
+ *
46
+ */
47
+ private string function parseHeader( required string markdown ) {
48
+ /* Get the header number */
49
+ var headerNumber = reMatchNoCase( "^[##]+", arguments.markdown).len() ? reMatchNoCase( "^[##]+", arguments.markdown)[1].len() : 0;
50
+
51
+ if (headerNumber) {
52
+ var pattern = "^[##]+ (.*)";
53
+ var replacement = "<h#headerNumber#>\1</h#headerNumber#>";
54
+
55
+ var output = reReplaceNoCase( arguments.markdown, pattern, replacement);
56
+ }
57
+ else {
58
+ var output = arguments.markdown;
59
+ }
60
+
61
+ return output;
62
+ }
63
+
64
+ /**
65
+ *
66
+ * Parse a list
67
+ *
68
+ * @markdown.hint Markdown text to parse
69
+ * @isInList.hint Flag to indicate if currently parsing a list or not
70
+ *
71
+ */
72
+ private string function parseList(
73
+ required string markdown,
74
+ required boolean isInList
75
+ ) {
76
+
77
+ var output = arguments.markdown;
78
+
79
+ if ( isListItem( arguments.markdown ) ) {
80
+ if ( !arguments.isInList ) {
81
+ output = "<ul>" & parseListItem( output );
82
+ }
83
+ else {
84
+ output = parseListItem( output );
85
+ }
86
+ }
87
+ else if ( arguments.isInList ) {
88
+ output = "</ul>" & output;
89
+ }
90
+
91
+ return output;
92
+ }
93
+
94
+
95
+ /**
96
+ *
97
+ * Parse a paragraph
98
+ *
99
+ * @markdown.hint Markdown text to parse
100
+ *
101
+ */
102
+ private string function parseParagraph( required string markdown ) {
103
+
104
+ if ( !reMatchNoCase( "<h|<ul|<p|<li", arguments.markdown ).len() ) {
105
+ var output = "<p>#arguments.markdown#</p>";
106
+ }
107
+ else {
108
+ var output = arguments.markdown;
109
+ }
110
+
111
+ return output;
112
+ }
113
+
114
+ /**
115
+ *
116
+ * Wrap a list with <ul> tags
117
+ *
118
+ * @markdown.hint Markdown text to parse
119
+ *
120
+ */
121
+ private string function wrapList( required string markdown ) {
122
+ return "<ul>#arguments.markdown#</ul>";
123
+ }
124
+
125
+ /**
126
+ *
127
+ * Check if markdown is a list item
128
+ *
129
+ * @markdown.hint Markdown text to parse
130
+ *
131
+ */
132
+ public boolean function isListItem( required string markdown ) {
133
+ /* Use regex to determine if markdown is list item */
134
+ return ( reFindNoCase( "\* (.*)",arguments.markdown ) > 0 );
135
+ }
136
+
137
+
138
+ /**
139
+ *
140
+ * Parse a list item
141
+ *
142
+ * @markdown.hint Markdown text to parse
143
+ *
144
+ */
145
+ private string function parseListItem( required string markdown ) {
146
+
147
+ var pattern = "\* (.*)";
148
+ var replacement = "<li>\1</li>";
149
+ var output = reReplaceNoCase( arguments.markdown, pattern, replacement );
150
+
151
+ /* Add a p tag to item if no other tag is present */
152
+ if ( !reFindNoCase("<li><", output ) ) {
153
+ var pattern = "<li>(.*)</li>";
154
+ var replacement = "<li><p>\1</p></li>";
155
+ var output = reReplaceNoCase( output, pattern, replacement );
156
+ }
157
+
158
+ return output;
159
+ }
160
+
161
+
162
+ /**
163
+ *
164
+ * Parse bold text from markdown
165
+ *
166
+ * @markdown.hint Markdown text to parse
167
+ *
168
+ */
169
+ private string function parseBold( required string markdown ) {
170
+ return parseWithDelimiter( arguments.markdown, "__", "strong" );
171
+ }
172
+
173
+ /**
174
+ *
175
+ * Parse bold text from markdown
176
+ *
177
+ * @markdown.hint Markdown text to parse
178
+ *
179
+ */
180
+ private string function parseItalic( required string markdown ) {
181
+
182
+ return parseWithDelimiter( arguments.markdown, "_", "em" );
183
+ }
184
+
185
+ /**
186
+ *
187
+ * Utility function to replace markdown delimiter with their tag equivalent
188
+ *
189
+ * @markdown.hint Markdown to parse
190
+ * @delimiter.hint Markdown syntax to replace with tag i.e __
191
+ * @tag.hint HTML tag to replace it with. i.e <strong>
192
+ *
193
+ */
194
+ public any function parseWithDelimiter(
195
+ required string markdown,
196
+ required string delimiter,
197
+ required string tag
198
+ ) {
199
+ var pattern = "#arguments.delimiter#(.*)#arguments.delimiter#";
200
+ var replacement = "<#arguments.tag#>\1</#arguments.tag#>";
201
+
202
+ return reReplaceNoCase( arguments.markdown, pattern, replacement);
203
+ }
204
+ }
@@ -0,0 +1,7 @@
1
+ component extends="MarkdownTest" {
2
+
3
+ function beforeAll(){
4
+ SUT = createObject( 'Solution' );
5
+ }
6
+
7
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * I am a CommandBox task runner which you can use to test your implementation of this exercise against the
3
+ * provided test suite. To use me, open the CommandBox CLI and run this:
4
+ *
5
+ * CommandBox> task run TestRunner
6
+ *
7
+ * To start up a test watcher that will automatically rerun the test suite every time you save a file change, run this:
8
+ *
9
+ * CommandBox> task run TestRunner --watcher
10
+ *
11
+ */
12
+ component {
13
+
14
+ /**
15
+ * @solution Runs the tests against the solution
16
+ * @watcher Start up a file watch that re-runs the tests on file changes. Use Ctrl-C to stop
17
+ */
18
+ function run( boolean solution=false, boolean watcher=false ) {
19
+
20
+ ensureTestBox();
21
+
22
+ if( watcher ) {
23
+
24
+ // Tabula rasa
25
+ command( 'cls' ).run();
26
+
27
+ // Start watcher
28
+ watch()
29
+ .paths( '*.cfc' )
30
+ .inDirectory( getCWD() )
31
+ .withDelay( 500 )
32
+ .onChange( function() {
33
+
34
+ // Clear the screen
35
+ command( 'cls' )
36
+ .run();
37
+
38
+ // This is neccessary so changes to tests get picked up right away.
39
+ pagePoolClear();
40
+
41
+ runTests( solution );
42
+
43
+ } )
44
+ .start();
45
+
46
+ } else {
47
+ runTests( solution );
48
+ }
49
+
50
+ }
51
+
52
+ /**
53
+ * Make sure the TestBox framework is installed
54
+ */
55
+ private function ensureTestBox() {
56
+ var excerciseRoot = getCWD();
57
+ var testBoxRoot = excerciseRoot & '/testbox';
58
+
59
+ if( !directoryExists( testBoxRoot ) ) {
60
+
61
+ print.boldYellowLine( 'Installing some missing dependencies for you!' ).toConsole();
62
+ command( 'install' )
63
+ .inWorkingDirectory( excerciseRoot )
64
+ .run();
65
+ }
66
+
67
+ // Bootstrap TestBox framework
68
+ filesystemUtil.createMapping( '/testbox', testBoxRoot );
69
+ }
70
+
71
+ /**
72
+ * Invoke TestBox to run the test suite
73
+ */
74
+ private function runTests( boolean solution=false ) {
75
+
76
+ // Create TestBox and run the tests
77
+ testData = new testbox.system.TestBox()
78
+ .runRaw( directory = {
79
+ // Find all CFCs...
80
+ mapping = filesystemUtil.makePathRelative( getCWD() ),
81
+ // ... in this directory ...
82
+ recurse = false,
83
+ // ... whose name ends in "test"
84
+ filter = function( path ) {
85
+ return path.reFind( ( solution ? 'Solution' : '' ) & 'Test.cfc$' );
86
+ }
87
+ } )
88
+ .getMemento();
89
+
90
+ // Print out the results with ANSI formatting for the CLI
91
+ getInstance( 'CLIRenderer@testbox-commands' )
92
+ .render( print, testData, true );
93
+
94
+ print.toConsole();
95
+
96
+ // Set proper exit code
97
+ if( testData.totalFail || testData.totalError ) {
98
+ setExitCode( 1 );
99
+ }
100
+ }
101
+
102
+ }
103
+