Adding Regular Expression koans.
authorJames Edward Gray II <james@graysoftinc.com>
Sun, 22 Aug 2010 03:11:06 +0000 (22:11 -0500)
committerJames Edward Gray II <james@graysoftinc.com>
Sun, 22 Aug 2010 03:11:06 +0000 (22:11 -0500)
koans/about_regular_expressions.rb [new file with mode: 0644]
koans/about_strings.rb
koans/path_to_enlightenment.rb
src/about_regular_expressions.rb [new file with mode: 0644]
src/about_strings.rb
src/path_to_enlightenment.rb

diff --git a/koans/about_regular_expressions.rb b/koans/about_regular_expressions.rb
new file mode 100644 (file)
index 0000000..417cca5
--- /dev/null
@@ -0,0 +1,158 @@
+require File.expand_path(File.dirname(__FILE__) + '/edgecase')
+
+class AboutRegularExpressions < EdgeCase::Koan
+  def test_a_pattern_is_a_regular_expression
+    assert_equal Regexp, /pattern/.class
+  end
+  
+  def test_a_regexp_can_search_a_string_for_matching_content
+    assert_equal "match", "some matching content"[/match/]
+  end
+  
+  def test_a_failed_match_returns_nil
+    assert_equal __, "some matching content"[/missing/]
+  end
+
+  # ------------------------------------------------------------------
+  
+  def test_question_mark_means_optional
+    assert_equal __, "abbcccddddeeeee"[/ab?/]
+    assert_equal __, "abbcccddddeeeee"[/az?/]
+  end
+  
+  def test_plus_means_one_or_more
+    assert_equal __, "abbcccddddeeeee"[/bc+/]
+  end
+  
+  def test_asterisk_means_zero_or_more
+    assert_equal __, "abbcccddddeeeee"[/ab*/]
+    assert_equal __, "abbcccddddeeeee"[/az*/]
+    assert_equal __, "abbcccddddeeeee"[/z*/]
+    
+    # THINK ABOUT IT:
+    #
+    # When would * fail to match?
+  end
+
+  # THINK ABOUT IT:
+  #
+  # We say that the repition operators above are "greedy."
+  # 
+  # Why?
+  
+  # ------------------------------------------------------------------
+  
+  def test_the_left_most_match_wins
+    assert_equal __, "abbccc az"[/az*/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_character_classes_give_options_for_a_character
+    animals = ["cat", "bat", "rat", "zat"]
+    assert_equal __, animals.select { |a| a[/[cbr]at/] }
+  end
+  
+  def test_slash_d_is_a_shortcut_for_a_digit_character_class
+    assert_equal __, "the number is 42"[/[0123456789]+/]
+    assert_equal __, "the number is 42"[/\d+/]
+  end
+  
+  def test_character_classes_can_include_ranges
+    assert_equal __, "the number is 42"[/[0-9]+/]
+  end
+  
+  def test_slash_s_is_a_shortcut_for_a_whitespace_character_class
+    assert_equal __, "space: \t\n"[/\s+/]
+  end
+  
+  def test_slash_w_is_a_shortcut_for_a_word_character_class
+    # NOTE:  This is more like how a programmer might define a word.
+    assert_equal __, "variable_1 = 42"[/[a-zA-Z0-9_]+/]
+    assert_equal __, "variable_1 = 42"[/\w+/]
+  end
+  
+  def test_period_is_a_shortcut_for_any_non_newline_character
+    assert_equal __, "abc\n123"[/a.+/]
+  end
+  
+  def test_a_character_class_can_be_negated
+    assert_equal __, "the number is 42"[/[^0-9]+/]
+  end
+  
+  def test_shortcut_character_classes_are_negated_with_capitals
+    assert_equal __, "the number is 42"[/\D+/]
+    assert_equal __, "space: \t\n"[/\S+/]
+    assert_equal __, "variable_1 = 42"[/\W+/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_slash_a_anchors_to_the_start_of_the_string
+    assert_equal __, "start end"[/\Astart/]
+    assert_equal __, "start end"[/\Aend/]
+  end
+  
+  def test_slash_z_anchors_to_the_end_of_the_string
+    assert_equal __, "start end"[/end\z/]
+    assert_equal __, "start end"[/start\z/]
+  end
+  
+  def test_caret_anchors_to_the_start_of_lines
+    assert_equal __, "num 42\n2 lines"[/^\d+/]
+  end
+  
+  def test_dollar_sign_anchors_to_the_end_of_lines
+    assert_equal __, "2 lines\nnum 42"[/\d+$/]
+  end
+  
+  def test_slash_b_anchors_to_a_word_boundary
+    assert_equal __, "bovine vines"[/\bvine./]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_parentheses_group_contents
+    assert_equal __, "ahahaha"[/(ha)+/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_parentheses_also_capture_matched_content_by_number
+    assert_equal __, "Gray, James"[/(\w+), (\w+)/, 1]
+    assert_equal __, "Gray, James"[/(\w+), (\w+)/, 2]
+  end
+  
+  def test_variables_can_also_be_used_to_access_captures
+    assert_equal __, "Name:  Gray, James"[/(\w+), (\w+)/]
+    assert_equal __, $1
+    assert_equal __, $2
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_a_vertical_pipe_means_or
+    grays = /(James|Dana|Summer) Gray/
+    assert_equal __, "James Gray"[grays]
+    assert_equal __, "Summer Gray"[grays, 1]
+    assert_equal __, "Jim Gray"[grays, 1]
+  end
+
+  # THINK ABOUT IT:
+  #
+  # Explain the difference between a character class ([…]) and alternation (|).
+  
+  # ------------------------------------------------------------------
+  
+  def test_scan_is_like_find_all
+    assert_equal __, "one two-three".scan(/\w+/)
+  end
+  
+  def test_sub_is_like_find_and_replace
+    assert_equal __, "one two-three".sub(/(t\w*)/) { $1[0, 1] }
+  end
+  
+  def test_gsub_is_like_find_and_replace_all
+    assert_equal __, "one two-three".gsub(/(t\w*)/) { $1[0, 1] }
+  end
+end
index 87bb4ec..f26e699 100644 (file)
@@ -174,8 +174,8 @@ EOS
     assert_equal [__, __, __, __], words
 
     # NOTE: Patterns are formed from Regular Expressions.  Ruby has a
-    # very powerful Regular Expression library.  Unfortunately, time
-    # does not permit us to explore it in detail now.
+    # very powerful Regular Expression library.  We will become
+    # enlightened about them soon.
   end
 
   def test_strings_can_be_joined
index ee57759..2e12c46 100644 (file)
@@ -8,6 +8,7 @@ require 'about_arrays'
 require 'about_array_assignment'
 require 'about_hashes'
 require 'about_strings'
+require 'about_regular_expressions'
 require 'about_methods'
 require 'about_constants'
 require 'about_control_statements'
diff --git a/src/about_regular_expressions.rb b/src/about_regular_expressions.rb
new file mode 100644 (file)
index 0000000..967bc6a
--- /dev/null
@@ -0,0 +1,158 @@
+require File.expand_path(File.dirname(__FILE__) + '/edgecase')
+
+class AboutRegularExpressions < EdgeCase::Koan
+  def test_a_pattern_is_a_regular_expression
+    assert_equal Regexp, /pattern/.class
+  end
+  
+  def test_a_regexp_can_search_a_string_for_matching_content
+    assert_equal "match", "some matching content"[/match/]
+  end
+  
+  def test_a_failed_match_returns_nil
+    assert_equal __(nil), "some matching content"[/missing/]
+  end
+
+  # ------------------------------------------------------------------
+  
+  def test_question_mark_means_optional
+    assert_equal __("ab"), "abbcccddddeeeee"[/ab?/]
+    assert_equal __("a"), "abbcccddddeeeee"[/az?/]
+  end
+  
+  def test_plus_means_one_or_more
+    assert_equal __("bccc"), "abbcccddddeeeee"[/bc+/]
+  end
+  
+  def test_asterisk_means_zero_or_more
+    assert_equal __("abb"), "abbcccddddeeeee"[/ab*/]
+    assert_equal __("a"), "abbcccddddeeeee"[/az*/]
+    assert_equal __(""), "abbcccddddeeeee"[/z*/]
+    
+    # THINK ABOUT IT:
+    #
+    # When would * fail to match?
+  end
+
+  # THINK ABOUT IT:
+  #
+  # We say that the repition operators above are "greedy."
+  # 
+  # Why?
+  
+  # ------------------------------------------------------------------
+  
+  def test_the_left_most_match_wins
+    assert_equal __("a"), "abbccc az"[/az*/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_character_classes_give_options_for_a_character
+    animals = ["cat", "bat", "rat", "zat"]
+    assert_equal __(["cat", "bat", "rat"]), animals.select { |a| a[/[cbr]at/] }
+  end
+  
+  def test_slash_d_is_a_shortcut_for_a_digit_character_class
+    assert_equal __("42"), "the number is 42"[/[0123456789]+/]
+    assert_equal __("42"), "the number is 42"[/\d+/]
+  end
+  
+  def test_character_classes_can_include_ranges
+    assert_equal __("42"), "the number is 42"[/[0-9]+/]
+  end
+  
+  def test_slash_s_is_a_shortcut_for_a_whitespace_character_class
+    assert_equal __(" \t\n"), "space: \t\n"[/\s+/]
+  end
+  
+  def test_slash_w_is_a_shortcut_for_a_word_character_class
+    # NOTE:  This is more like how a programmer might define a word.
+    assert_equal __("variable_1"), "variable_1 = 42"[/[a-zA-Z0-9_]+/]
+    assert_equal __("variable_1"), "variable_1 = 42"[/\w+/]
+  end
+  
+  def test_period_is_a_shortcut_for_any_non_newline_character
+    assert_equal __("abc"), "abc\n123"[/a.+/]
+  end
+  
+  def test_a_character_class_can_be_negated
+    assert_equal __("the number is "), "the number is 42"[/[^0-9]+/]
+  end
+  
+  def test_shortcut_character_classes_are_negated_with_capitals
+    assert_equal __("the number is "), "the number is 42"[/\D+/]
+    assert_equal __("space:"), "space: \t\n"[/\S+/]
+    assert_equal __(" = "), "variable_1 = 42"[/\W+/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_slash_a_anchors_to_the_start_of_the_string
+    assert_equal __("start"), "start end"[/\Astart/]
+    assert_equal __(nil), "start end"[/\Aend/]
+  end
+  
+  def test_slash_z_anchors_to_the_end_of_the_string
+    assert_equal __("end"), "start end"[/end\z/]
+    assert_equal __(nil), "start end"[/start\z/]
+  end
+  
+  def test_caret_anchors_to_the_start_of_lines
+    assert_equal __("2"), "num 42\n2 lines"[/^\d+/]
+  end
+  
+  def test_dollar_sign_anchors_to_the_end_of_lines
+    assert_equal __("42"), "2 lines\nnum 42"[/\d+$/]
+  end
+  
+  def test_slash_b_anchors_to_a_word_boundary
+    assert_equal __("vines"), "bovine vines"[/\bvine./]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_parentheses_group_contents
+    assert_equal __("hahaha"), "ahahaha"[/(ha)+/]
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_parentheses_also_capture_matched_content_by_number
+    assert_equal __("Gray"), "Gray, James"[/(\w+), (\w+)/, 1]
+    assert_equal __("James"), "Gray, James"[/(\w+), (\w+)/, 2]
+  end
+  
+  def test_variables_can_also_be_used_to_access_captures
+    assert_equal __("Gray, James"), "Name:  Gray, James"[/(\w+), (\w+)/]
+    assert_equal __("Gray"), $1
+    assert_equal __("James"), $2
+  end
+  
+  # ------------------------------------------------------------------
+  
+  def test_a_vertical_pipe_means_or
+    grays = /(James|Dana|Summer) Gray/
+    assert_equal __("James Gray"), "James Gray"[grays]
+    assert_equal __("Summer"), "Summer Gray"[grays, 1]
+    assert_equal __(nil), "Jim Gray"[grays, 1]
+  end
+
+  # THINK ABOUT IT:
+  #
+  # Explain the difference between a character class ([…]) and alternation (|).
+  
+  # ------------------------------------------------------------------
+  
+  def test_scan_is_like_find_all
+    assert_equal __(["one", "two", "three"]), "one two-three".scan(/\w+/)
+  end
+  
+  def test_sub_is_like_find_and_replace
+    assert_equal __("one t-three"), "one two-three".sub(/(t\w*)/) { $1[0, 1] }
+  end
+  
+  def test_gsub_is_like_find_and_replace_all
+    assert_equal __("one t-t"), "one two-three".gsub(/(t\w*)/) { $1[0, 1] }
+  end
+end
index 106ba79..94211a9 100644 (file)
@@ -174,8 +174,8 @@ EOS
     assert_equal [__("the"), __("rain"), __("in"), __("spain")], words
 
     # NOTE: Patterns are formed from Regular Expressions.  Ruby has a
-    # very powerful Regular Expression library.  Unfortunately, time
-    # does not permit us to explore it in detail now.
+    # very powerful Regular Expression library.  We will become
+    # enlightened about them soon.
   end
 
   def test_strings_can_be_joined
index ee57759..2e12c46 100644 (file)
@@ -8,6 +8,7 @@ require 'about_arrays'
 require 'about_array_assignment'
 require 'about_hashes'
 require 'about_strings'
+require 'about_regular_expressions'
 require 'about_methods'
 require 'about_constants'
 require 'about_control_statements'