Chars enables you to work transparently with multibyte encodings in the Ruby String class without having extensive knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an encoding safe manner. All the normal String methods are also implemented on the proxy.
String methods are proxied through the Chars object, and can be accessed through the chars method. Methods which would normally return a String object now return a Chars object so methods can be chained.
"The Perfect String ".chars.downcase.strip.normalize #=> "the perfect string"
Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made. If certain methods do explicitly check the class, call to_s before you pass chars objects to them.
bad.explicit_checking_method "T".chars.downcase.to_s
The actual operations on the string are delegated to handlers. Theoretically handlers can be implemented for any encoding, but the default handler handles UTF-8. This handler is set during initialization, if you want to use you own handler, you can set it on the Chars class. Look at the UTF8Handler source for an example how to implement your own handler. If you your own handler to work on anything but UTF-8 you probably also want to override Chars#handler.
ActiveSupport::Multibyte::Chars.handler = MyHandler
Note that a few methods are defined on Chars instead of the handler because they are defined on Object or Kernel and method_missing can‘t catch them.
- Comparable
[R] | string |
Set the handler class for the Char objects.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 98 98: def self.handler=(klass) 99: @@handler = klass 100: end
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 45 45: def initialize(str) 46: @string = (str.string rescue str) 47: end
Returns -1, 0 or +1 depending on whether the Chars object is to be sorted before, equal or after the object on the right side of the operation. It accepts any object that implements to_s. See String.<=> for more details.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 52 52: def <=>(other); @string <=> other.to_s; end
Like String.=~ only it returns the character offset (in codepoints) instead of the byte offset.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 64 64: def =~(other) 65: handler.translate_offset(@string, @string =~ other) 66: end
Gsub works exactly the same as gsub on a normal string.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 61 61: def gsub(*a, &b); @string.gsub(*a, &b).chars; end
Returns the proper handler for the contained string depending on $KCODE and the encoding of the string. This method is used internally to always redirect messages to the proper classes depending on the context.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 104 104: def handler 105: if utf8_pragma? 106: @@handler 107: else 108: ActiveSupport::Multibyte::Handlers::PassthruHandler 109: end 110: end
Try to forward all undefined methods to the handler, when a method is not defined on the handler, send it to the contained string. Method_missing is also responsible for making the bang! methods destructive.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 70 70: def method_missing(m, *a, &b) 71: begin 72: # Simulate methods with a ! at the end because we can't touch the enclosed string from the handlers. 73: if m.to_s =~ /^(.*)\!$/ 74: result = handler.send($1, @string, *a, &b) 75: if result == @string 76: result = nil 77: else 78: @string.replace result 79: end 80: else 81: result = handler.send(m, @string, *a, &b) 82: end 83: rescue NoMethodError 84: result = @string.send(m, *a, &b) 85: rescue Handlers::EncodingError 86: @string.replace handler.tidy_bytes(@string) 87: retry 88: end 89: 90: if result.kind_of?(String) 91: result.chars 92: else 93: result 94: end 95: end
Works just like String#split, with the exception that the items in the resulting list are Chars instances instead of String. This makes chaining methods easier.
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 56 56: def split(*args) 57: @string.split(*args).map { |i| i.chars } 58: end
The magic method to make String and Chars comparable
[ show source ]
# File vendor/rails/activesupport/lib/active_support/multibyte/chars.rb, line 38 38: def to_str 39: # Using any other ways of overriding the String itself will lead you all the way from infinite loops to 40: # core dumps. Don't go there. 41: @string 42: end