trackler 2.0.8.51 → 2.0.8.52

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/tracks/cpp/.editorconfig +21 -0
  4. data/tracks/cpp/exercises/anagram/anagram_test.cpp +2 -1
  5. data/tracks/cpp/exercises/nucleotide-count/example.cpp +5 -1
  6. data/tracks/cpp/exercises/nucleotide-count/nucleotide_count_test.cpp +5 -0
  7. data/tracks/cpp/exercises/queen-attack/queen_attack_test.cpp +8 -5
  8. data/tracks/elixir/config.json +8 -0
  9. data/tracks/elixir/exercises/hello-world/hello_world.exs +1 -1
  10. data/tracks/elixir/exercises/scale-generator/example.exs +119 -0
  11. data/tracks/elixir/exercises/scale-generator/scale_generator.exs +83 -0
  12. data/tracks/elixir/exercises/scale-generator/scale_generator_test.exs +282 -0
  13. data/tracks/go/README.md +17 -4
  14. data/tracks/go/gen/gen.go +85 -15
  15. data/tracks/java/exercises/pascals-triangle/src/example/java/PascalsTriangleGenerator.java +21 -0
  16. data/tracks/java/exercises/pascals-triangle/src/test/java/PascalsTriangleGeneratorTest.java +86 -0
  17. data/tracks/javascript/.github/stale.yml +0 -0
  18. data/tracks/javascript/exercises/leap/HINT.md +52 -0
  19. data/tracks/javascript/exercises/leap/leap.js +6 -2
  20. data/tracks/objective-c/docs/TESTS.md +5 -4
  21. data/tracks/purescript/config.json +8 -0
  22. data/tracks/purescript/exercises/crypto-square/bower.json +18 -0
  23. data/tracks/purescript/exercises/crypto-square/examples/src/CryptoSquare.purs +63 -0
  24. data/tracks/purescript/exercises/crypto-square/src/CryptoSquare.purs +6 -0
  25. data/tracks/purescript/exercises/crypto-square/test/Main.purs +92 -0
  26. data/tracks/ruby/exercises/hello-world/.meta/.version +1 -1
  27. data/tracks/ruby/exercises/hello-world/example.tt +7 -5
  28. data/tracks/ruby/exercises/hello-world/hello_world_test.rb +4 -15
  29. data/tracks/ruby/lib/hello_world_cases.rb +5 -5
  30. metadata +14 -6
  31. data/tracks/clojurescript/.github/ISSUE_TEMPLATE.md +0 -9
  32. data/tracks/cpp/exercises/queen-attack/require_equal_containers.h +0 -88
  33. data/tracks/java/exercises/pascals-triangle/src/example/java/PascalsTriangle.java +0 -26
  34. data/tracks/java/exercises/pascals-triangle/src/test/java/PascalsTriangleTest.java +0 -85
@@ -181,10 +181,23 @@ directory within each exercise that makes use of a test cases generator. This
181
181
  *.meta* directory will be ignored when a user fetches an exercise.
182
182
 
183
183
  Whenever the shared JSON data changes, the test cases will need to be regenerated.
184
- To do this, make sure that the **x-common** repository has been cloned in the same
185
- parent-directory as the **xgo** repository. Then navigate into the **xgo**
186
- directory and run `go run exercises/<exercise>/.meta/gen.go`. You should see
187
- that the `<exercise>/cases_test.go` file has changed. Commit the change.
184
+ The generator will first look for a local copy of the **x-common** repository.
185
+ If there isn't one it will attempt to get the relevant json data for the
186
+ exercise from the **x-common** repository on GitHub.
187
+
188
+ To use a local copy of the **x-common** repository, make sure that it has been
189
+ cloned into the same parent-directory as the **xgo** repository.
190
+
191
+ ```sh
192
+ $ tree -L 1 .
193
+ .
194
+ ├── x-common
195
+ └── xgo
196
+ ```
197
+
198
+ To regenerate the test cases, navigate into the **xgo** directory and run
199
+ `go run exercises/<exercise>/.meta/gen.go`. You should see that the
200
+ `<exercise>/cases_test.go` file has changed. Commit the change.
188
201
 
189
202
  ## Pull requests
190
203
 
@@ -7,18 +7,20 @@ import (
7
7
  "fmt"
8
8
  "go/format"
9
9
  "io/ioutil"
10
+ "log"
11
+ "net/http"
10
12
  "os"
11
13
  "os/exec"
12
14
  "path/filepath"
13
15
  "runtime"
14
16
  "text/template"
17
+ "time"
15
18
  )
16
19
 
17
- // dirMetadata is the location of the x-common repository
18
- // on the filesystem.
19
- // We're making the assumption that the x-common repository
20
- // has been cloned to the same parent directory as the xgo
21
- // repository. E.g.
20
+ // dirMetadata is the location of the x-common repository on the filesystem.
21
+ // We're making the assumption that the x-common repository has been cloned to
22
+ // the same parent directory as the xgo repository.
23
+ // E.g.
22
24
  //
23
25
  // $ tree -L 1 .
24
26
  // .
@@ -31,7 +33,21 @@ var dirMetadata string
31
33
  // the exercise directory. Falls back to the present working directory.
32
34
  var dirExercise string
33
35
 
34
- // Header tells how the test data was generated, for display in the header of cases_test.go
36
+ // genClient creates an http client with a 10 second timeout so we don't get
37
+ // stuck waiting for a response.
38
+ var genClient = &http.Client{Timeout: 10 * time.Second}
39
+
40
+ const (
41
+ // canonicalDataURL is the URL for the raw canonical-data.json data,
42
+ // requires exercise name.
43
+ canonicalDataURL = "https://raw.githubusercontent.com/exercism/x-common/master/exercises/%s/canonical-data.json"
44
+ // commitsURL is the GitHub api endpoint for the canonical-data.json
45
+ // file commit history, requires exercise name.
46
+ commitsURL = "https://api.github.com/repos/exercism/x-common/commits?path=exercises/%s/canonical-data.json"
47
+ )
48
+
49
+ // Header tells how the test data was generated, for display in the header of
50
+ // cases_test.go
35
51
  type Header struct {
36
52
  // Ori is a deprecated short name for Origin.
37
53
  // TODO: Remove Ori once everything switches to Origin.
@@ -70,11 +86,21 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
70
86
  return errors.New("unable to determine current path")
71
87
  }
72
88
  jFile := filepath.Join("exercises", exercise, "canonical-data.json")
73
- // find and read the json source file
74
- jPath, jOrigin, jCommit := getPath(jFile)
89
+ // try to find and read the local json source file
90
+ log.Printf("[LOCAL] fetching %s test data\n", exercise)
91
+ jPath, jOrigin, jCommit := getLocal(jFile)
92
+ if jPath != "" {
93
+ log.Printf("[LOCAL] source: %s\n", jPath)
94
+ }
75
95
  jSrc, err := ioutil.ReadFile(filepath.Join(jPath, jFile))
76
96
  if err != nil {
77
- return err
97
+ // fetch json data remotely if there's no local file
98
+ log.Println("[LOCAL] No test data found")
99
+ log.Printf("[REMOTE] fetching %s test data\n", exercise)
100
+ jSrc, jOrigin, jCommit, err = getRemote(exercise)
101
+ if err != nil {
102
+ return err
103
+ }
78
104
  }
79
105
 
80
106
  // unmarshal the json source to a Go structure
@@ -91,7 +117,7 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
91
117
  Version string
92
118
  }
93
119
  if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
94
- return fmt.Errorf(`Didn't contain version: %v`, err)
120
+ return fmt.Errorf(`didn't contain version: %v`, err)
95
121
  }
96
122
 
97
123
  // package up a little meta data
@@ -107,7 +133,7 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
107
133
 
108
134
  // render the Go test cases
109
135
  var b bytes.Buffer
110
- if err = t.Execute(&b, &d); err != nil {
136
+ if err := t.Execute(&b, &d); err != nil {
111
137
  return err
112
138
  }
113
139
  // clean it up
@@ -119,17 +145,17 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
119
145
  return ioutil.WriteFile(filepath.Join(dirExercise, "cases_test.go"), src, 0666)
120
146
  }
121
147
 
122
- func getPath(jFile string) (jPath, jOrigin, jCommit string) {
148
+ func getLocal(jFile string) (jPath, jOrigin, jCommit string) {
123
149
  // Ideally draw from a .json which is pulled from the official x-common
124
150
  // repository. For development however, accept a file in current directory
125
151
  // if there is no .json in source control. Also allow an override in any
126
152
  // case by environment variable.
127
- if jPath = os.Getenv("EXTEST"); jPath > "" {
153
+ if jPath := os.Getenv("EXTEST"); jPath > "" {
128
154
  return jPath, "local file", "" // override
129
155
  }
130
156
  c := exec.Command("git", "log", "-1", "--oneline", jFile)
131
157
  c.Dir = dirMetadata
132
- ori, err := c.Output()
158
+ origin, err := c.Output()
133
159
  if err != nil {
134
160
  return "", "local file", "" // no source control
135
161
  }
@@ -137,5 +163,49 @@ func getPath(jFile string) (jPath, jOrigin, jCommit string) {
137
163
  return "", "local file", "" // not in source control
138
164
  }
139
165
  // good. return source control dir and commit.
140
- return c.Dir, "exercism/x-common", string(bytes.TrimSpace(ori))
166
+ return c.Dir, "exercism/x-common", string(bytes.TrimSpace(origin))
167
+ }
168
+
169
+ func getRemote(exercise string) (body []byte, jOrigin string, jCommit string, err error) {
170
+ url := fmt.Sprintf(canonicalDataURL, exercise)
171
+ resp, err := genClient.Get(url)
172
+ if err != nil {
173
+ return []byte{}, "", "", err
174
+ }
175
+ if resp.StatusCode != http.StatusOK {
176
+ return []byte{}, "", "", fmt.Errorf("error fetching remote data: %s", resp.Status)
177
+ }
178
+ defer resp.Body.Close()
179
+ body, err = ioutil.ReadAll(resp.Body)
180
+ if err != nil {
181
+ return []byte{}, "", "", err
182
+ }
183
+ c, err := getRemoteCommit(exercise)
184
+ if err != nil {
185
+ // we always expect to have the commit in the cases_test.go
186
+ // file, so return the error if we can't fetch it
187
+ return []byte{}, "", "", err
188
+ }
189
+ log.Printf("[REMOTE] source: %s\n", url)
190
+ return body, "exercism/x-common", c, nil
191
+ }
192
+
193
+ func getRemoteCommit(exercise string) (string, error) {
194
+ type Commits struct {
195
+ Sha string
196
+ Commit struct {
197
+ Message string
198
+ }
199
+ }
200
+ resp, err := genClient.Get(fmt.Sprintf(commitsURL, exercise))
201
+ if err != nil {
202
+ return "", err
203
+ }
204
+ defer resp.Body.Close()
205
+ var c []Commits
206
+ err = json.NewDecoder(resp.Body).Decode(&c)
207
+ if err != nil {
208
+ return "", err
209
+ }
210
+ return fmt.Sprintf("%s %s", c[0].Sha[0:7], c[0].Commit.Message), nil
141
211
  }
@@ -0,0 +1,21 @@
1
+ class PascalsTriangleGenerator {
2
+
3
+ int[][] generateTriangle(int rows) {
4
+ if (rows < 0) {
5
+ throw new IllegalArgumentException("Rows can't be negative!");
6
+ }
7
+
8
+ int[][] triangle = new int[rows][];
9
+
10
+ for (int i = 0; i < rows; i++) {
11
+ triangle[i] = new int[i + 1];
12
+ triangle[i][0] = 1;
13
+ for (int j = 1; j < i; j++) {
14
+ triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
15
+ }
16
+ triangle[i][i] = 1;
17
+ }
18
+ return triangle;
19
+ }
20
+
21
+ }
@@ -0,0 +1,86 @@
1
+ import org.junit.Before;
2
+ import org.junit.Test;
3
+ import org.junit.Ignore;
4
+ import org.junit.Rule;
5
+ import org.junit.rules.ExpectedException;
6
+
7
+
8
+ import static org.junit.Assert.assertArrayEquals;
9
+ import static org.junit.Assert.assertEquals;
10
+
11
+ /*
12
+ * version: 1.0.0
13
+ */
14
+ public class PascalsTriangleGeneratorTest {
15
+
16
+ private PascalsTriangleGenerator pascalsTriangleGenerator;
17
+
18
+ @Before
19
+ public void setUp() {
20
+ pascalsTriangleGenerator = new PascalsTriangleGenerator();
21
+ }
22
+
23
+ @Rule
24
+ public ExpectedException thrown = ExpectedException.none();
25
+
26
+ @Test
27
+ public void testTriangleWithZeroRows() {
28
+ int[][] expectedOutput = new int[][]{};
29
+
30
+ assertArrayEquals(expectedOutput, pascalsTriangleGenerator.generateTriangle(0));
31
+ }
32
+
33
+ @Ignore
34
+ @Test
35
+ public void testTriangleWithOneRow() {
36
+ int[][] expectedOutput = new int[][]{
37
+ {1}
38
+ };
39
+
40
+ assertArrayEquals(expectedOutput, pascalsTriangleGenerator.generateTriangle(1));
41
+ }
42
+
43
+ @Ignore
44
+ @Test
45
+ public void testTriangleWithTwoRows() {
46
+ int[][] expectedOutput = new int[][]{
47
+ {1},
48
+ {1, 1}
49
+ };
50
+
51
+ assertArrayEquals(expectedOutput, pascalsTriangleGenerator.generateTriangle(2));
52
+ }
53
+
54
+ @Ignore
55
+ @Test
56
+ public void testTriangleWithThreeRows() {
57
+ int[][] expectedOutput = new int[][]{
58
+ {1},
59
+ {1, 1},
60
+ {1, 2, 1}
61
+ };
62
+
63
+ assertArrayEquals(expectedOutput, pascalsTriangleGenerator.generateTriangle(3));
64
+ }
65
+
66
+ @Ignore
67
+ @Test
68
+ public void testTriangleWithFourRows() {
69
+ int[][] expectedOutput = new int[][]{
70
+ {1},
71
+ {1, 1},
72
+ {1, 2, 1},
73
+ {1, 3, 3, 1}
74
+ };
75
+
76
+ assertArrayEquals(expectedOutput, pascalsTriangleGenerator.generateTriangle(4));
77
+ }
78
+
79
+ @Ignore
80
+ @Test
81
+ public void testValidatesNotNegativeRows() {
82
+ thrown.expect(IllegalArgumentException.class);
83
+ pascalsTriangleGenerator.generateTriangle(-1);
84
+ }
85
+
86
+ }
File without changes
@@ -0,0 +1,52 @@
1
+ This is the first test for this exercise:
2
+
3
+ ```javascript
4
+ it('is not very common', function() {
5
+ var year = new Year(2015);
6
+ expect(year.isLeap()).toBe(false);
7
+ });
8
+ ```
9
+
10
+ Each test in the exercise follows the same pattern:
11
+
12
+ 1. A new Year is instantiated with a value and stored in a variable: year.
13
+ 2. The test calls an instance method, isLeap(), from the variable `year`.
14
+
15
+ The `Year` function is a constructor, which means that it is specifically designed to provide a template for new objects.
16
+
17
+ When a new `Year` object is created:
18
+
19
+ ```javascript
20
+ var year = new Year(2015);
21
+ ```
22
+
23
+ We expect `year` to, at the very least, have a specific value (in this case 2015) assigned to it. Otherwise it would not represent an actual year.
24
+
25
+ This means that we must store the value passed as a parameter when the new `Year` is created (2015), so that the new `Year` can access it.
26
+
27
+ The way a constructor stores these fundamental values (instance variables) is like this:
28
+
29
+ ```javascript
30
+ var Constructor = function(input) {
31
+ this.value = input;
32
+ };
33
+ ```
34
+
35
+ The `this` in the constructor refers to the newly created instance of the `Constructor`.
36
+
37
+ Once the input (parameter) is stored in an instance variable, any instance methods, such as:
38
+
39
+ ```javascript
40
+ Constructor.prototype.instanceMethod = function() {
41
+ // this method can now access the input by calling `this.value`
42
+ };
43
+ ```
44
+
45
+ The instance method accesses the input using `this.value` (in this example).
46
+
47
+ This is why code needs to be written in two separate functions:
48
+
49
+ 1. The first function, `Year`, is a constructor that serves as a template for year objects that are created with their value (such as 2015).
50
+ This function needs to store the input when a new `Year` is created.
51
+ 2. The second function, isLeap(), is an instance method that is called from a new `year`.
52
+ This function (method) should contain the logic to determine if the given year is a leap year.
@@ -3,9 +3,13 @@
3
3
  // convenience to get you started writing code faster.
4
4
  //
5
5
 
6
- var Year = function() {};
6
+ var Year = function(input) {
7
+ //
8
+ // YOUR CODE GOES HERE
9
+ //
10
+ };
7
11
 
8
- Year.prototype.isLeap = function(input) {
12
+ Year.prototype.isLeap = function() {
9
13
  //
10
14
  // YOUR CODE GOES HERE
11
15
  //
@@ -29,19 +29,20 @@ __Note:__ If you receive the error "No visible `@interface` for ExerciseName dec
29
29
 
30
30
  ### A Test Runner
31
31
 
32
- An alternative to manually generating the project file is to use a test runner utility, written in ruby, that will create a project file for you with the test file, header file and source file.
32
+ An alternative to manually generating the project file is to use a test runner utility written in ruby, [`objc`](https://rubygems.org/gems/objc/), that will create a project file for you with the test file, header file and source file.
33
33
 
34
34
  ```bash
35
35
  $ gem install objc
36
- $ brew install xctool
37
36
  ```
38
37
 
39
38
  Run the tests with:
40
39
 
41
40
  ```bash
42
- $ objc ExerciseName
41
+ $ objc -x ExerciseName
43
42
  ```
44
43
 
45
- The objc utility uses the exercise name to find the test file, `ExerciseNameTest.m`, the header file, `ExerciseName.h` and source file `ExerciseName.m`. The files are inserted into a temporary Xcode Project and then `xctool` is used to run the tests for the project.
44
+ (Note the `-x`/`--xcodebuild` flag, which specifies using `xcodebuild` instead of `xctool`. The latter does not work with Xcode's latest releases.)
45
+
46
+ The objc utility uses the exercise name to find the test file, `ExerciseNameTest.m`, the header file, `ExerciseName.h` and source file `ExerciseName.m`. The files are inserted into a temporary Xcode Project and then `xcodebuild` is used to run the tests for the project.
46
47
 
47
48
  While `objc` makes it so you never have to launch Xcode to complete these exercises, the error messages and feedback through the command-line are not as clear as through the Xcode user interface.
@@ -136,6 +136,14 @@
136
136
  "topics": [
137
137
  "strings"
138
138
  ]
139
+ },
140
+ {
141
+ "slug": "crypto-square",
142
+ "difficulty": 1,
143
+ "topics": [
144
+ "strings",
145
+ "matrices"
146
+ ]
139
147
  }
140
148
  ],
141
149
  "deprecated": [
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "crypto-square",
3
+ "ignore": [
4
+ "**/.*",
5
+ "node_modules",
6
+ "bower_components",
7
+ "output"
8
+ ],
9
+ "dependencies": {
10
+ "purescript-prelude": "^3.0.0",
11
+ "purescript-strings": "^3.0.0",
12
+ "purescript-unicode": "^3.0.1"
13
+ },
14
+ "devDependencies": {
15
+ "purescript-psci-support": "^3.0.0",
16
+ "purescript-test-unit": "^11.0.0"
17
+ }
18
+ }
@@ -0,0 +1,63 @@
1
+ module CryptoSquare
2
+ ( normalizedPlaintext
3
+ , plaintextSegments
4
+ , encoded
5
+ , ciphertext
6
+ ) where
7
+
8
+ import Prelude
9
+ import Data.Array as A
10
+ import Data.Array (filter, fromFoldable, replicate, toUnfoldable, (:))
11
+ import Data.Char.Unicode (isAlphaNum)
12
+ import Data.Foldable (maximum)
13
+ import Data.Int (ceil, toNumber)
14
+ import Data.List (transpose)
15
+ import Data.Maybe (fromMaybe)
16
+ import Data.String (drop, fromCharArray, joinWith, length, take, toCharArray, toLower)
17
+ import Math (sqrt)
18
+
19
+ normalizedPlaintext :: String -> String
20
+ normalizedPlaintext
21
+ = toCharArray
22
+ >>> filter isAlphaNum
23
+ >>> fromCharArray
24
+ >>> toLower
25
+
26
+ plaintextSegments :: String -> Array String
27
+ plaintextSegments str = toSquare norm
28
+ where norm = normalizedPlaintext str
29
+ cols = sqrt (length norm # toNumber) # ceil
30
+ toSquare "" = []
31
+ toSquare s = take cols s : toSquare (drop cols s)
32
+
33
+ transposeArray :: forall a. Array (Array a) -> Array (Array a)
34
+ transposeArray
35
+ = toUnfoldable
36
+ >>> map toUnfoldable
37
+ >>> transpose
38
+ >>> map fromFoldable
39
+ >>> fromFoldable
40
+
41
+ encoded :: String -> String
42
+ encoded = plaintextSegments
43
+ >>> map toCharArray
44
+ >>> transposeArray
45
+ >>> map fromCharArray
46
+ >>> joinWith ""
47
+
48
+ spaces :: Int -> Array Char
49
+ spaces n = replicate n ' '
50
+
51
+ equalPad :: Array (Array Char) -> Array (Array Char)
52
+ equalPad arr = map pad arr
53
+ where width = fromMaybe 0 (maximum $ map A.length arr)
54
+ pad el = el <> spaces (width - A.length el)
55
+
56
+ ciphertext :: String -> String
57
+ ciphertext = plaintextSegments
58
+ >>> map toCharArray
59
+ >>> transposeArray
60
+ >>> equalPad
61
+ >>> map fromCharArray
62
+ >>> joinWith " "
63
+