trackler 2.2.1.68 → 2.2.1.69

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/pov/description.md +0 -2
  4. data/problem-specifications/exercises/protein-translation/canonical-data.json +3 -3
  5. data/problem-specifications/exercises/simple-cipher/description.md +4 -6
  6. data/tracks/csharp/exercises/all-your-base/AllYourBaseTest.cs +7 -7
  7. data/tracks/csharp/exercises/dominoes/README.md +2 -11
  8. data/tracks/csharp/exercises/reverse-string/ReverseStringTest.cs +2 -2
  9. data/tracks/dart/CONTRIBUTING.md +7 -4
  10. data/tracks/delphi/config.json +23 -20
  11. data/tracks/ecmascript/CONTRIBUTING.md +3 -0
  12. data/tracks/ecmascript/README.md +2 -5
  13. data/tracks/fsharp/config.json +11 -0
  14. data/tracks/fsharp/exercises/Exercises.sln +6 -0
  15. data/tracks/fsharp/exercises/all-your-base/AllYourBaseTest.fs +7 -7
  16. data/tracks/fsharp/exercises/diamond/Diamond.fsproj +1 -0
  17. data/tracks/fsharp/exercises/diamond/DiamondTest.fs +111 -114
  18. data/tracks/fsharp/exercises/diamond/README.md +2 -1
  19. data/tracks/fsharp/exercises/dominoes/README.md +2 -5
  20. data/tracks/fsharp/exercises/palindrome-products/Example.fs +27 -19
  21. data/tracks/fsharp/exercises/palindrome-products/PalindromeProducts.fs +3 -3
  22. data/tracks/fsharp/exercises/palindrome-products/PalindromeProductsTest.fs +52 -49
  23. data/tracks/fsharp/exercises/reverse-string/ReverseStringTest.fs +2 -2
  24. data/tracks/fsharp/exercises/spiral-matrix/Example.fs +35 -0
  25. data/tracks/fsharp/exercises/spiral-matrix/Program.fs +1 -0
  26. data/tracks/fsharp/exercises/spiral-matrix/README.md +25 -0
  27. data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrix.fs +3 -0
  28. data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrix.fsproj +23 -0
  29. data/tracks/fsharp/exercises/spiral-matrix/SpiralMatrixTest.fs +47 -0
  30. data/tracks/fsharp/generators/Generators.fs +42 -5
  31. data/tracks/fsharp/generators/Properties/launchSettings.json +1 -2
  32. data/tracks/java/config.json +7 -2
  33. data/tracks/java/exercises/binary-search-tree/src/test/java/BinarySearchTreeTest.java +0 -1
  34. data/tracks/javascript/.eslintignore +1 -2
  35. data/tracks/javascript/exercises/wordy/example.js +2 -2
  36. data/tracks/perl6/exercises/atbash-cipher/AtbashCipher.pm6 +6 -0
  37. data/tracks/perl6/exercises/atbash-cipher/atbash-cipher.t +2 -9
  38. data/tracks/perl6/exercises/atbash-cipher/example.yaml +8 -9
  39. data/tracks/perl6/exercises/grade-school/Example.pm6 +1 -1
  40. data/tracks/perl6/exercises/grade-school/GradeSchool.pm6 +1 -1
  41. data/tracks/perl6/exercises/grade-school/example.yaml +19 -12
  42. data/tracks/perl6/exercises/grade-school/grade-school.t +19 -12
  43. data/tracks/python/.github/stale.yml +6 -3
  44. data/tracks/python/config.json +17 -0
  45. data/tracks/python/exercises/dot-dsl/.meta/hints.md +14 -0
  46. data/tracks/python/exercises/dot-dsl/README.md +52 -0
  47. data/tracks/python/exercises/dot-dsl/dot_dsl.py +27 -0
  48. data/tracks/python/exercises/dot-dsl/dot_dsl_test.py +117 -0
  49. data/tracks/python/exercises/dot-dsl/example.py +52 -0
  50. data/tracks/python/exercises/proverb/README.md +5 -2
  51. data/tracks/python/exercises/proverb/example.py +5 -5
  52. data/tracks/python/exercises/proverb/proverb.py +1 -1
  53. data/tracks/python/exercises/proverb/proverb_test.py +42 -52
  54. data/tracks/python/exercises/say/example.py +2 -2
  55. data/tracks/python/exercises/say/say_test.py +2 -2
  56. data/tracks/rust/README.md +2 -0
  57. data/tracks/rust/_test/count-ignores.sh +15 -8
  58. data/tracks/rust/bin/test-exercise +4 -1
  59. data/tracks/rust/exercises/perfect-numbers/.meta/ignore-count-ignores +6 -0
  60. data/tracks/rust/exercises/perfect-numbers/tests/perfect-numbers.rs +32 -22
  61. data/tracks/typescript/config.json +16 -0
  62. data/tracks/typescript/exercises/two-bucket/README.md +66 -0
  63. data/tracks/typescript/exercises/two-bucket/package.json +36 -0
  64. data/tracks/typescript/exercises/two-bucket/tsconfig.json +22 -0
  65. data/tracks/typescript/exercises/two-bucket/tslint.json +127 -0
  66. data/tracks/typescript/exercises/two-bucket/two-bucket.example.ts +97 -0
  67. data/tracks/typescript/exercises/two-bucket/two-bucket.test.ts +47 -0
  68. data/tracks/typescript/exercises/two-bucket/two-bucket.ts +0 -0
  69. data/tracks/typescript/exercises/two-bucket/yarn.lock +2624 -0
  70. metadata +23 -2
@@ -449,19 +449,35 @@ type OcrNumbers() =
449
449
 
450
450
  type Pangram() =
451
451
  inherit Exercise()
452
-
453
- type PerfectNumbers() =
452
+
453
+ type PalindromeProducts() =
454
454
  inherit Exercise()
455
455
 
456
- let toClassification value = string value |> String.humanize
456
+ let toFactors (value: obj) =
457
+ let jArray = value :?> JArray
458
+ let factors = jArray.ToObject<int list>()
459
+ sprintf "(%A, %A)" factors.[0] factors.[1]
460
+
461
+ let toPalindromeProducts (value: obj) =
462
+ let jObject = value :?> JObject
463
+ let palindromeValue = jObject.Value<int>("value")
464
+ let factors =
465
+ jObject.Value<JArray>("factors")
466
+ |> normalizeJArray
467
+ |> Seq.map toFactors
468
+ |> formatList
469
+
470
+ sprintf "(%d, %s)" palindromeValue factors
457
471
 
458
472
  override this.RenderExpected (canonicalDataCase, key, value) =
459
473
  value
460
- |> Option.ofNonError
461
- |> Option.map toClassification
474
+ |> Option.ofNonError
475
+ |> Option.map toPalindromeProducts
462
476
  |> formatOption
463
477
  |> parenthesizeOption
464
478
 
479
+ override this.PropertiesUsedAsSutParameter canonicalDataCase = ["input_min"; "input_max"]
480
+
465
481
  type PascalsTriangle() =
466
482
  inherit Exercise()
467
483
 
@@ -491,6 +507,18 @@ type PascalsTriangle() =
491
507
  | _ -> base.IdentifierTypeAnnotation (canonicalDataCase, key, value)
492
508
 
493
509
  override this.ToTestMethodBodyAssertTemplate canonicalDataCase = "AssertEqual"
510
+
511
+ type PerfectNumbers() =
512
+ inherit Exercise()
513
+
514
+ let toClassification value = string value |> String.humanize
515
+
516
+ override this.RenderExpected (canonicalDataCase, key, value) =
517
+ value
518
+ |> Option.ofNonError
519
+ |> Option.map toClassification
520
+ |> formatOption
521
+ |> parenthesizeOption
494
522
 
495
523
  type PhoneNumber() =
496
524
  inherit Exercise()
@@ -717,6 +745,15 @@ type RomanNumerals() =
717
745
  type ScrabbleScore() =
718
746
  inherit Exercise()
719
747
 
748
+ type SpiralMatrix() =
749
+ inherit Exercise()
750
+
751
+ override this.RenderExpected (canonicalDataCase, key, value) =
752
+ (value :?> JArray)
753
+ |> normalizeJArray
754
+ |> Seq.map formatValue
755
+ |> formatMultiLineList
756
+
720
757
  type TwelveDays() =
721
758
  inherit Exercise()
722
759
 
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "profiles": {
3
3
  "Generators": {
4
- "commandName": "Project",
5
- "commandLineArgs": "-e allergies"
4
+ "commandName": "Project"
6
5
  }
7
6
  }
8
7
  }
@@ -721,7 +721,12 @@
721
721
  "core": true,
722
722
  "difficulty": 6,
723
723
  "slug": "binary-search",
724
- "topics": null,
724
+ "topics": [
725
+ "recursion",
726
+ "arrays",
727
+ "searching",
728
+ "generics"
729
+ ],
725
730
  "unlocked_by": null,
726
731
  "uuid": "50136dc3-caf7-4fa1-b7bd-0cba1bea9176"
727
732
  },
@@ -1150,4 +1155,4 @@
1150
1155
  ],
1151
1156
  "language": "Java",
1152
1157
  "solution_pattern": "reference"
1153
- }
1158
+ }
@@ -6,7 +6,6 @@ import static org.junit.Assert.assertEquals;
6
6
  import static org.junit.Assert.assertNotNull;
7
7
  import org.junit.Ignore;
8
8
  import org.junit.Test;
9
- import org.junit.Before;
10
9
 
11
10
  public class BinarySearchTreeTest {
12
11
 
@@ -36,5 +36,4 @@ exercises/secret-handshake
36
36
  exercises/simple-cipher
37
37
  exercises/simple-linked-list
38
38
  exercises/triangle
39
- exercises/trinary
40
- exercises/wordy
39
+ exercises/trinary
@@ -47,11 +47,11 @@ WordProblem.prototype.evaluate = function () {
47
47
  var out = 0;
48
48
  var m = this.matches;
49
49
 
50
- if (m[1] !== undefined && m[2] !== undefined && m[3] !== undefined) {
50
+ if ( (typeof m[1]) === 'string' && (typeof m[2]) === 'string' && (typeof m[3]) === 'string') {
51
51
  out = this.operate(m[2], m[1], m[3]);
52
52
  }
53
53
 
54
- if (m[4] !== undefined && m[5] !== undefined) {
54
+ if ( (typeof m[4]) === 'string' && (typeof m[5]) === 'string') {
55
55
  out = this.operate(m[4], out, m[5]);
56
56
  }
57
57
 
@@ -1 +1,7 @@
1
1
  unit module AtbashCipher:ver<1>;
2
+
3
+ sub encode ($phrase) is export {
4
+ }
5
+
6
+ sub decode ($phrase) is export {
7
+ }
@@ -7,7 +7,7 @@ use JSON::Fast;
7
7
  my Str:D $exercise := 'AtbashCipher';
8
8
  my Version:D $version = v1;
9
9
  my Str $module //= $exercise;
10
- plan 4;
10
+ plan 14;
11
11
 
12
12
  use-ok $module or bail-out;
13
13
  require ::($module);
@@ -22,14 +22,7 @@ if ::($exercise).^ver !~~ $version {
22
22
  require ::($module) <&encode &decode>;
23
23
 
24
24
  my $c-data = from-json $=pod.pop.contents;
25
- for @($c-data<cases>) {
26
- my $test = .<description> ~~ 'encode' ?? 'encode' !! 'decode';
27
- subtest $test => {
28
- my @cases = |.<cases>;
29
- plan +@cases;
30
- is &::($test)(.<phrase>), |.<expected description> for @cases;
31
- }
32
- }
25
+ is .<phrase>.&::(.<property>), |.<expected description> for $c-data<cases>»<cases>».Array.flat;
33
26
 
34
27
  =head2 Canonical Data
35
28
  =begin code
@@ -1,16 +1,9 @@
1
1
  exercise: AtbashCipher
2
2
  version: 1
3
- plan: 4
3
+ plan: 14
4
4
  imports: '&encode &decode'
5
5
  tests: |-
6
- for @($c-data<cases>) {
7
- my $test = .<description> ~~ 'encode' ?? 'encode' !! 'decode';
8
- subtest $test => {
9
- my @cases = |.<cases>;
10
- plan +@cases;
11
- is &::($test)(.<phrase>), |.<expected description> for @cases;
12
- }
13
- }
6
+ is .<phrase>.&::(.<property>), |.<expected description> for $c-data<cases>»<cases>».Array.flat;
14
7
 
15
8
  unit: module
16
9
  example: |-
@@ -26,3 +19,9 @@ example: |-
26
19
  .subst( /\W/, '', :g )
27
20
  .trans( [ 'a'..'z' ] => ['a'..'z'].reverse );
28
21
  }
22
+ stub: |-
23
+ sub encode ($phrase) is export {
24
+ }
25
+
26
+ sub decode ($phrase) is export {
27
+ }
@@ -1,4 +1,4 @@
1
- unit module GradeSchool:ver<1>;
1
+ unit module GradeSchool:ver<2>;
2
2
 
3
3
  class Roster is export {
4
4
  has %!roster;
@@ -1,4 +1,4 @@
1
- unit module GradeSchool:ver<1>;
1
+ unit module GradeSchool:ver<2>;
2
2
 
3
3
  class Roster is export {
4
4
  }
@@ -1,26 +1,33 @@
1
1
  exercise: GradeSchool
2
- version: 1
3
- plan: 10
2
+ version: 2
3
+ plan: 19
4
4
  imports: Roster
5
5
  tests: |-
6
6
  subtest "Roster class methods", {
7
7
  plan 3;
8
8
  ok ::('Roster').can($_), $_ for <add-student list-grade list-all>;
9
9
  }
10
+
10
11
  my $roster = ::('Roster').new;
12
+
11
13
  ok $roster.?add-student(:name('Jim'), :2grade), 'Add Jim to grade 2';
12
- is $roster.?list-grade(2), <Jim>, 'List grade 2';
14
+ cmp-ok $roster.?list-grade(2), '~~', <Jim>, 'List grade 2';
15
+
13
16
  ok $roster.?add-student(:name('Zoe'), :2grade), 'Add Zoe to grade 2';
14
17
  ok $roster.?add-student(:name('Barb'), :1grade), 'Add Barb to grade 1';
15
- is $roster.?list-grade(2), <Jim Zoe>, 'List grade 2';
16
- is $roster.?list-grade(1), <Barb>, 'List grade 1';
17
- subtest 'Additional students', {
18
- plan 6;
19
- ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
20
- ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
21
- ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
22
- }
23
- is $roster.?list-all, ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
18
+ cmp-ok $roster.?list-grade(2), '~~', <Jim Zoe>, 'List grade 2';
19
+ cmp-ok $roster.?list-grade(1), '~~', <Barb>, 'List grade 1';
20
+
21
+ cmp-ok $roster.?list-all, '~~', ('Grade 1', <Barb>, 'Grade 2', <Jim Zoe>), 'List all';
22
+
23
+ ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
24
+ ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
25
+ ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
26
+
27
+ cmp-ok $roster.?list-grade(1), '~~', <Anna Barb Charlie>, 'List grade 1';
28
+ cmp-ok $roster.?list-grade(2), '~~', <Alex Jim Zoe>, 'List grade 2';
29
+ cmp-ok $roster.?list-grade(3), '~~', <Dick Harry Tom>, 'List grade 3';
30
+ cmp-ok $roster.?list-all, '~~', ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
24
31
 
25
32
  unit: module
26
33
  example: |-
@@ -4,9 +4,9 @@ use Test;
4
4
  use lib $?FILE.IO.dirname;
5
5
 
6
6
  my Str:D $exercise := 'GradeSchool';
7
- my Version:D $version = v1;
7
+ my Version:D $version = v2;
8
8
  my Str $module //= $exercise;
9
- plan 10;
9
+ plan 19;
10
10
 
11
11
  use-ok $module or bail-out;
12
12
  require ::($module);
@@ -24,19 +24,26 @@ subtest "Roster class methods", {
24
24
  plan 3;
25
25
  ok ::('Roster').can($_), $_ for <add-student list-grade list-all>;
26
26
  }
27
+
27
28
  my $roster = ::('Roster').new;
29
+
28
30
  ok $roster.?add-student(:name('Jim'), :2grade), 'Add Jim to grade 2';
29
- is $roster.?list-grade(2), <Jim>, 'List grade 2';
31
+ cmp-ok $roster.?list-grade(2), '~~', <Jim>, 'List grade 2';
32
+
30
33
  ok $roster.?add-student(:name('Zoe'), :2grade), 'Add Zoe to grade 2';
31
34
  ok $roster.?add-student(:name('Barb'), :1grade), 'Add Barb to grade 1';
32
- is $roster.?list-grade(2), <Jim Zoe>, 'List grade 2';
33
- is $roster.?list-grade(1), <Barb>, 'List grade 1';
34
- subtest 'Additional students', {
35
- plan 6;
36
- ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
37
- ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
38
- ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
39
- }
40
- is $roster.?list-all, ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
35
+ cmp-ok $roster.?list-grade(2), '~~', <Jim Zoe>, 'List grade 2';
36
+ cmp-ok $roster.?list-grade(1), '~~', <Barb>, 'List grade 1';
37
+
38
+ cmp-ok $roster.?list-all, '~~', ('Grade 1', <Barb>, 'Grade 2', <Jim Zoe>), 'List all';
39
+
40
+ ok $roster.?add-student(:name($_), :1grade), "Add $_ to grade 1" for <Charlie Anna>;
41
+ ok $roster.?add-student(:name('Alex'), :2grade), 'Add Alex to grade 2';
42
+ ok $roster.?add-student(:name($_), :3grade), "Add $_ to grade 3" for <Tom Dick Harry>;
43
+
44
+ cmp-ok $roster.?list-grade(1), '~~', <Anna Barb Charlie>, 'List grade 1';
45
+ cmp-ok $roster.?list-grade(2), '~~', <Alex Jim Zoe>, 'List grade 2';
46
+ cmp-ok $roster.?list-grade(3), '~~', <Dick Harry Tom>, 'List grade 3';
47
+ cmp-ok $roster.?list-all, '~~', ('Grade 1', <Anna Barb Charlie>, 'Grade 2', <Alex Jim Zoe>, 'Grade 3', <Dick Harry Tom>), 'List all';
41
48
 
42
49
  INIT { $module = 'Example' if %*ENV<EXERCISM> }
@@ -1,16 +1,19 @@
1
1
  # Number of days of inactivity before an issue becomes stale
2
- daysUntilStale: 60
2
+ daysUntilStale: 21
3
3
  # Number of days of inactivity before a stale issue is closed
4
4
  daysUntilClose: 7
5
5
  # Issues with these labels will never be considered stale
6
6
  exemptLabels:
7
7
  - discussion
8
8
  - pinned
9
+ - epic
10
+ - enhancement
11
+ - beginner friendly
9
12
  # Label to use when marking an issue as stale
10
- staleLabel: on hiatus
13
+ staleLabel: abandoned
11
14
  # Comment to post when marking an issue as stale. Set to `false` to disable
12
15
  markComment: >
13
- This issue has been automatically marked as `on hiatus` because it has not had
16
+ This issue has been automatically marked as `abandoned` because it has not had
14
17
  recent activity. It will be closed if no further activity occurs. Thank you
15
18
  for your contributions.
16
19
  # Comment to post when closing a stale issue. Set to `false` to disable
@@ -763,6 +763,23 @@
763
763
  "object_oriented_programming"
764
764
  ]
765
765
  },
766
+ {
767
+ "uuid": "a9c2fbda-a1e4-42dd-842f-4de5bb361b91",
768
+ "slug": "dot-dsl",
769
+ "core": false,
770
+ "unlocked_by": null,
771
+ "difficulty": 5,
772
+ "topics": [
773
+ "equality",
774
+ "classes",
775
+ "lists",
776
+ "domain_specific_languages",
777
+ "graphs",
778
+ "object_oriented_programming",
779
+ "test_driven_development",
780
+ "transforming"
781
+ ]
782
+ },
766
783
  {
767
784
  "uuid": "dc6e61a2-e9b9-4406-ba5c-188252afbba1",
768
785
  "slug": "transpose",
@@ -0,0 +1,14 @@
1
+ ## Description of DSL
2
+
3
+ A graph, in this DSL, is an object of type `Graph`, taking a list of one
4
+ or more
5
+
6
+ + attributes
7
+ + nodes
8
+ + edges
9
+
10
+ described as tuples.
11
+
12
+ The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
13
+
14
+ Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.
@@ -0,0 +1,52 @@
1
+ # Dot Dsl
2
+
3
+ Write a Domain Specific Language similar to the Graphviz dot language.
4
+
5
+ A [Domain Specific Language
6
+ (DSL)](https://en.wikipedia.org/wiki/Domain-specific_language) is a
7
+ small language optimized for a specific domain.
8
+
9
+ For example the dot language of [Graphviz](http://graphviz.org) allows
10
+ you to write a textual description of a graph which is then transformed
11
+ into a picture by one of the graphviz tools (such as `dot`). A simple
12
+ graph looks like this:
13
+
14
+ graph {
15
+ graph [bgcolor="yellow"]
16
+ a [color="red"]
17
+ b [color="blue"]
18
+ a -- b [color="green"]
19
+ }
20
+
21
+ Putting this in a file `example.dot` and running `dot example.dot -T png
22
+ -o example.png` creates an image `example.png` with red and blue circle
23
+ connected by a green line on a yellow background.
24
+
25
+ Create a DSL similar to the dot language.
26
+
27
+ ## Description of DSL
28
+
29
+ A graph, in this DSL, is an object of type `Graph`, taking a list of one
30
+ or more
31
+
32
+ + attributes
33
+ + nodes
34
+ + edges
35
+
36
+ described as tuples.
37
+
38
+ The implementations of `Node` and `Edge` provided in `dot_dsl.py`.
39
+
40
+ Observe the test cases in `dot_dsl_test.py` to understand the DSL's design.
41
+
42
+ ## Submitting Exercises
43
+
44
+ Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
45
+
46
+ For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
47
+
48
+ For more detailed information about running tests, code style and linting, please see the [help page](http://exercism.io/languages/python).
49
+
50
+ ## Submitting Incomplete Solutions
51
+
52
+ It's possible to submit an incomplete solution so you can see how others have completed the exercise.
@@ -0,0 +1,27 @@
1
+ NODE, EDGE, ATTR = range(3)
2
+
3
+
4
+ class Node(object):
5
+ def __init__(self, name, attrs={}):
6
+ self.name = name
7
+ self.attrs = attrs
8
+
9
+ def __eq__(self, other):
10
+ return self.name == other.name and self.attrs == other.attrs
11
+
12
+
13
+ class Edge(object):
14
+ def __init__(self, src, dst, attrs={}):
15
+ self.src = src
16
+ self.dst = dst
17
+ self.attrs = attrs
18
+
19
+ def __eq__(self, other):
20
+ return (self.src == other.src and
21
+ self.dst == other.dst and
22
+ self.attrs == other.attrs)
23
+
24
+
25
+ class Graph(object):
26
+ def __init__(self, data=[]):
27
+ pass
@@ -0,0 +1,117 @@
1
+ import unittest
2
+
3
+ from dot_dsl import Graph, Node, Edge, NODE, EDGE, ATTR
4
+
5
+
6
+ class DotDslTest(unittest.TestCase):
7
+ def test_empty_graph(self):
8
+ g = Graph()
9
+
10
+ self.assertEqual(g.nodes, [])
11
+ self.assertEqual(g.edges, [])
12
+ self.assertEqual(g.attrs, {})
13
+
14
+ def test_graph_with_one_node(self):
15
+ g = Graph([
16
+ (NODE, "a", {})
17
+ ])
18
+
19
+ self.assertEqual(g.nodes, [Node("a")])
20
+ self.assertEqual(g.edges, [])
21
+ self.assertEqual(g.attrs, {})
22
+
23
+ def test_graph_with_one_node_with_keywords(self):
24
+ g = Graph([
25
+ (NODE, "a", {"color": "green"})
26
+ ])
27
+
28
+ self.assertEqual(g.nodes, [Node("a", {"color": "green"})])
29
+ self.assertEqual(g.edges, [])
30
+ self.assertEqual(g.attrs, {})
31
+
32
+ def test_graph_with_one_edge(self):
33
+ g = Graph([
34
+ (EDGE, "a", "b", {})
35
+ ])
36
+
37
+ self.assertEqual(g.nodes, [])
38
+ self.assertEqual(g.edges, [Edge("a", "b", {})])
39
+ self.assertEqual(g.attrs, {})
40
+
41
+ def test_graph_with_one_attribute(self):
42
+ g = Graph([
43
+ (ATTR, "foo", "1")
44
+ ])
45
+
46
+ self.assertEqual(g.nodes, [])
47
+ self.assertEqual(g.edges, [])
48
+ self.assertEqual(g.attrs, {"foo": "1"})
49
+
50
+ def test_graph_with_attributes(self):
51
+ g = Graph([
52
+ (ATTR, "foo", "1"),
53
+ (ATTR, "title", "Testing Attrs"),
54
+ (NODE, "a", {"color": "green"}),
55
+ (NODE, "c", {}),
56
+ (NODE, "b", {"label", "Beta!"}),
57
+ (EDGE, "b", "c", {}),
58
+ (EDGE, "a", "b", {"color": "blue"}),
59
+ (ATTR, "bar", "true")
60
+ ])
61
+
62
+ self.assertEqual(g.nodes, [Node("a", {"color": "green"}),
63
+ Node("c", {}),
64
+ Node("b", {"label", "Beta!"})])
65
+ self.assertEqual(g.edges, [Edge("b", "c", {}),
66
+ Edge("a", "b", {"color": "blue"})])
67
+ self.assertEqual(g.attrs, {
68
+ "foo": "1",
69
+ "title": "Testing Attrs",
70
+ "bar": "true"
71
+ })
72
+
73
+ def test_malformed_graph(self):
74
+ with self.assertRaises(TypeError):
75
+ Graph(1)
76
+
77
+ with self.assertRaises(TypeError):
78
+ Graph("problematic")
79
+
80
+ def test_malformed_graph_item(self):
81
+ with self.assertRaises(TypeError):
82
+ Graph([
83
+ ()
84
+ ])
85
+
86
+ with self.assertRaises(TypeError):
87
+ Graph([
88
+ (ATTR, )
89
+ ])
90
+
91
+ def test_malformed_attr(self):
92
+ with self.assertRaises(ValueError):
93
+ Graph([
94
+ (ATTR, 1, 2, 3)
95
+ ])
96
+
97
+ def test_malformed_node(self):
98
+ with self.assertRaises(ValueError):
99
+ Graph([
100
+ (NODE, 1, 2, 3)
101
+ ])
102
+
103
+ def test_malformed_EDGE(self):
104
+ with self.assertRaises(ValueError):
105
+ Graph([
106
+ (EDGE, 1, 2)
107
+ ])
108
+
109
+ def test_unknown_item(self):
110
+ with self.assertRaises(ValueError):
111
+ Graph([
112
+ (99, 1, 2)
113
+ ])
114
+
115
+
116
+ if __name__ == '__main__':
117
+ unittest.main()
@@ -0,0 +1,52 @@
1
+ NODE, EDGE, ATTR = range(3)
2
+
3
+
4
+ class Node(object):
5
+ def __init__(self, name, attrs={}):
6
+ self.name = name
7
+ self.attrs = attrs
8
+
9
+ def __eq__(self, other):
10
+ return self.name == other.name and self.attrs == other.attrs
11
+
12
+
13
+ class Edge(object):
14
+ def __init__(self, src, dst, attrs={}):
15
+ self.src = src
16
+ self.dst = dst
17
+ self.attrs = attrs
18
+
19
+ def __eq__(self, other):
20
+ return (self.src == other.src and
21
+ self.dst == other.dst and
22
+ self.attrs == other.attrs)
23
+
24
+
25
+ class Graph(object):
26
+ def __init__(self, data=[]):
27
+ self.nodes = []
28
+ self.edges = []
29
+ self.attrs = {}
30
+
31
+ if not isinstance(data, list):
32
+ raise TypeError("Graph data malformed")
33
+
34
+ for item in data:
35
+ if len(item) < 3:
36
+ raise TypeError("Graph item incomplete")
37
+
38
+ type_ = item[0]
39
+ if type_ == ATTR:
40
+ if len(item) != 3:
41
+ raise ValueError("ATTR malformed")
42
+ self.attrs[item[1]] = item[2]
43
+ elif type_ == NODE:
44
+ if len(item) != 3:
45
+ raise ValueError("NODE malformed")
46
+ self.nodes.append(Node(item[1], item[2]))
47
+ elif type_ == EDGE:
48
+ if len(item) != 4:
49
+ raise ValueError("EDGE malformed")
50
+ self.edges.append(Edge(item[1], item[2], item[3]))
51
+ else:
52
+ raise ValueError("Unknown item {}".format(item[0]))
@@ -2,7 +2,7 @@
2
2
 
3
3
  For want of a horseshoe nail, a kingdom was lost, or so the saying goes.
4
4
 
5
- Output the full text of this proverbial rhyme:
5
+ Given a list of inputs, generate the relevant proverb. For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme:
6
6
 
7
7
  ```text
8
8
  For want of a nail the shoe was lost.
@@ -11,8 +11,11 @@ For want of a horse the rider was lost.
11
11
  For want of a rider the message was lost.
12
12
  For want of a message the battle was lost.
13
13
  For want of a battle the kingdom was lost.
14
- And all for the want of a horseshoe nail.
14
+ And all for the want of a nail.
15
15
  ```
16
+
17
+ Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. No line of the output text should be a static, unchanging string; all should vary according to the input given.
18
+
16
19
  ## Submitting Exercises
17
20
 
18
21
  Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
@@ -1,7 +1,7 @@
1
- def proverb(itens, qualifier=''):
1
+ def proverb(rhyme_items):
2
+ if not rhyme_items:
3
+ return ""
2
4
  phrases = ['For want of a {0} the {1} was lost.'.format(el1, el2)
3
- for el1, el2 in zip(itens, itens[1:])]
4
- qualifier += ' ' if qualifier else ''
5
- phrases.append('And all for the want of a {0}{1}.'.format(qualifier,
6
- itens[0]))
5
+ for el1, el2 in zip(rhyme_items, rhyme_items[1:])]
6
+ phrases.append('And all for the want of a {0}.'.format(rhyme_items[0]))
7
7
  return '\n'.join(phrases)
@@ -1,2 +1,2 @@
1
- def proverb(rhyme_text):
1
+ def proverb(rhyme_items):
2
2
  pass