trackler 2.0.3.0 → 2.0.3.1

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