diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 99b974e4a73fd..ac61870871ddd 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -93,7 +93,7 @@ def reverse chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*')) end - # Limit the byte size of the string to a number of bytes without breaking characters. Usable + # Limits the byte size of the string to a number of bytes without breaking characters. Usable # when the storage for a string is limited for some reason. # # Example: @@ -102,7 +102,7 @@ def limit(limit) slice(0...translate_offset(limit)) end - # Convert characters in the string to uppercase. + # Converts characters in the string to uppercase. # # Example: # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?" @@ -110,7 +110,7 @@ def upcase chars Unicode.upcase(@wrapped_string) end - # Convert characters in the string to lowercase. + # Converts characters in the string to lowercase. # # Example: # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum" @@ -118,6 +118,14 @@ def downcase chars Unicode.downcase(@wrapped_string) end + # Converts characters in the string to the opposite case. + # + # Example: + # 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN" + def swapcase + chars Unicode.swapcase(@wrapped_string) + end + # Converts the first character to uppercase and the remainder to lowercase. # # Example: diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb index 94fb0a48aaa25..a0a8f3c97ecb5 100644 --- a/activesupport/lib/active_support/multibyte/unicode.rb +++ b/activesupport/lib/active_support/multibyte/unicode.rb @@ -293,9 +293,17 @@ def upcase(string) apply_mapping string, :uppercase_mapping end + def swapcase(string) + apply_mapping string, :swapcase_mapping + end + # Holds data about a codepoint in the Unicode database class Codepoint attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping + + def swapcase_mapping + uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping + end end # Holds static data from the Unicode database diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 971bd30781d1b..63e7a35c015ff 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -106,7 +106,7 @@ def test_split_should_return_an_array_of_chars_instances end end - %w{capitalize downcase lstrip reverse rstrip upcase}.each do |method| + %w{capitalize downcase lstrip reverse rstrip swapcase upcase}.each do |method| class_eval(<<-EOTESTS) def test_#{method}_bang_should_return_self_when_modifying_wrapped_string chars = ' él piDió Un bUen café ' @@ -161,6 +161,7 @@ def test_string_methods_are_chainable assert chars('').decompose.kind_of?(ActiveSupport::Multibyte.proxy_class) assert chars('').compose.kind_of?(ActiveSupport::Multibyte.proxy_class) assert chars('').tidy_bytes.kind_of?(ActiveSupport::Multibyte.proxy_class) + assert chars('').swapcase.kind_of?(ActiveSupport::Multibyte.proxy_class) end def test_should_be_equal_to_the_wrapped_string @@ -432,6 +433,11 @@ def test_downcase_should_downcase_ascii_characters assert_equal 'abc', 'aBc'.mb_chars.downcase end + def test_swapcase_should_swap_ascii_characters + assert_equal '', ''.mb_chars.swapcase + assert_equal 'AbC', 'aBc'.mb_chars.swapcase + end + def test_capitalize_should_work_on_ascii_characters assert_equal '', ''.mb_chars.capitalize assert_equal 'Abc', 'abc'.mb_chars.capitalize @@ -466,10 +472,15 @@ def test_upcase_should_be_unicode_aware end def test_downcase_should_be_unicode_aware - assert_equal "абвгд\0f", chars("аБвгд\0f").downcase + assert_equal "абвгд\0f", chars("аБвгд\0F").downcase assert_equal 'こにちわ', chars('こにちわ').downcase end + def test_swapcase_should_be_unicode_aware + assert_equal "аaéÜ\0f", chars("АAÉü\0F").swapcase + assert_equal 'こにちわ', chars('こにちわ').swapcase + end + def test_capitalize_should_be_unicode_aware { 'аБвг аБвг' => 'Абвг абвг', 'аБвг АБВГ' => 'Абвг абвг',