Skip to content

Commit 8a56b2b

Browse files
authored
Merge branch 'edgecase:master' into bo-tato-observr-patch-1
2 parents 6fc6906 + 6f919cd commit 8a56b2b

5 files changed

Lines changed: 243 additions & 3 deletions

File tree

download/rubykoans.zip

1.38 KB
Binary file not shown.

src/about_hashes.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,14 @@ def test_default_value_with_block
113113
assert_equal __(["dos"]), hash[:two]
114114
assert_equal __([]), hash[:three]
115115
end
116+
117+
def test_default_value_attribute
118+
hash = Hash.new
119+
120+
assert_equal __(nil), hash[:some_key]
121+
122+
hash.default = 'peanut'
123+
124+
assert_equal __('peanut'), hash[:some_key]
125+
end
116126
end

src/about_keyword_arguments.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,20 @@ def test_keyword_arguments_with_wrong_number_of_arguments
2424
assert_match(/#{__("wrong number of arguments")}/, exception.message)
2525
end
2626

27-
# THINK ABOUT IT:
28-
#
29-
# Keyword arguments always have a default value, making them optional to the caller
27+
def method_with_mandatory_keyword_arguments(one:, two: 'two')
28+
[one, two]
29+
end
30+
31+
def test_mandatory_keyword_arguments
32+
assert_equal __(['one', 'two']), method_with_mandatory_keyword_arguments(one: 'one')
33+
assert_equal __([1, 2]), method_with_mandatory_keyword_arguments(two: 2, one: 1)
34+
end
35+
36+
def test_mandatory_keyword_arguments_without_mandatory_argument
37+
exception = assert_raise(___(ArgumentError)) do
38+
method_with_mandatory_keyword_arguments
39+
end
40+
assert_match(/#{__("missing keyword: :one")}/, exception.message)
41+
end
3042

3143
end

src/about_pattern_matching.rb

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
require File.expand_path(File.dirname(__FILE__) + '/neo')
2+
3+
class AboutPatternMatching < Neo::Koan
4+
5+
def test_pattern_may_not_match
6+
begin
7+
case [true, false]
8+
in [a, b] if a == b # The condition after pattern is called guard.
9+
:match
10+
end
11+
rescue Exception => ex
12+
# What exception has been caught?
13+
assert_equal __, ex.class
14+
end
15+
end
16+
17+
def test_we_can_use_else
18+
result = case [true, false]
19+
in [a, b] if a == b
20+
:match
21+
else
22+
:no_match
23+
end
24+
25+
assert_equal __, result
26+
end
27+
28+
# ------------------------------------------------------------------
29+
30+
def value_pattern(variable)
31+
case variable
32+
in 0
33+
:match_exact_value
34+
in 1..10
35+
:match_in_range
36+
in Integer
37+
:match_with_class
38+
else
39+
:no_match
40+
end
41+
end
42+
43+
def test_value_pattern
44+
assert_equal __, value_pattern(0)
45+
assert_equal __, value_pattern(5)
46+
assert_equal __, value_pattern(100)
47+
assert_equal __, value_pattern('Not a Number!')
48+
end
49+
50+
# ------------------------------------------------------------------
51+
# This pattern will bind variable to the value
52+
53+
def variable_pattern_with_binding(variable)
54+
case 0
55+
in variable
56+
variable
57+
else
58+
:no_match
59+
end
60+
end
61+
62+
def test_variable_pattern_with_binding
63+
assert_equal __, variable_pattern_with_binding(1)
64+
end
65+
66+
# ------------------------------------------------------------------
67+
68+
# We can pin the value of the variable with ^
69+
70+
def variable_pattern_with_pin(variable)
71+
case 0
72+
in ^variable
73+
variable
74+
else
75+
:no_match
76+
end
77+
end
78+
79+
def test_variable_pattern_with_pin
80+
assert_equal __, variable_pattern_with_pin(1)
81+
end
82+
83+
# ------------------------------------------------------------------
84+
85+
# We can drop values from pattern
86+
87+
def pattern_with_dropping(variable)
88+
case variable
89+
in [_, 2]
90+
:match
91+
else
92+
:no_match
93+
end
94+
end
95+
96+
def test_pattern_with_dropping
97+
assert_equal __, pattern_with_dropping(['I will not be checked', 2])
98+
assert_equal __, pattern_with_dropping(['I will not be checked', 'But I will!'])
99+
end
100+
101+
# ------------------------------------------------------------------
102+
103+
# We can use logical *or* in patterns
104+
105+
def alternative_pattern(variable)
106+
case variable
107+
in 0 | false | nil
108+
:match
109+
else
110+
:no_match
111+
end
112+
end
113+
114+
def test_alternative_pattern
115+
assert_equal __, alternative_pattern(0)
116+
assert_equal __, alternative_pattern(false)
117+
assert_equal __, alternative_pattern(nil)
118+
assert_equal __, alternative_pattern(4)
119+
end
120+
121+
# ------------------------------------------------------------------
122+
123+
# As pattern binds the variable to the value if pattern matches
124+
# pat: pat => var
125+
126+
def as_pattern
127+
a = 'First I was afraid'
128+
129+
case 'I was petrified'
130+
in String => a
131+
a
132+
else
133+
:no_match
134+
end
135+
end
136+
137+
def test_as_pattern
138+
assert_equal __, as_pattern
139+
end
140+
141+
# ------------------------------------------------------------------
142+
143+
# Array pattern works with all objects that have #deconstruct method that returns Array
144+
# It is useful to cut needed parts from Array-ish objects
145+
146+
class Deconstructible
147+
def initialize(str)
148+
@data = str
149+
end
150+
151+
def deconstruct
152+
@data&.split('')
153+
end
154+
end
155+
156+
def array_pattern(deconstructible)
157+
case deconstructible
158+
in 'a', *res, 'd'
159+
res
160+
else
161+
:no_match
162+
end
163+
end
164+
165+
def test_array_pattern
166+
assert_equal __, array_pattern(Deconstructible.new('abcd'))
167+
assert_equal __, array_pattern(Deconstructible.new('123'))
168+
end
169+
170+
# ------------------------------------------------------------------
171+
172+
# Hash pattern is quite the same as Array pattern, but it expects #deconsturct_keys(keys) method
173+
# It works with symbol keys for now
174+
175+
class LetterAccountant
176+
def initialize(str)
177+
@data = str
178+
end
179+
180+
def deconstruct_keys(keys)
181+
# we will count number of occurrences of each key in our data
182+
keys.map { |key| [key, @data.count(key.to_s)] }.to_h
183+
end
184+
end
185+
186+
def hash_pattern(deconstructible_as_hash)
187+
case deconstructible_as_hash
188+
in {a: a, b: b}
189+
[a, b]
190+
else
191+
:no_match
192+
end
193+
end
194+
195+
def test_hash_pattern
196+
assert_equal __, hash_pattern(LetterAccountant.new('aaabbc'))
197+
assert_equal __, hash_pattern(LetterAccountant.new('xyz'))
198+
end
199+
200+
# we can write it even shorter
201+
def hash_pattern_with_sugar(deconstructible_as_hash)
202+
case deconstructible_as_hash
203+
in a:, b:
204+
[a, b]
205+
else
206+
:no_match
207+
end
208+
end
209+
210+
def test_hash_pattern_with_sugar
211+
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('aaabbc'))
212+
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('xyz'))
213+
end
214+
215+
end

src/path_to_enlightenment.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@
3838
in_ruby_version("jruby") do
3939
require 'about_java_interop'
4040
end
41+
in_ruby_version("2.7") do
42+
require 'about_pattern_matching'
43+
end
4144
require 'about_extra_credit'

0 commit comments

Comments
 (0)