trackler 2.0.3.0 → 2.0.3.1

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/fixtures/common/exercises/no-metadata/description.md +1 -0
  3. data/lib/trackler/problem.rb +12 -0
  4. data/lib/trackler/problems.rb +8 -12
  5. data/lib/trackler/version.rb +1 -1
  6. data/tracks/bash/README.md +2 -0
  7. data/tracks/c/config.json +9 -2
  8. data/tracks/c/exercises/sieve/makefile +15 -0
  9. data/tracks/c/exercises/sieve/src/example.c +50 -0
  10. data/tracks/c/exercises/sieve/src/sieve.h +10 -0
  11. data/tracks/c/exercises/sieve/test/test_sieve.c +113 -0
  12. data/tracks/c/exercises/sieve/test/vendor/unity.c +1300 -0
  13. data/tracks/c/exercises/sieve/test/vendor/unity.h +274 -0
  14. data/tracks/c/exercises/sieve/test/vendor/unity_internals.h +701 -0
  15. data/tracks/coffeescript/.travis.yml +1 -0
  16. data/tracks/coldfusion/.travis.yml +1 -0
  17. data/tracks/crystal/Makefile +18 -15
  18. data/tracks/crystal/README.md +5 -5
  19. data/tracks/crystal/config.json +6 -0
  20. data/tracks/crystal/exercises/acronym/src/acronym.cr +1 -0
  21. data/tracks/crystal/exercises/anagram/src/anagram.cr +1 -0
  22. data/tracks/crystal/exercises/atbash-cipher/src/atbash_cipher.cr +1 -0
  23. data/tracks/crystal/exercises/binary/src/binary.cr +1 -0
  24. data/tracks/crystal/exercises/bob/src/bob.cr +1 -0
  25. data/tracks/crystal/exercises/bracket-push/src/bracket_push.cr +1 -0
  26. data/tracks/crystal/exercises/difference-of-squares/spec/difference_of_squares_spec.cr +44 -0
  27. data/tracks/crystal/exercises/difference-of-squares/src/example.cr +15 -0
  28. data/tracks/crystal/exercises/forth/src/forth.cr +1 -0
  29. data/tracks/crystal/exercises/gigasecond/src/gigasecond.cr +1 -0
  30. data/tracks/crystal/exercises/hamming/src/hamming.cr +1 -0
  31. data/tracks/crystal/exercises/hello-world/src/hello_world.cr +1 -0
  32. data/tracks/crystal/exercises/largest-series-product/src/largest_series_product.cr +1 -0
  33. data/tracks/crystal/exercises/leap/src/leap.cr +1 -0
  34. data/tracks/crystal/exercises/pangram/src/pangram.cr +1 -0
  35. data/tracks/crystal/exercises/raindrops/src/raindrops.cr +1 -0
  36. data/tracks/crystal/exercises/react/src/react.cr +1 -0
  37. data/tracks/crystal/exercises/rna-transcription/src/rna_transcription.cr +1 -0
  38. data/tracks/crystal/exercises/roman-numerals/src/roman_numerals.cr +1 -0
  39. data/tracks/crystal/exercises/sieve/src/sieve.cr +1 -0
  40. data/tracks/crystal/src/generator/exercises/difference_of_squares.cr +39 -0
  41. data/tracks/go/exercises/allergies/allergies_test.go +2 -2
  42. data/tracks/go/exercises/anagram/anagram_test.go +3 -3
  43. data/tracks/go/exercises/grade-school/grade_school_test.go +3 -3
  44. data/tracks/go/exercises/secret-handshake/secret_handshake_test.go +1 -1
  45. data/tracks/java/config.json +13 -1
  46. data/tracks/java/exercises/binary-search-tree/build.gradle +18 -0
  47. data/tracks/java/exercises/binary-search-tree/src/example/java/BST.java +122 -0
  48. data/tracks/java/exercises/binary-search-tree/src/main/java/.keep +0 -0
  49. data/tracks/java/exercises/binary-search-tree/src/test/java/.keep +0 -0
  50. data/tracks/java/exercises/binary-search-tree/src/test/java/BSTTest.java +163 -0
  51. data/tracks/java/exercises/pythagorean-triplet/build.gradle +18 -0
  52. data/tracks/java/exercises/pythagorean-triplet/src/example/java/PythagoreanTriplet.java +117 -0
  53. data/tracks/java/exercises/pythagorean-triplet/src/main/java/PythagoreanTriplet.java +3 -0
  54. data/tracks/java/exercises/pythagorean-triplet/src/test/java/PythagoreanTripletTest.java +95 -0
  55. data/tracks/java/exercises/settings.gradle +2 -0
  56. data/tracks/julia/README.md +16 -0
  57. data/tracks/perl5/README.md +2 -0
  58. data/tracks/perl6/README.md +2 -1
  59. data/tracks/php/config.json +5 -0
  60. data/tracks/php/exercises/sieve/example.php +29 -0
  61. data/tracks/php/exercises/sieve/sieve_test.php +201 -0
  62. data/tracks/pony/.travis.yml +1 -0
  63. data/tracks/python/exercises/anagram/anagram_test.py +1 -0
  64. data/tracks/python/exercises/beer-song/beer_song_test.py +1 -0
  65. data/tracks/python/exercises/binary-search/binary_search_test.py +1 -0
  66. data/tracks/python/exercises/binary/binary_test.py +1 -0
  67. data/tracks/python/exercises/bob/bob_test.py +1 -0
  68. data/tracks/python/exercises/clock/clock_test.py +1 -0
  69. data/tracks/python/exercises/etl/etl_test.py +1 -0
  70. data/tracks/python/exercises/gigasecond/gigasecond_test.py +1 -0
  71. data/tracks/python/exercises/grains/grains_test.py +1 -0
  72. data/tracks/python/exercises/leap/leap_test.py +1 -0
  73. data/tracks/python/exercises/meetup/meetup_test.py +1 -0
  74. data/tracks/python/exercises/nucleotide-count/nucleotide_count_test.py +1 -0
  75. data/tracks/python/exercises/ocr-numbers/example.py +1 -0
  76. data/tracks/python/exercises/ocr-numbers/ocr_test.py +1 -0
  77. data/tracks/python/exercises/perfect-numbers/perfect_numbers_test.py +1 -0
  78. data/tracks/python/exercises/phone-number/phone_number_test.py +1 -0
  79. data/tracks/python/exercises/pig-latin/pig_latin_test.py +1 -0
  80. data/tracks/python/exercises/point-mutations/point_mutations_test.py +1 -0
  81. data/tracks/python/exercises/poker/poker_test.py +1 -0
  82. data/tracks/python/exercises/prime-factors/prime_factors_test.py +1 -0
  83. data/tracks/python/exercises/pythagorean-triplet/example.py +2 -3
  84. data/tracks/python/exercises/rail-fence-cipher/rail_fence_cipher_test.py +1 -0
  85. data/tracks/python/exercises/raindrops/raindrops_test.py +1 -0
  86. data/tracks/python/exercises/rectangles/rectangles_count_test.py +1 -0
  87. data/tracks/python/exercises/robot-simulator/robot_simulator_test.py +1 -0
  88. data/tracks/python/exercises/roman-numerals/roman_numerals_test.py +1 -0
  89. data/tracks/python/exercises/run-length-encoding/run_length_test.py +1 -0
  90. data/tracks/python/exercises/say/say_test.py +1 -0
  91. data/tracks/python/exercises/scrabble-score/scrabble_score_test.py +1 -0
  92. data/tracks/python/exercises/sieve/sieve_test.py +1 -0
  93. data/tracks/python/exercises/space-age/space_age_test.py +1 -0
  94. data/tracks/python/exercises/triangle/triangle_test.py +1 -0
  95. data/tracks/python/exercises/word-count/word_count_test.py +1 -0
  96. data/tracks/python/requirements-travis.txt +1 -1
  97. data/tracks/ruby/docs/24pullrequests.md +21 -0
  98. data/tracks/ruby/exercises/bowling/.version +1 -1
  99. data/tracks/ruby/exercises/bowling/bowling_test.rb +44 -15
  100. data/tracks/ruby/exercises/bowling/example.rb +10 -5
  101. data/tracks/ruby/exercises/bowling/example.tt +4 -2
  102. data/tracks/ruby/lib/bowling_cases.rb +1 -1
  103. data/tracks/scala/exercises/bank-account/HINTS.md +11 -0
  104. data/tracks/scala/exercises/bank-account/example.scala +2 -4
  105. data/tracks/scala/exercises/bank-account/src/main/scala/BankAccount.scala +13 -0
  106. data/tracks/scala/exercises/bank-account/src/test/scala/BankAccountTest.scala +5 -5
  107. data/tracks/scala/exercises/hamming/HINTS.md +57 -0
  108. data/tracks/scala/exercises/hello-world/HINTS.md +8 -0
  109. data/tracks/scala/exercises/nucleotide-count/HINTS.md +61 -0
  110. metadata +48 -2
@@ -52,5 +52,6 @@ class RaindropsTest(unittest.TestCase):
52
52
  def test_12121(self):
53
53
  self.assertEqual("12121", raindrops(12121))
54
54
 
55
+
55
56
  if __name__ == '__main__':
56
57
  unittest.main()
@@ -68,5 +68,6 @@ class WordTest(unittest.TestCase):
68
68
  ]
69
69
  assert 2 == count(lines)
70
70
 
71
+
71
72
  if __name__ == '__main__':
72
73
  unittest.main()
@@ -69,5 +69,6 @@ class RobotTests(unittest.TestCase):
69
69
  self.assertEqual((11, 5), robot.coordinates)
70
70
  self.assertEqual(NORTH, robot.bearing)
71
71
 
72
+
72
73
  if __name__ == '__main__':
73
74
  unittest.main()
@@ -29,5 +29,6 @@ class RomanTest(unittest.TestCase):
29
29
  for arabic, numeral in self.numerals.items():
30
30
  self.assertEqual(numeral, roman_numerals.numeral(arabic))
31
31
 
32
+
32
33
  if __name__ == '__main__':
33
34
  unittest.main()
@@ -33,5 +33,6 @@ class WordCountTests(unittest.TestCase):
33
33
  def test_decode_unicode(self):
34
34
  self.assertMultiLineEqual('⏰⚽⚽⚽⭐⭐⏰', decode('⏰3⚽2⭐⏰'))
35
35
 
36
+
36
37
  if __name__ == '__main__':
37
38
  unittest.main()
@@ -65,5 +65,6 @@ class SayTest(unittest.TestCase):
65
65
  "one hundred and twenty-three",
66
66
  say(987654321123))
67
67
 
68
+
68
69
  if __name__ == '__main__':
69
70
  unittest.main()
@@ -25,5 +25,6 @@ class WordTest(unittest.TestCase):
25
25
  def test_scores_are_case_insensitive(self):
26
26
  self.assertEqual(41, score("OxyphenButazone"))
27
27
 
28
+
28
29
  if __name__ == '__main__':
29
30
  unittest.main()
@@ -29,5 +29,6 @@ class SieveTest(unittest.TestCase):
29
29
  953, 967, 971, 977, 983, 991, 997]
30
30
  self.assertEqual(expected, sieve(1000))
31
31
 
32
+
32
33
  if __name__ == '__main__':
33
34
  unittest.main()
@@ -47,5 +47,6 @@ class SpaceAgeTest(unittest.TestCase):
47
47
  self.assertEqual(260.16, age.on_earth())
48
48
  self.assertEqual(1.58, age.on_neptune())
49
49
 
50
+
50
51
  if __name__ == '__main__':
51
52
  unittest.main()
@@ -63,5 +63,6 @@ class TriangleTests(unittest.TestCase):
63
63
  Triangle, 7, 3, 2
64
64
  )
65
65
 
66
+
66
67
  if __name__ == '__main__':
67
68
  unittest.main()
@@ -84,5 +84,6 @@ class WordCountTests(unittest.TestCase):
84
84
  word_count('до🖖свидания!')
85
85
  )
86
86
 
87
+
87
88
  if __name__ == '__main__':
88
89
  unittest.main()
@@ -1,3 +1,3 @@
1
- flake8>=3.0,<3.0.99
1
+ flake8>=3.2,<3.2.99
2
2
  pep8>=1.7,<1.7.99
3
3
  pyflakes>=1.3,<1.3.99
@@ -0,0 +1,21 @@
1
+ ## 24 Pull Requests for Exercism - Ruby Edition!
2
+
3
+ We are welcoming contributors to the project.
4
+
5
+ Look for the following labels in our issue list:
6
+
7
+ * first-timers only
8
+ * good first patch
9
+ * bug
10
+ * enhancement
11
+
12
+ Or notice something to contribute? Documentation not quite up to par?
13
+ Something missing? Submitting and reviewing exercises on Exercism and
14
+ notice something you don't like? Create a pull request!
15
+
16
+ Or notice something to contribute? Documentation not quite up to par?
17
+ Something missing? Create a pull request!
18
+
19
+ Check out the [Getting Involved in a Track](https://github.com/exercism/exercism.io/blob/master/docs/getting-involved-in-a-track.md) documentation, and some [ideas for things we are looking for](http://exercism.io/languages/ruby/todo) for exercises that are needed to be implemented.
20
+
21
+ We are available to answer questions in the [![Join the chat at https://gitter.im/exercism/xruby](https://badges.gitter.im/exercism/xruby.svg)](https://gitter.im/exercism/xruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge).
@@ -1 +1 @@
1
- 2
1
+ 3
@@ -3,8 +3,8 @@ gem 'minitest', '>= 5.0.0'
3
3
  require 'minitest/autorun'
4
4
  require_relative 'bowling'
5
5
 
6
- # Test data version:
7
- # 0a51cfc
6
+ # Test data version: c59c2c4
7
+ #
8
8
  class BowlingTest < Minitest::Test
9
9
  def setup
10
10
  @game = Game.new
@@ -50,7 +50,7 @@ class BowlingTest < Minitest::Test
50
50
  assert_equal 17, @game.score
51
51
  end
52
52
 
53
- def test_a_strike_earns_ten_points_in_frame_with_a_single_roll
53
+ def test_a_strike_earns_ten_points_in_a_frame_with_a_single_roll
54
54
  skip
55
55
  roll([10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
56
56
  assert_equal 10, @game.score
@@ -100,7 +100,7 @@ class BowlingTest < Minitest::Test
100
100
 
101
101
  def test_rolls_can_not_score_negative_points
102
102
  skip
103
- assert_raises StandardError do
103
+ assert_raises Game::BowlingError do
104
104
  roll([-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
105
105
  @game.score
106
106
  end
@@ -108,7 +108,7 @@ class BowlingTest < Minitest::Test
108
108
 
109
109
  def test_a_roll_can_not_score_more_than_10_points
110
110
  skip
111
- assert_raises StandardError do
111
+ assert_raises Game::BowlingError do
112
112
  roll([11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
113
113
  @game.score
114
114
  end
@@ -116,23 +116,53 @@ class BowlingTest < Minitest::Test
116
116
 
117
117
  def test_two_rolls_in_a_frame_can_not_score_more_than_10_points
118
118
  skip
119
- assert_raises StandardError do
119
+ assert_raises Game::BowlingError do
120
120
  roll([5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
121
121
  @game.score
122
122
  end
123
123
  end
124
124
 
125
+ def test_bonus_roll_after_a_strike_in_the_last_frame_can_not_score_more_than_10_points
126
+ skip
127
+ assert_raises Game::BowlingError do
128
+ roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11])
129
+ @game.score
130
+ end
131
+ end
132
+
125
133
  def test_two_bonus_rolls_after_a_strike_in_the_last_frame_can_not_score_more_than_10_points
126
134
  skip
127
- assert_raises StandardError do
135
+ assert_raises Game::BowlingError do
128
136
  roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 6])
129
137
  @game.score
130
138
  end
131
139
  end
132
140
 
141
+ def test_two_bonus_rolls_after_a_strike_in_the_last_frame_can_score_more_than_10_points_if_one_is_a_strike
142
+ skip
143
+ roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6])
144
+ assert_equal 26, @game.score
145
+ end
146
+
147
+ def test_the_second_bonus_rolls_after_a_strike_in_the_last_frame_can_not_be_a_strike_if_the_first_one_is_not_a_strike
148
+ skip
149
+ assert_raises Game::BowlingError do
150
+ roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6, 10])
151
+ @game.score
152
+ end
153
+ end
154
+
155
+ def test_second_bonus_roll_after_a_strike_in_the_last_frame_can_not_score_than_10_points
156
+ skip
157
+ assert_raises Game::BowlingError do
158
+ roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 11])
159
+ @game.score
160
+ end
161
+ end
162
+
133
163
  def test_an_unstarted_game_can_not_be_scored
134
164
  skip
135
- assert_raises StandardError do
165
+ assert_raises Game::BowlingError do
136
166
  roll([])
137
167
  @game.score
138
168
  end
@@ -140,7 +170,7 @@ class BowlingTest < Minitest::Test
140
170
 
141
171
  def test_an_incomplete_game_can_not_be_scored
142
172
  skip
143
- assert_raises StandardError do
173
+ assert_raises Game::BowlingError do
144
174
  roll([0, 0])
145
175
  @game.score
146
176
  end
@@ -148,7 +178,7 @@ class BowlingTest < Minitest::Test
148
178
 
149
179
  def test_a_game_with_more_than_ten_frames_can_not_be_scored
150
180
  skip
151
- assert_raises StandardError do
181
+ assert_raises Game::BowlingError do
152
182
  roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
153
183
  @game.score
154
184
  end
@@ -156,7 +186,7 @@ class BowlingTest < Minitest::Test
156
186
 
157
187
  def test_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
158
188
  skip
159
- assert_raises StandardError do
189
+ assert_raises Game::BowlingError do
160
190
  roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10])
161
191
  @game.score
162
192
  end
@@ -164,7 +194,7 @@ class BowlingTest < Minitest::Test
164
194
 
165
195
  def test_both_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
166
196
  skip
167
- assert_raises StandardError do
197
+ assert_raises Game::BowlingError do
168
198
  roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10])
169
199
  @game.score
170
200
  end
@@ -172,7 +202,7 @@ class BowlingTest < Minitest::Test
172
202
 
173
203
  def test_bonus_roll_for_a_spare_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
174
204
  skip
175
- assert_raises StandardError do
205
+ assert_raises Game::BowlingError do
176
206
  roll([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3])
177
207
  @game.score
178
208
  end
@@ -194,9 +224,8 @@ class BowlingTest < Minitest::Test
194
224
  #
195
225
  # If you are curious, read more about constants on RubyDoc:
196
226
  # http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html
197
-
198
227
  def test_bookkeeping
199
228
  skip
200
- assert_equal 2, BookKeeping::VERSION
229
+ assert_equal 3, BookKeeping::VERSION
201
230
  end
202
231
  end
@@ -1,11 +1,16 @@
1
1
  module BookKeeping
2
- VERSION = 2
2
+ VERSION = 3
3
3
  end
4
4
 
5
5
  class Game
6
6
  PINS = { MIN: 0, MAX: 10 }.freeze
7
7
  at_exit { public :roll, :score }
8
8
 
9
+
10
+ class BowlingError < StandardError; end
11
+ class RollError < BowlingError; end
12
+ class GameError < BowlingError; end
13
+
9
14
  private
10
15
 
11
16
  def initialize
@@ -20,9 +25,9 @@ class Game
20
25
  end
21
26
 
22
27
  def validate(pins)
23
- raise StandardError, 'Invalid number of pins' unless (PINS[:MIN]..PINS[:MAX]).cover?(pins)
24
- raise StandardError, 'Too many pins in frame' unless valid_frame?(pins)
25
- raise StandardError, 'Game is over, no rolls allowed' if game_complete?
28
+ raise RollError, 'Invalid number of pins' unless (PINS[:MIN]..PINS[:MAX]).cover?(pins)
29
+ raise RollError, 'Too many pins in frame' unless valid_frame?(pins)
30
+ raise GameError, 'Game is over, no rolls allowed' if game_complete?
26
31
  end
27
32
 
28
33
  def valid_frame?(pins)
@@ -33,7 +38,7 @@ class Game
33
38
  end
34
39
 
35
40
  def score
36
- raise StandardError, 'Score unavailable until end of the game' unless game_complete?
41
+ raise GameError, 'Score unavailable until end of the game' unless game_complete?
37
42
  @score_card.values.map.with_index(1) do |f, i|
38
43
  score_frame(f, i)
39
44
  end.reduce(:+)
@@ -3,8 +3,8 @@ gem 'minitest', '>= 5.0.0'
3
3
  require 'minitest/autorun'
4
4
  require_relative 'bowling'
5
5
 
6
- # Test data version:
7
- # <%= sha1 %>
6
+ # Test data version: <%= sha1 %>
7
+ #
8
8
  class BowlingTest < Minitest::Test
9
9
  def setup
10
10
  @game = Game.new
@@ -13,11 +13,13 @@ class BowlingTest < Minitest::Test
13
13
  def roll(rolls)
14
14
  rolls.each { |pins| @game.roll(pins) }
15
15
  end
16
+
16
17
  <% test_cases.each do |test_case| %>
17
18
  def <%= test_case.test_name %>
18
19
  <%= test_case.skipped %>
19
20
  <%= test_case.work_load %>
20
21
  end
22
+
21
23
  <% end %>
22
24
  <%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
23
25
  def test_bookkeeping
@@ -20,7 +20,7 @@ class BowlingCase < OpenStruct
20
20
  def assert
21
21
  if assert_error?
22
22
  [
23
- 'assert_raises StandardError do',
23
+ 'assert_raises Game::BowlingError do',
24
24
  " #{roll}",
25
25
  ' @game.score',
26
26
  'end'
@@ -0,0 +1,11 @@
1
+ ## Hints
2
+
3
+ This exercise is testing mutable state that can be accessed saftely from multiple threads. Scala provides a variety of ways to protect
4
+ mutable state. For developers familiar with Java concurrency, Scala can utilize the Java concurrency support such as the Java synchronized block.
5
+
6
+ ### Common Pitfalls
7
+
8
+ In Scala there are two ways to achieve mutable state: Use a "var" or a mutable object.
9
+ Two common mistakes here are:
10
+ - Do not use a "var" that is also a mutable object. One is enough, but not both together.
11
+ - Don't expose the "var" or mutable object to the outside world. So make them "private" and change the mutable object into immutable before you return it as a value.
@@ -23,8 +23,6 @@ protected case class Account(var balance: Option[Int] = Some(0)) extends BankAcc
23
23
  }
24
24
  }
25
25
 
26
- object BankAccount {
27
- def apply(): BankAccount = Account()
26
+ object Bank {
27
+ def openAccount(): BankAccount = Account()
28
28
  }
29
-
30
-
@@ -0,0 +1,13 @@
1
+ trait BankAccount {
2
+
3
+ def closeAccount(): Unit
4
+
5
+ def getBalance: Option[Int]
6
+
7
+ def incrementBalance(increment: Int): Option[Int]
8
+ }
9
+
10
+ object Bank {
11
+ def openAccount(): BankAccount = ???
12
+ }
13
+
@@ -3,12 +3,12 @@ import org.scalatest.{Matchers, FunSuite}
3
3
 
4
4
  class BankAccountTest extends FunSuite with Matchers with Conductors with IntegrationPatience {
5
5
  test("open account") {
6
- BankAccount().getBalance should be (Some(0))
6
+ Bank.openAccount().getBalance should be (Some(0))
7
7
  }
8
8
 
9
9
  test("incrementing and checking balance") {
10
10
  pending
11
- val acct = BankAccount()
11
+ val acct = Bank.openAccount()
12
12
  acct.getBalance should be (Some(0))
13
13
  acct.incrementBalance(10) should be (Some(10))
14
14
  acct.getBalance should be (Some(10))
@@ -18,7 +18,7 @@ class BankAccountTest extends FunSuite with Matchers with Conductors with Integr
18
18
 
19
19
  test("closed account should hold no balance") {
20
20
  pending
21
- val acct = BankAccount()
21
+ val acct = Bank.openAccount()
22
22
  acct.closeAccount()
23
23
  acct.incrementBalance(10)
24
24
  acct.incrementBalance(10)
@@ -30,7 +30,7 @@ class BankAccountTest extends FunSuite with Matchers with Conductors with Integr
30
30
  val conductor = new Conductor
31
31
  import conductor._
32
32
 
33
- val acct = BankAccount()
33
+ val acct = Bank.openAccount()
34
34
 
35
35
  thread("t1") {
36
36
  acct.incrementBalance(10)
@@ -54,7 +54,7 @@ class BankAccountTest extends FunSuite with Matchers with Conductors with Integr
54
54
  val conductor = new Conductor
55
55
  import conductor._
56
56
 
57
- val acct = BankAccount()
57
+ val acct = Bank.openAccount()
58
58
 
59
59
  thread("t1") {
60
60
  for (a <- 1 to 10)
@@ -0,0 +1,57 @@
1
+ ## Hints
2
+ `Option` is used to indicate a computation that may possibly have no useful result
3
+ (for example due to an error or invalid input).
4
+ If you are unfamiliar with `Option` you may read [this tutorial](http://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html).
5
+ `Option` is a so-called [Monad](https://en.wikipedia.org/wiki/Monad_(functional_programming)) which covers a "computational aspect", in this case possible absence of a value.
6
+ Proper use of Monads can result in very concise yet elegant
7
+ and readable code. Improper use can easily result in the contrary.
8
+ Watch [this video](https://www.youtube.com/watch?v=Mw_Jnn_Y5iA) to learn more.
9
+ #### Common pitfalls that you should avoid
10
+ There are a few rules of thumbs for `Option`:
11
+ 1. If you don't need it don't use it. Instead of
12
+ ```scala
13
+ def add1(x: Int): Option[Int] = Some(x + 1)
14
+ ```
15
+ better have
16
+ ```scala
17
+ def add1(x: Int): Int = x + 1
18
+ ```
19
+ (there is `Option.map` to apply such simple functions,
20
+ so you don't have to clutter them with `Option`).
21
+ 2. Don't "unwrap" if you don't really need to.
22
+ Often there are built-in functions for your purpose. Indicators of premature
23
+ unwrapping are `isDefined/isEmpty` or pattern matching. Instead of
24
+ ```scala
25
+ val x: Option[Int] = ...
26
+
27
+ if (x.isDefined) x.get + 1 else 0
28
+ // or
29
+ x match {
30
+ case Some(n) => n + 1
31
+ case None => 0
32
+ }
33
+ ```
34
+ better have
35
+ ```scala
36
+ x map (_ + 1) getOrElse 0
37
+ ```
38
+ 3. Monads can be used inside a for-comprehension FTW.
39
+ This is advisable when you want to "compose" several `Option` instances. Instead of
40
+ ```scala
41
+ val xo: Option[Int] = ...
42
+ val yo: Option[Int] = ...
43
+ val zo: Option[Int] = ...
44
+
45
+ xo.flatMap(x =>
46
+ yo.flatMap(y =>
47
+ zo.map(z =>
48
+ x + y + z)))
49
+ ```
50
+ better have
51
+ ```scala
52
+ for {
53
+ x <- xo
54
+ y <- yo
55
+ z <- zo
56
+ } yield x + y + z
57
+ ```