From e6474080622220ed892c287ba2e62a5c29087741 Mon Sep 17 00:00:00 2001 From: interdictor Date: Thu, 21 May 2020 22:45:22 +0200 Subject: [PATCH 1/5] Add test_default_value_attribute to about_hashes.rb --- src/about_hashes.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/about_hashes.rb b/src/about_hashes.rb index ef58621..327bfc2 100644 --- a/src/about_hashes.rb +++ b/src/about_hashes.rb @@ -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 From 71639538514582d512e8fe82b2a0b4f58479e5e5 Mon Sep 17 00:00:00 2001 From: wolwer1ne Date: Wed, 10 Jun 2020 18:46:03 +0300 Subject: [PATCH 2/5] Added pattern matching koans --- src/about_pattern_matching.rb | 215 ++++++++++++++++++++++++++++++++++ src/path_to_enlightenment.rb | 3 + 2 files changed, 218 insertions(+) create mode 100644 src/about_pattern_matching.rb diff --git a/src/about_pattern_matching.rb b/src/about_pattern_matching.rb new file mode 100644 index 0000000..43ee0a0 --- /dev/null +++ b/src/about_pattern_matching.rb @@ -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 patternis 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 \ No newline at end of file diff --git a/src/path_to_enlightenment.rb b/src/path_to_enlightenment.rb index 9e8ccbe..a31ec04 100644 --- a/src/path_to_enlightenment.rb +++ b/src/path_to_enlightenment.rb @@ -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' From c7a56e50fac2851e2ced74fd8691827ea4ff3222 Mon Sep 17 00:00:00 2001 From: Varun Iyer Date: Mon, 15 Aug 2022 14:12:39 -0700 Subject: [PATCH 3/5] Add a koan for mandatory keyword arguments Previously, `about_keyword_arguments.rb` asked the reader to reflect on the fact that keyword arguments must have default values. However, this does not seem to be true anymore in Ruby. This commit replaces that comment with two koans that guide the reader through validating and using a method with mandatory keyword arguments. --- src/about_keyword_arguments.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/about_keyword_arguments.rb b/src/about_keyword_arguments.rb index 1addb39..3c9856b 100644 --- a/src/about_keyword_arguments.rb +++ b/src/about_keyword_arguments.rb @@ -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 From 71196f474b0e7deaa92237b951f429100cde3f9d Mon Sep 17 00:00:00 2001 From: Wolwer1ne Date: Mon, 17 Oct 2022 11:43:38 +0300 Subject: [PATCH 4/5] Fix typo --- src/about_pattern_matching.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/about_pattern_matching.rb b/src/about_pattern_matching.rb index 43ee0a0..065e100 100644 --- a/src/about_pattern_matching.rb +++ b/src/about_pattern_matching.rb @@ -169,7 +169,7 @@ class AboutPatternMatching < Neo::Koan # ------------------------------------------------------------------ - # Hash patternis quite the same as Array pattern, but it expects #deconsturct_keys(keys) method + # 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 From 6f919cdb01ca9f5b0ddb9caca19889318cb3ee22 Mon Sep 17 00:00:00 2001 From: Tony Schneider Date: Wed, 9 Aug 2023 00:39:23 -0400 Subject: [PATCH 5/5] Update rubykoans.zip --- download/rubykoans.zip | Bin 38975 -> 40386 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/download/rubykoans.zip b/download/rubykoans.zip index ce558aa091d621d8e3a642390015c9ba90f30c84..e693cd601c8e8078abb5d39009416e4cb99064fe 100644 GIT binary patch delta 9089 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
xg8EvbIWT9r_B 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