trackler 2.2.1.68 → 2.2.1.69

Sign up to get free protection for your applications and to get access to all the features.
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