Merge branch 'edgecase:master' into bo-tato-observr-patch-1

This commit is contained in:
bo-tato
2023-08-09 12:40:48 -05:00
committed by GitHub
5 changed files with 243 additions and 3 deletions

Binary file not shown.

View File

@@ -113,4 +113,14 @@ class AboutHashes < Neo::Koan
assert_equal __(["dos"]), hash[:two]
assert_equal __([]), hash[:three]
end
def test_default_value_attribute
hash = Hash.new
assert_equal __(nil), hash[:some_key]
hash.default = 'peanut'
assert_equal __('peanut'), hash[:some_key]
end
end

View File

@@ -24,8 +24,20 @@ class AboutKeywordArguments < Neo::Koan
assert_match(/#{__("wrong number of arguments")}/, exception.message)
end
# THINK ABOUT IT:
#
# Keyword arguments always have a default value, making them optional to the caller
def method_with_mandatory_keyword_arguments(one:, two: 'two')
[one, two]
end
def test_mandatory_keyword_arguments
assert_equal __(['one', 'two']), method_with_mandatory_keyword_arguments(one: 'one')
assert_equal __([1, 2]), method_with_mandatory_keyword_arguments(two: 2, one: 1)
end
def test_mandatory_keyword_arguments_without_mandatory_argument
exception = assert_raise(___(ArgumentError)) do
method_with_mandatory_keyword_arguments
end
assert_match(/#{__("missing keyword: :one")}/, exception.message)
end
end

View File

@@ -0,0 +1,215 @@
require File.expand_path(File.dirname(__FILE__) + '/neo')
class AboutPatternMatching < Neo::Koan
def test_pattern_may_not_match
begin
case [true, false]
in [a, b] if a == b # The condition after pattern is called guard.
:match
end
rescue Exception => ex
# What exception has been caught?
assert_equal __, ex.class
end
end
def test_we_can_use_else
result = case [true, false]
in [a, b] if a == b
:match
else
:no_match
end
assert_equal __, result
end
# ------------------------------------------------------------------
def value_pattern(variable)
case variable
in 0
:match_exact_value
in 1..10
:match_in_range
in Integer
:match_with_class
else
:no_match
end
end
def test_value_pattern
assert_equal __, value_pattern(0)
assert_equal __, value_pattern(5)
assert_equal __, value_pattern(100)
assert_equal __, value_pattern('Not a Number!')
end
# ------------------------------------------------------------------
# This pattern will bind variable to the value
def variable_pattern_with_binding(variable)
case 0
in variable
variable
else
:no_match
end
end
def test_variable_pattern_with_binding
assert_equal __, variable_pattern_with_binding(1)
end
# ------------------------------------------------------------------
# We can pin the value of the variable with ^
def variable_pattern_with_pin(variable)
case 0
in ^variable
variable
else
:no_match
end
end
def test_variable_pattern_with_pin
assert_equal __, variable_pattern_with_pin(1)
end
# ------------------------------------------------------------------
# We can drop values from pattern
def pattern_with_dropping(variable)
case variable
in [_, 2]
:match
else
:no_match
end
end
def test_pattern_with_dropping
assert_equal __, pattern_with_dropping(['I will not be checked', 2])
assert_equal __, pattern_with_dropping(['I will not be checked', 'But I will!'])
end
# ------------------------------------------------------------------
# We can use logical *or* in patterns
def alternative_pattern(variable)
case variable
in 0 | false | nil
:match
else
:no_match
end
end
def test_alternative_pattern
assert_equal __, alternative_pattern(0)
assert_equal __, alternative_pattern(false)
assert_equal __, alternative_pattern(nil)
assert_equal __, alternative_pattern(4)
end
# ------------------------------------------------------------------
# As pattern binds the variable to the value if pattern matches
# pat: pat => var
def as_pattern
a = 'First I was afraid'
case 'I was petrified'
in String => a
a
else
:no_match
end
end
def test_as_pattern
assert_equal __, as_pattern
end
# ------------------------------------------------------------------
# Array pattern works with all objects that have #deconstruct method that returns Array
# It is useful to cut needed parts from Array-ish objects
class Deconstructible
def initialize(str)
@data = str
end
def deconstruct
@data&.split('')
end
end
def array_pattern(deconstructible)
case deconstructible
in 'a', *res, 'd'
res
else
:no_match
end
end
def test_array_pattern
assert_equal __, array_pattern(Deconstructible.new('abcd'))
assert_equal __, array_pattern(Deconstructible.new('123'))
end
# ------------------------------------------------------------------
# Hash pattern is quite the same as Array pattern, but it expects #deconsturct_keys(keys) method
# It works with symbol keys for now
class LetterAccountant
def initialize(str)
@data = str
end
def deconstruct_keys(keys)
# we will count number of occurrences of each key in our data
keys.map { |key| [key, @data.count(key.to_s)] }.to_h
end
end
def hash_pattern(deconstructible_as_hash)
case deconstructible_as_hash
in {a: a, b: b}
[a, b]
else
:no_match
end
end
def test_hash_pattern
assert_equal __, hash_pattern(LetterAccountant.new('aaabbc'))
assert_equal __, hash_pattern(LetterAccountant.new('xyz'))
end
# we can write it even shorter
def hash_pattern_with_sugar(deconstructible_as_hash)
case deconstructible_as_hash
in a:, b:
[a, b]
else
:no_match
end
end
def test_hash_pattern_with_sugar
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('aaabbc'))
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('xyz'))
end
end

View File

@@ -38,4 +38,7 @@ require 'about_to_str'
in_ruby_version("jruby") do
require 'about_java_interop'
end
in_ruby_version("2.7") do
require 'about_pattern_matching'
end
require 'about_extra_credit'