class Mail::Multibyte::Chars
Mail::Multibyte.proxy_class = CharsForUTF32
end
end
string.length % 4 == 0
def self.accepts?(string)
end
@wrapped_string.size / 4
def size
class CharsForUTF32
Mail::Multibyte.proxy_class.
encodings you can write your own multibyte string handler and configure it through
The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
bad.explicit_checking_method “T”.mb_chars.downcase.to_s
If certain methods do explicitly check the class, call to_s
before you pass chars objects to them.
Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made.
“The Perfect String ”.mb_chars.downcase.strip.normalize # => “the perfect string”
which would normally return a String object now return a Chars object so methods can be chained.
String methods are proxied through the Chars object, and can be accessed through the mb_chars
method. Methods
encoding safe manner. All the normal String methods are also implemented on the proxy.
knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an
Chars enables you to work transparently with UTF-8 encoding in the Ruby String class without having extensive
:nodoc:
:nodoc:
def <=>(other)
'é'.mb_chars <=> 'ü'.mb_chars # => -1
that implements +to_s+:
equal or after the object on the right side of the operation. It accepts any object
Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
def <=>(other) @wrapped_string <=> other.to_s end
def =~(other)
def =~(other) @wrapped_string =~ other end
def []=(*args)
s
s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1
s = "Müller"
# => "Müeler"
s
s.mb_chars[2] = "e" # Replace character with offset 2
s = "Müller"
Example:
Like String#[]=, except instead of byte offsets you specify character offsets.
def []=(*args) replace_by = args.pop # Indexed replace with regular expressions already works if args.first.is_a?(Regexp) @wrapped_string[*args] = replace_by else result = Unicode.u_unpack(@wrapped_string) if args[0].is_a?(Integer) raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length min = args[0] max = args[1].nil? ? min : (min + args[1] - 1) range = Range.new(min, max) replace_by = [replace_by].pack('U') if replace_by.is_a?(Integer) elsif args.first.is_a?(Range) raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length range = args[0] else needle = args[0].to_s min = index(needle) max = min + Unicode.u_unpack(needle).length - 1 range = Range.new(min, max) end result[range] = Unicode.u_unpack(replace_by) @wrapped_string.replace(result.pack('U*')) end end
def acts_like_string?
def acts_like_string? true end
def capitalize
Example:
Converts the first character to uppercase and the remainder to lowercase.
def capitalize (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase end
def chars(string) #:nodoc:
def chars(string) #:nodoc: self.class.new(string) end
def compose
'é'.length # => 3
Example:
Performs composition on all the characters.
def compose chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*')) end
def decompose
'é'.length # => 2
Example:
Performs canonical decomposition on all the characters.
def decompose chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*')) end
def downcase
Example:
Convert characters in the string to lowercase.
def downcase chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping)) end
def g_length
Mail::Multibyte.mb_chars('क्षि').length # => 4
Example:
Returns the number of grapheme clusters in the string.
def g_length Unicode.g_unpack(@wrapped_string).length end
def initialize(string)
def initialize(string) @wrapped_string = string.dup @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? end
def justify(integer, way, padstr=' ') #:nodoc:
def justify(integer, way, padstr=' ') #:nodoc: raise ArgumentError, "zero width padding" if padstr.length == 0 padsize = integer - size padsize = padsize > 0 ? padsize : 0 case way when :right result = @wrapped_string.dup.insert(0, padding(padsize, padstr)) when :left result = @wrapped_string.dup.insert(-1, padding(padsize, padstr)) when :center lpad = padding((padsize / 2.0).floor, padstr) rpad = padding((padsize / 2.0).ceil, padstr) result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad) end chars(result) end
def limit(limit)
s = 'こんにちは'
Example:
when the storage for a string is limited for some reason.
Limit the byte size of the string to a number of bytes without breaking characters. Usable
def limit(limit) slice(0...translate_offset(limit)) end
def method_missing(method, *args, &block)
def method_missing(method, *args, &block) if method.to_s =~ /!$/ @wrapped_string.__send__(method, *args, &block) self else result = @wrapped_string.__send__(method, *args, &block) result.kind_of?(String) ? chars(result) : result end end
def normalize(form = nil)
:c, :kc, :d, or :kd. Default is
* form - The form you want to normalize in. Should be one of the following:
passing strings to databases and validations.
Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
def normalize(form = nil) chars(Unicode.normalize(@wrapped_string, form)) end
def padding(padsize, padstr=' ') #:nodoc:
def padding(padsize, padstr=' ') #:nodoc: if padsize != 0 chars(padstr * ((padsize / Unicode.u_unpack(padstr).size) + 1)).slice(0, padsize) else '' end end
def respond_to?(method, include_private=false)
Returns +true+ if _obj_ responds to the given method. Private methods are included in the search
def respond_to?(method, include_private=false) super || @wrapped_string.respond_to?(method, include_private) || false end
def reverse
Example:
Reverses all characters in the string.
def reverse chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*')) end
def slice(*args)
Example:
character.
Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
def slice(*args) if args.size > 2 raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp))) raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native elsif (args.size == 2 && !args[1].is_a?(Numeric)) raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native elsif args[0].kind_of? Range cps = Unicode.u_unpack(@wrapped_string).slice(*args) result = cps.nil? ? nil : cps.pack('U*') elsif args[0].kind_of? Regexp result = @wrapped_string.slice(*args) elsif args.size == 1 && args[0].kind_of?(Numeric) character = Unicode.u_unpack(@wrapped_string)[args[0]] result = character && [character].pack('U') else cps = Unicode.u_unpack(@wrapped_string).slice(*args) result = cps && cps.pack('U*') end result && chars(result) end
def split(*args)
Example:
instances instead of String. This makes chaining methods easier.
Works just like String#split, with the exception that the items in the resulting list are Chars
def split(*args) @wrapped_string.split(*args).map { |i| i.mb_chars } end
def tidy_bytes(force = false)
Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
def tidy_bytes(force = false) chars(Unicode.tidy_bytes(@wrapped_string, force)) end
def titleize
Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
Example:
Capitalizes the first letter of every word, when possible.
def titleize chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping }) end
def translate_offset(byte_offset) #:nodoc:
def translate_offset(byte_offset) #:nodoc: return nil if byte_offset.nil? return 0 if @wrapped_string == '' if @wrapped_string.respond_to?(:force_encoding) @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT) end begin @wrapped_string[0...byte_offset].unpack('U*').length rescue ArgumentError byte_offset -= 1 retry end end
def upcase
Example:
Convert characters in the string to uppercase.
def upcase chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping)) end