trackler 2.2.1.77 → 2.2.1.78

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/transpose/canonical-data.json +94 -116
  4. data/tracks/bash/config/maintainers.json +11 -1
  5. data/tracks/clojure/config.json +10 -2
  6. data/tracks/clojure/exercises/reverse-string/README.md +14 -0
  7. data/tracks/clojure/exercises/reverse-string/project.clj +4 -0
  8. data/tracks/clojure/exercises/reverse-string/src/example.clj +5 -0
  9. data/tracks/clojure/exercises/reverse-string/src/reverse_string.clj +5 -0
  10. data/tracks/clojure/exercises/reverse-string/test/reverse_string_test.clj +18 -0
  11. data/tracks/clojure/exercises/say/README.md +70 -0
  12. data/tracks/clojure/exercises/{two-fer → say}/project.clj +3 -3
  13. data/tracks/clojure/exercises/say/src/example.clj +9 -0
  14. data/tracks/clojure/exercises/say/src/say.clj +5 -0
  15. data/tracks/clojure/exercises/say/test/say_test.clj +48 -0
  16. data/tracks/coffeescript/docs/ABOUT.md +4 -9
  17. data/tracks/coffeescript/docs/INSTALLATION.md +2 -2
  18. data/tracks/erlang/exercises/pangram/rebar.config +1 -1
  19. data/tracks/erlang/exercises/pangram/src/example.erl +2 -3
  20. data/tracks/erlang/exercises/pangram/test/pangram_tests.erl +12 -13
  21. data/tracks/fsharp/exercises/grep/Example.fs +16 -20
  22. data/tracks/fsharp/exercises/grep/GrepTest.fs +154 -233
  23. data/tracks/fsharp/generators/Common.fs +7 -2
  24. data/tracks/fsharp/generators/Exercise.fs +51 -16
  25. data/tracks/fsharp/generators/Formatting.fs +13 -6
  26. data/tracks/fsharp/generators/Generators.fs +44 -14
  27. data/tracks/fsharp/generators/Rendering.fs +8 -1
  28. data/tracks/fsharp/generators/Templates/Generators/_GrepSetup.liquid +37 -0
  29. data/tracks/fsharp/generators/Templates/_TestClass.liquid +9 -3
  30. data/tracks/fsharp/generators/Templates/{_TestMethod.liquid → _TestFunction.liquid} +0 -0
  31. data/tracks/fsharp/generators/Templates/{_TestMethodBody.liquid → _TestFunctionBody.liquid} +0 -0
  32. data/tracks/fsharp/generators/Templates/_TestMember.liquid +3 -0
  33. data/tracks/fsharp/generators/Templates/_TestMemberBody.liquid +4 -0
  34. data/tracks/fsharp/generators/Templates/_TestModule.liquid +17 -0
  35. data/tracks/go/config.json +24 -0
  36. data/tracks/go/exercises/acronym/.meta/gen.go +5 -3
  37. data/tracks/go/exercises/acronym/acronym.go +0 -2
  38. data/tracks/go/exercises/acronym/cases_test.go +2 -2
  39. data/tracks/go/exercises/acronym/example.go +2 -3
  40. data/tracks/go/exercises/all-your-base/.meta/gen.go +9 -7
  41. data/tracks/go/exercises/all-your-base/cases_test.go +2 -2
  42. data/tracks/go/exercises/allergies/.meta/gen.go +10 -6
  43. data/tracks/go/exercises/allergies/cases_test.go +2 -2
  44. data/tracks/go/exercises/anagram/.meta/gen.go +7 -5
  45. data/tracks/go/exercises/anagram/cases_test.go +2 -2
  46. data/tracks/go/exercises/armstrong-numbers/.meta/gen.go +54 -0
  47. data/tracks/go/exercises/armstrong-numbers/armstrong_test.go +12 -0
  48. data/tracks/go/exercises/armstrong-numbers/cases_test.go +52 -0
  49. data/tracks/go/exercises/armstrong-numbers/example.go +24 -0
  50. data/tracks/go/exercises/binary-search/.meta/gen.go +7 -5
  51. data/tracks/go/exercises/binary-search/cases_test.go +2 -2
  52. data/tracks/go/exercises/book-store/.meta/gen.go +5 -3
  53. data/tracks/go/exercises/book-store/cases_test.go +2 -2
  54. data/tracks/go/exercises/connect/example.go +4 -5
  55. data/tracks/go/exercises/dominoes/.meta/gen.go +61 -0
  56. data/tracks/go/exercises/dominoes/.meta/hints.md +29 -0
  57. data/tracks/go/exercises/dominoes/README.md +67 -0
  58. data/tracks/go/exercises/dominoes/cases_test.go +72 -0
  59. data/tracks/go/exercises/dominoes/dominoes_test.go +106 -0
  60. data/tracks/go/exercises/dominoes/example.go +146 -0
  61. data/tracks/go/exercises/ledger/example.go +1 -1
  62. data/tracks/groovy/.gitignore +3 -0
  63. data/tracks/groovy/.travis.yml +10 -1
  64. data/tracks/groovy/bin/prepeare_exercise_builds.groovy +77 -0
  65. data/tracks/groovy/build.gradle +25 -0
  66. data/tracks/groovy/exercises/difference-of-squares/Example.groovy +3 -3
  67. data/tracks/groovy/exercises/gigasecond/GigasecondSpec.groovy +3 -3
  68. data/tracks/groovy/exercises/linked-list/{DoubleLinkedList.groovy → LinkedList.groovy} +0 -0
  69. data/tracks/groovy/exercises/linked-list/{DoubleLinkedListSpec.groovy → LinkedListSpec.groovy} +0 -0
  70. data/tracks/groovy/exercises/linked-list/README.md +1 -1
  71. data/tracks/groovy/gradle/wrapper/gradle-wrapper.jar +0 -0
  72. data/tracks/groovy/gradle/wrapper/gradle-wrapper.properties +6 -0
  73. data/tracks/groovy/gradlew +172 -0
  74. data/tracks/groovy/gradlew.bat +84 -0
  75. data/tracks/groovy/settings.gradle +9 -0
  76. data/tracks/ocaml/exercises/acronym/.merlin +1 -1
  77. data/tracks/ocaml/exercises/all-your-base/.merlin +1 -1
  78. data/tracks/ocaml/exercises/anagram/.merlin +1 -1
  79. data/tracks/ocaml/exercises/atbash-cipher/.merlin +1 -1
  80. data/tracks/ocaml/exercises/beer-song/.merlin +1 -1
  81. data/tracks/ocaml/exercises/binary-search/.merlin +1 -1
  82. data/tracks/ocaml/exercises/bob/.merlin +1 -1
  83. data/tracks/ocaml/exercises/bowling/.merlin +1 -1
  84. data/tracks/ocaml/exercises/change/.merlin +1 -1
  85. data/tracks/ocaml/exercises/difference-of-squares/.merlin +1 -1
  86. data/tracks/ocaml/exercises/etl/.merlin +1 -1
  87. data/tracks/ocaml/exercises/forth/.merlin +1 -1
  88. data/tracks/ocaml/exercises/grade-school/.merlin +1 -1
  89. data/tracks/ocaml/exercises/hamming/.merlin +1 -1
  90. data/tracks/ocaml/exercises/hangman/.merlin +1 -1
  91. data/tracks/ocaml/exercises/hello-world/.merlin +1 -1
  92. data/tracks/ocaml/exercises/hexadecimal/.merlin +1 -1
  93. data/tracks/ocaml/exercises/leap/.merlin +1 -1
  94. data/tracks/ocaml/exercises/list-ops/.merlin +1 -1
  95. data/tracks/ocaml/exercises/luhn/.merlin +1 -1
  96. data/tracks/ocaml/exercises/meetup/.merlin +1 -1
  97. data/tracks/ocaml/exercises/minesweeper/.merlin +1 -1
  98. data/tracks/ocaml/exercises/nucleotide-count/.merlin +1 -1
  99. data/tracks/ocaml/exercises/phone-number/.merlin +1 -1
  100. data/tracks/ocaml/exercises/prime-factors/.merlin +1 -1
  101. data/tracks/ocaml/exercises/raindrops/.merlin +1 -1
  102. data/tracks/ocaml/exercises/react/.merlin +1 -1
  103. data/tracks/ocaml/exercises/rectangles/.merlin +1 -1
  104. data/tracks/ocaml/exercises/rna-transcription/.merlin +1 -1
  105. data/tracks/ocaml/exercises/run-length-encoding/.merlin +1 -1
  106. data/tracks/ocaml/exercises/say/.merlin +1 -1
  107. data/tracks/ocaml/exercises/space-age/.merlin +1 -1
  108. data/tracks/ocaml/exercises/triangle/.merlin +1 -1
  109. data/tracks/ocaml/exercises/word-count/.merlin +1 -1
  110. data/tracks/ocaml/exercises/zipper/.merlin +1 -1
  111. data/tracks/typescript/config/maintainers.json +2 -2
  112. metadata +37 -12
  113. data/tracks/clojure/exercises/two-fer/README.md +0 -19
  114. data/tracks/clojure/exercises/two-fer/src/example.clj +0 -5
  115. data/tracks/clojure/exercises/two-fer/src/two_fer.clj +0 -5
  116. data/tracks/clojure/exercises/two-fer/test/two_fer_test.clj +0 -12
  117. data/tracks/erlang/exercises/pangram/include/exercism.hrl +0 -11
@@ -31,13 +31,18 @@ type TestMethodBodyAssert =
31
31
  { Sut: string
32
32
  Expected: string }
33
33
 
34
- type TestClass =
34
+ type TestFile =
35
35
  { Version: string
36
36
  ExerciseName: string
37
37
  TestModuleName: string
38
38
  TestedModuleName: string
39
39
  Namespaces: string list
40
- Methods: string list }
40
+ Methods: string list
41
+ Setup: string }
42
+
43
+ type TestFileFormat =
44
+ | Module
45
+ | Class
41
46
 
42
47
  module Logging =
43
48
  let setupLogger() =
@@ -23,17 +23,24 @@ type GeneratorExercise() =
23
23
  abstract member MapCanonicalDataCaseProperty : CanonicalDataCase * string * obj -> obj
24
24
 
25
25
  // Convert canonical data to representation used when rendering
26
- abstract member ToTestClass : CanonicalData -> TestClass
26
+ abstract member ToTestFile : CanonicalData -> TestFile
27
27
  abstract member ToTestMethod : int * CanonicalDataCase -> TestMethod
28
28
  abstract member ToTestMethodBody : CanonicalDataCase -> TestMethodBody
29
29
  abstract member ToTestMethodBodyAssert : CanonicalDataCase -> TestMethodBodyAssert
30
- abstract member ToTestMethodBodyAssertTemplate : CanonicalDataCase -> string
30
+
31
+ // Determine the templates to use when rendering
32
+ abstract member TestFileTemplate : string
33
+ abstract member TestMethodTemplate : int * CanonicalDataCase -> string
34
+ abstract member TestMethodBodyTemplate : CanonicalDataCase -> string
35
+ abstract member TestMethodBodyAssertTemplate : CanonicalDataCase -> string
36
+ abstract member TestFileFormat: TestFileFormat
31
37
 
32
38
  // Rendering of canonical data
33
39
  abstract member Render : CanonicalData -> string
34
- abstract member RenderTestMethod : int -> CanonicalDataCase -> string
40
+ abstract member RenderTestMethod : int * CanonicalDataCase -> string
35
41
  abstract member RenderTestMethodBody : CanonicalDataCase -> string
36
42
  abstract member RenderTestMethodName : CanonicalDataCase -> string
43
+ abstract member RenderSetup : CanonicalData -> string
37
44
 
38
45
  // Generic value/identifier rendering methods
39
46
  abstract member RenderValue : CanonicalDataCase * string * obj -> string
@@ -66,10 +73,10 @@ type GeneratorExercise() =
66
73
  member this.TestedModuleName = this.GetType().Name.Pascalize()
67
74
 
68
75
  member this.WriteToFile contents =
69
- let testClassPath = Path.Combine("..", "exercises", this.Name, sprintf "%s.fs" this.TestModuleName)
76
+ let testFilePath = Path.Combine("..", "exercises", this.Name, sprintf "%s.fs" this.TestModuleName)
70
77
 
71
- Directory.CreateDirectory(Path.GetDirectoryName(testClassPath)) |> ignore
72
- File.WriteAllText(testClassPath, contents)
78
+ Directory.CreateDirectory(Path.GetDirectoryName(testFilePath)) |> ignore
79
+ File.WriteAllText(testFilePath, contents)
73
80
 
74
81
  member this.Regenerate(canonicalData) =
75
82
  canonicalData
@@ -94,13 +101,16 @@ type GeneratorExercise() =
94
101
 
95
102
  // Convert canonical data to representation used when rendering
96
103
 
97
- default this.ToTestClass canonicalData =
104
+ default this.ToTestFile canonicalData =
105
+ let renderTestMethod i canonicalDataCase = this.RenderTestMethod(i, canonicalDataCase)
106
+
98
107
  { Version = canonicalData.Version
99
108
  ExerciseName = this.Name
100
109
  TestModuleName = this.TestModuleName
101
110
  TestedModuleName = this.TestedModuleName
102
111
  Namespaces = ["FsUnit.Xunit"; "Xunit"] @ this.AdditionalNamespaces
103
- Methods = List.mapi this.RenderTestMethod canonicalData.Cases }
112
+ Methods = List.mapi renderTestMethod canonicalData.Cases
113
+ Setup = this.RenderSetup canonicalData }
104
114
 
105
115
  default this.ToTestMethod (index, canonicalDataCase) =
106
116
  { Skip = index > 0
@@ -115,27 +125,50 @@ type GeneratorExercise() =
115
125
  { Sut = this.RenderSut canonicalDataCase
116
126
  Expected = this.RenderValueOrIdentifier (canonicalDataCase, "expected", canonicalDataCase.Expected) }
117
127
 
118
- default this.ToTestMethodBodyAssertTemplate canonicalDataCase =
128
+ // Determine the templates to use when rendering
129
+
130
+ default this.TestFileTemplate =
131
+ match this.TestFileFormat with
132
+ | Module -> "TestModule"
133
+ | Class -> "TestClass"
134
+
135
+ default this.TestMethodTemplate (_, _) =
136
+ match this.TestFileFormat with
137
+ | Module -> "TestFunction"
138
+ | Class -> "TestMember"
139
+
140
+ default this.TestMethodBodyTemplate _ =
141
+ match this.TestFileFormat with
142
+ | Module -> "TestFunctionBody"
143
+ | Class -> "TestMemberBody"
144
+
145
+ default this.TestMethodBodyAssertTemplate canonicalDataCase =
119
146
  match canonicalDataCase.Expected with
120
147
  | :? JArray as jArray when jArray.Count = 0 && not (List.contains "expected" (this.PropertiesWithIdentifier canonicalDataCase)) -> "AssertEmpty"
121
148
  | _ -> "AssertEqual"
122
149
 
150
+ default __.TestFileFormat = TestFileFormat.Module
151
+
123
152
  // Rendering of canonical data
124
153
 
125
154
  default this.Render canonicalData =
126
155
  canonicalData
127
- |> this.ToTestClass
128
- |> renderPartialTemplate "TestClass"
156
+ |> this.ToTestFile
157
+ |> renderPartialTemplate this.TestFileTemplate
158
+
159
+ default this.RenderTestMethod (index, canonicalDataCase) =
160
+ let template = this.TestMethodTemplate (index, canonicalDataCase)
129
161
 
130
- default this.RenderTestMethod index canonicalDataCase =
131
162
  (index, canonicalDataCase)
132
163
  |> this.ToTestMethod
133
- |> renderPartialTemplate "TestMethod"
164
+ |> renderPartialTemplate template
134
165
 
135
166
  default this.RenderTestMethodBody canonicalDataCase =
167
+ let template = this.TestMethodBodyTemplate canonicalDataCase
168
+
136
169
  canonicalDataCase
137
170
  |> this.ToTestMethodBody
138
- |> renderPartialTemplate "TestMethodBody"
171
+ |> renderPartialTemplate template
139
172
 
140
173
  default this.RenderTestMethodName canonicalDataCase =
141
174
  match this.UseFullMethodName canonicalDataCase with
@@ -144,7 +177,9 @@ type GeneratorExercise() =
144
177
  | true ->
145
178
  canonicalDataCase.DescriptionPath
146
179
  |> String.concat " - "
147
- |> String.upperCaseFirst
180
+ |> String.upperCaseFirst
181
+
182
+ default __.RenderSetup _ = ""
148
183
 
149
184
  // Generic value/identifier rendering methods
150
185
 
@@ -195,7 +230,7 @@ type GeneratorExercise() =
195
230
  |> List.choose renderArrangeProperty
196
231
 
197
232
  default this.RenderAssert canonicalDataCase =
198
- let template = this.ToTestMethodBodyAssertTemplate canonicalDataCase
233
+ let template = this.TestMethodBodyAssertTemplate canonicalDataCase
199
234
 
200
235
  canonicalDataCase
201
236
  |> this.ToTestMethodBodyAssert
@@ -121,7 +121,7 @@ let rec formatValue (value: obj) =
121
121
  | _ ->
122
122
  string value
123
123
 
124
- let formatCollection formatString collection =
124
+ let private formatCollection formatString collection =
125
125
  collection
126
126
  |> String.concat "; "
127
127
  |> sprintf formatString
@@ -132,7 +132,7 @@ let formatArray sequence = formatCollection "[|%s|]" sequence
132
132
 
133
133
  let formatSequence sequence = formatCollection "seq {%s}" sequence
134
134
 
135
- let formatMultiLineCollection (openPrefix, closePostfix) collection =
135
+ let private formatMultiLineCollection (openPrefix, closePostfix) collection indentation =
136
136
  match Seq.length collection with
137
137
  | 0 ->
138
138
  sprintf "%s%s" openPrefix closePostfix
@@ -153,15 +153,22 @@ let formatMultiLineCollection (openPrefix, closePostfix) collection =
153
153
  collection
154
154
  |> Seq.mapi formatLine
155
155
  |> Seq.toList
156
- |> List.map (indent 2)
156
+ |> List.map (indent indentation)
157
157
  |> String.concat "\n"
158
158
  |> sprintf "\n%s"
159
159
 
160
- let formatMultiLineList sequence = formatMultiLineCollection ("[", "]") sequence
161
160
 
162
- let formatMultiLineArray sequence = formatMultiLineCollection ("[|", "|]") sequence
161
+ let formatMultiLineListWithIndentation indentation sequence = formatMultiLineCollection ("[", "]") sequence indentation
163
162
 
164
- let formatMultiLineSequence sequence = formatMultiLineCollection ("seq {", "}") sequence
163
+ let formatMultiLineArrayWithIndentation indentation sequence = formatMultiLineCollection ("[|", "|]") sequence indentation
164
+
165
+ let formatMultiLineSequenceWithIndentation indentation sequence = formatMultiLineCollection ("seq {", "}") sequence indentation
166
+
167
+ let formatMultiLineList sequence = formatMultiLineListWithIndentation 2 sequence
168
+
169
+ let formatMultiLineArray sequence = formatMultiLineArrayWithIndentation 2 sequence
170
+
171
+ let formatMultiLineSequence sequence = formatMultiLineSequenceWithIndentation 2 sequence
165
172
 
166
173
  let formatMultiLineString strings =
167
174
  let length = Seq.length strings
@@ -4,6 +4,7 @@ open System
4
4
  open System.Globalization
5
5
  open Newtonsoft.Json.Linq
6
6
  open Formatting
7
+ open Rendering
7
8
  open Exercise
8
9
 
9
10
  type Acronym() =
@@ -61,7 +62,7 @@ type Allergies() =
61
62
  type Alphametics() =
62
63
  inherit GeneratorExercise()
63
64
 
64
- member __.formatMap<'TKey, 'TValue> (value: obj) =
65
+ member __.FormatMap<'TKey, 'TValue> (value: obj) =
65
66
  if isNull value then
66
67
  "None"
67
68
  else
@@ -77,7 +78,7 @@ type Alphametics() =
77
78
  else
78
79
  sprintf "%s |> Map.ofList |> Some" formattedList
79
80
 
80
- override this.RenderExpected (_, _, value) = this.formatMap<char, int> value
81
+ override this.RenderExpected (_, _, value) = this.FormatMap<char, int> value
81
82
 
82
83
  override this.PropertiesWithIdentifier canonicalDataCase = this.Properties canonicalDataCase
83
84
 
@@ -164,7 +165,7 @@ type Clock() =
164
165
  let minute = clock.["minute"].ToObject<string>()
165
166
  sprintf "let %s = create %s %s" clockId hour minute
166
167
 
167
- member private this.renderPropertyValue canonicalDataCase property =
168
+ member private this.RenderPropertyValue canonicalDataCase property =
168
169
  this.RenderSutParameter (canonicalDataCase, property, Map.find property canonicalDataCase.Properties)
169
170
 
170
171
  override __.PropertiesWithIdentifier _ = ["clock1"; "clock2"]
@@ -177,8 +178,8 @@ type Clock() =
177
178
  override this.RenderArrange canonicalDataCase =
178
179
  match canonicalDataCase.Property with
179
180
  | "create" | "add" ->
180
- let hour = this.renderPropertyValue canonicalDataCase "hour"
181
- let minute = this.renderPropertyValue canonicalDataCase "minute"
181
+ let hour = this.RenderPropertyValue canonicalDataCase "hour"
182
+ let minute = this.RenderPropertyValue canonicalDataCase "minute"
182
183
  [sprintf "let clock = create %s %s" hour minute]
183
184
  | _ ->
184
185
  base.RenderArrange canonicalDataCase
@@ -188,7 +189,7 @@ type Clock() =
188
189
  | "create" ->
189
190
  sprintf "display clock"
190
191
  | "add" ->
191
- this.renderPropertyValue canonicalDataCase "add"
192
+ this.RenderPropertyValue canonicalDataCase "add"
192
193
  |> sprintf "add %s clock |> display"
193
194
  | "equal" ->
194
195
  "clock1 = clock2"
@@ -250,7 +251,7 @@ type Dominoes() =
250
251
  type Etl() =
251
252
  inherit GeneratorExercise()
252
253
 
253
- member __.formatMap<'TKey, 'TValue> (value: obj) =
254
+ member __.FormatMap<'TKey, 'TValue> (value: obj) =
254
255
  let input = value :?> JObject
255
256
  let dict = input.ToObject<Collections.Generic.Dictionary<'TKey, 'TValue>>();
256
257
  let formattedList =
@@ -263,9 +264,9 @@ type Etl() =
263
264
  else
264
265
  sprintf "%s |> Map.ofList" formattedList
265
266
 
266
- override this.RenderInput (_, _, value) = this.formatMap<int, List<char>> value
267
+ override this.RenderInput (_, _, value) = this.FormatMap<int, List<char>> value
267
268
 
268
- override this.RenderExpected (_, _, value) = this.formatMap<char, int> value
269
+ override this.RenderExpected (_, _, value) = this.FormatMap<char, int> value
269
270
 
270
271
  override this.PropertiesWithIdentifier canonicalDataCase = this.Properties canonicalDataCase
271
272
 
@@ -323,6 +324,35 @@ type Grains() =
323
324
  | "-1" -> "Error \"Invalid input\""
324
325
  | x -> sprintf "Ok %sUL" x
325
326
 
327
+ type Grep() =
328
+ inherit GeneratorExercise()
329
+
330
+ override this.PropertiesWithIdentifier canonicalDataCase = this.Properties canonicalDataCase
331
+
332
+ override __.RenderExpected (_, _, value) =
333
+ (value :?> JArray)
334
+ |> normalizeJArray
335
+ |> Seq.map formatValue
336
+ |> formatMultiLineListWithIndentation 3
337
+
338
+ override __.RenderSetup _ = renderPartialTemplate "Generators/GrepSetup" Map.empty<string, obj>
339
+
340
+ override __.RenderArrange canonicalDataCase =
341
+ base.RenderArrange canonicalDataCase @ [""; "createFiles() |> ignore"]
342
+
343
+ override __.IdentifierTypeAnnotation (canonicalDataCase, key, value) =
344
+ match key with
345
+ | "expected" ->
346
+ match value :?> JArray |> Seq.isEmpty with
347
+ | true -> Some "string list"
348
+ | false -> None
349
+ | _ ->
350
+ base.IdentifierTypeAnnotation(canonicalDataCase, key, value)
351
+
352
+ override __.AdditionalNamespaces = [typeof<System.IO.File>.Namespace]
353
+
354
+ override __.TestFileFormat = TestFileFormat.Class
355
+
326
356
  type Hamming() =
327
357
  inherit GeneratorExercise()
328
358
 
@@ -417,7 +447,7 @@ type Meetup() =
417
447
  override __.PropertiesUsedAsSutParameter _ =
418
448
  ["year"; "month"; "dayofweek"; "week"]
419
449
 
420
- override this.AdditionalNamespaces = [typeof<DateTime>.Namespace]
450
+ override __.AdditionalNamespaces = [typeof<DateTime>.Namespace]
421
451
 
422
452
  type Minesweeper() =
423
453
  inherit GeneratorExercise()
@@ -447,7 +477,7 @@ type NthPrime() =
447
477
  type NucleotideCount() =
448
478
  inherit GeneratorExercise()
449
479
 
450
- member __.formatMap<'TKey, 'TValue> (value: obj) =
480
+ member __.FormatMap<'TKey, 'TValue> (value: obj) =
451
481
  match Option.ofNonError value with
452
482
  | None ->
453
483
  "None"
@@ -464,7 +494,7 @@ type NucleotideCount() =
464
494
  else
465
495
  sprintf "%s |> Map.ofList |> Some" formattedList
466
496
 
467
- override this.RenderExpected (_, _, value) = this.formatMap<char, int> value
497
+ override this.RenderExpected (_, _, value) = this.FormatMap<char, int> value
468
498
 
469
499
  override this.PropertiesWithIdentifier canonicalDataCase = this.Properties canonicalDataCase
470
500
 
@@ -544,7 +574,7 @@ type PascalsTriangle() =
544
574
  | false -> None
545
575
  | _ -> base.IdentifierTypeAnnotation (canonicalDataCase, key, value)
546
576
 
547
- override __.ToTestMethodBodyAssertTemplate _ = "AssertEqual"
577
+ override __.TestMethodBodyAssertTemplate _ = "AssertEqual"
548
578
 
549
579
  type PerfectNumbers() =
550
580
  inherit GeneratorExercise()
@@ -770,7 +800,7 @@ type RunLengthEncoding() =
770
800
  | _ ->
771
801
  base.RenderSut canonicalDataCase
772
802
 
773
- override this.RenderTestMethodName canonicalDataCase =
803
+ override __.RenderTestMethodName canonicalDataCase =
774
804
  match canonicalDataCase.Property with
775
805
  | "consistency" ->
776
806
  base.RenderTestMethodName canonicalDataCase
@@ -7,6 +7,7 @@ open FSharp.Reflection
7
7
  open DotLiquid
8
8
  open DotLiquid.FileSystems
9
9
  open Formatting
10
+ open System.Collections
10
11
 
11
12
  type OutputFilter() =
12
13
  static member Format (input: string) = formatValue input
@@ -47,11 +48,17 @@ let rec private registerTypeTree templateDataType =
47
48
  registrations.[templateDataType] <- true
48
49
  for p in properties do registerTypeTree p.PropertyType
49
50
 
51
+ let private hashFromData (data: obj) =
52
+ match FSharpType.IsRecord (data.GetType()) with
53
+ | true -> Hash.FromAnonymousObject(data)
54
+ | false -> Hash.FromDictionary(data :?> IDictionary<string, obj>)
55
+
50
56
  let renderInlineTemplate template data =
51
57
  data.GetType() |> registerTypeTree
52
58
 
53
59
  let parsedTemplate = Template.Parse template
54
- parsedTemplate.Render(Hash.FromAnonymousObject(data))
60
+ let hash = hashFromData data
61
+ parsedTemplate.Render(hash)
55
62
 
56
63
  let renderPartialTemplate templateName data =
57
64
  let template = sprintf "{%% include \"%s\" %%}" templateName
@@ -0,0 +1,37 @@
1
+ let iliadFileName = "iliad.txt"
2
+ let iliadContents =
3
+ """Achilles sing, O Goddess! Peleus' son;
4
+ His wrath pernicious, who ten thousand woes
5
+ Caused to Achaia's host, sent many a soul
6
+ Illustrious into Ades premature,
7
+ And Heroes gave (so stood the will of Jove)
8
+ To dogs and to all ravening fowls a prey,
9
+ When fierce dispute had separated once
10
+ The noble Chief Achilles from the son
11
+ Of Atreus, Agamemnon, King of men."""
12
+
13
+ let midsummerNightFileName = "midsummer-night.txt"
14
+ let midsummerNightContents =
15
+ """I do entreat your grace to pardon me.
16
+ I know not by what power I am made bold,
17
+ Nor how it may concern my modesty,
18
+ In such a presence here to plead my thoughts;
19
+ But I beseech your grace that I may know
20
+ The worst that may befall me in this case,
21
+ If I refuse to wed Demetrius."""
22
+
23
+ let paradiseLostFileName = "paradise-lost.txt"
24
+ let paradiseLostContents =
25
+ """Of Mans First Disobedience, and the Fruit
26
+ Of that Forbidden Tree, whose mortal tast
27
+ Brought Death into the World, and all our woe,
28
+ With loss of Eden, till one greater Man
29
+ Restore us, and regain the blissful Seat,
30
+ Sing Heav'nly Muse, that on the secret top
31
+ Of Oreb, or of Sinai, didst inspire
32
+ That Shepherd, who first taught the chosen Seed"""
33
+
34
+ let createFiles() =
35
+ File.WriteAllText(iliadFileName, iliadContents)
36
+ File.WriteAllText(midsummerNightFileName, midsummerNightContents)
37
+ File.WriteAllText(paradiseLostFileName, paradiseLostContents)
@@ -7,7 +7,13 @@ open {{ namespace }}
7
7
  {%- endfor -%}
8
8
 
9
9
  open {{ TestedModuleName }}
10
+ {%- if Setup != empty -%}
10
11
 
11
- {%- for method in Methods -%}
12
- {{ method }}
13
- {%- endfor -%}
12
+ {{ Setup }}
13
+ {%- endif -%}
14
+
15
+ type {{ TestModuleName }}() =
16
+
17
+ {%- for method in Methods -%}
18
+ {{ method }}
19
+ {%- endfor -%}
@@ -0,0 +1,3 @@
1
+ [<Fact{% if Skip %}(Skip = "Remove to run test"){% endif %}>]
2
+ member this.``{{ Name }}`` () =
3
+ {{ Body }}
@@ -0,0 +1,4 @@
1
+ {%- for arrange in Arrange -%}
2
+ {{ arrange | indent | indent }}
3
+ {%- endfor -%}
4
+ {{ Assert | indent | indent }}