Class: UnicodeMultibyte::Multibyte::Chars

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/unicodechars/multibyte/chars.rb

Overview

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  ".multibyte_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".multibyte_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.

UnicodeMultibyte::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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(str) ⇒ Chars

Create a new Chars instance.



67
68
69
# File 'lib/unicodechars/multibyte/chars.rb', line 67

def initialize(str)
  @string = (str.string rescue str)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *a, &b) ⇒ Object

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.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/unicodechars/multibyte/chars.rb', line 96

def method_missing(m, *a, &b)
  begin
    # Simulate methods with a ! at the end because we can't touch the enclosed string from the handlers.
    if m.to_s =~ /^(.*)\!$/
      result = handler.send($1, @string, *a, &b)
      if result == @string
        result = nil
      else
        @string.replace result
      end
    else
      result = handler.send(m, @string, *a, &b)
    end
  rescue NoMethodError
    result = @string.send(m, *a, &b)
  rescue Handlers::EncodingError
    @string.replace handler.tidy_bytes(@string)
    retry
  end
  
  if result.kind_of?(String)
    result.multibyte_chars
  else
    result
  end
end

Instance Attribute Details

#stringObject (readonly) Also known as: to_s

The contained string



32
33
34
# File 'lib/unicodechars/multibyte/chars.rb', line 32

def string
  @string
end

Class Method Details

.handler=(klass) ⇒ Object

Set the handler class for the Char objects.



124
125
126
# File 'lib/unicodechars/multibyte/chars.rb', line 124

def self.handler=(klass)
  @@handler = klass
end

Instance Method Details

#<=>(other) ⇒ Object

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.



78
# File 'lib/unicodechars/multibyte/chars.rb', line 78

def <=>(other); @string <=> other.to_s; end

#=~(other) ⇒ Object

Like String.=~ only it returns the character offset (in codepoints) instead of the byte offset.



90
91
92
# File 'lib/unicodechars/multibyte/chars.rb', line 90

def =~(other)
  handler.translate_offset(@string, @string =~ other)
end

#[](num) ⇒ Object

Fix [] for single numbers



58
59
60
61
62
63
64
# File 'lib/unicodechars/multibyte/chars.rb', line 58

def [](num)
  if num.is_a?(Fixnum)
    self[num..num]
  else
    super
  end
end

#each(&block) ⇒ Object



71
72
73
# File 'lib/unicodechars/multibyte/chars.rb', line 71

def each &block
  split(//).each(&block)
end

#gsub(*a, &b) ⇒ Object

Gsub works exactly the same as gsub on a normal string.



87
# File 'lib/unicodechars/multibyte/chars.rb', line 87

def gsub(*a, &b); @string.gsub(*a, &b).multibyte_chars; end

#handlerObject

Returns the proper handler for the contained string depending on $MULTIBYTE_CODE and the encoding of the string. This method is used internally to always redirect messages to the proper classes depending on the context.



130
131
132
133
134
135
136
# File 'lib/unicodechars/multibyte/chars.rb', line 130

def handler
  if utf8_pragma?
    @@handler
  else
    UnicodeMultibyte::Multibyte::Handlers::PassthruHandler
  end
end

#inspectObject

Makes unicode string look like a string in the console



45
46
47
# File 'lib/unicodechars/multibyte/chars.rb', line 45

def inspect
  @string.inspect
end

#is_a?(type) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
52
53
54
55
# File 'lib/unicodechars/multibyte/chars.rb', line 49

def is_a?(type)
  if type == String
    true
  else
    super
  end
end

#split(*args) ⇒ Object

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.



82
83
84
# File 'lib/unicodechars/multibyte/chars.rb', line 82

def split(*args)
  @string.split(*args).map { |i| i.multibyte_chars }
end

#to_strObject

The magic method to make String and Chars comparable



38
39
40
41
42
# File 'lib/unicodechars/multibyte/chars.rb', line 38

def to_str
  # Using any other ways of overriding the String itself will lead you all the way from infinite loops to
  # core dumps. Don't go there.
  @string
end