dball-mail 2.2.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/CHANGELOG.rdoc +459 -0
  2. data/README.rdoc +582 -0
  3. data/Rakefile +66 -0
  4. data/TODO.rdoc +9 -0
  5. data/lib/VERSION +4 -0
  6. data/lib/mail/attachments_list.rb +105 -0
  7. data/lib/mail/body.rb +286 -0
  8. data/lib/mail/configuration.rb +71 -0
  9. data/lib/mail/core_extensions/nil.rb +11 -0
  10. data/lib/mail/core_extensions/string.rb +27 -0
  11. data/lib/mail/elements/address.rb +306 -0
  12. data/lib/mail/elements/address_list.rb +74 -0
  13. data/lib/mail/elements/content_disposition_element.rb +30 -0
  14. data/lib/mail/elements/content_location_element.rb +25 -0
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  16. data/lib/mail/elements/content_type_element.rb +35 -0
  17. data/lib/mail/elements/date_time_element.rb +26 -0
  18. data/lib/mail/elements/envelope_from_element.rb +34 -0
  19. data/lib/mail/elements/message_ids_element.rb +29 -0
  20. data/lib/mail/elements/mime_version_element.rb +26 -0
  21. data/lib/mail/elements/phrase_list.rb +21 -0
  22. data/lib/mail/elements/received_element.rb +30 -0
  23. data/lib/mail/elements.rb +14 -0
  24. data/lib/mail/encodings/7bit.rb +31 -0
  25. data/lib/mail/encodings/8bit.rb +31 -0
  26. data/lib/mail/encodings/base64.rb +33 -0
  27. data/lib/mail/encodings/binary.rb +31 -0
  28. data/lib/mail/encodings/quoted_printable.rb +38 -0
  29. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  30. data/lib/mail/encodings.rb +268 -0
  31. data/lib/mail/envelope.rb +35 -0
  32. data/lib/mail/field.rb +223 -0
  33. data/lib/mail/field_list.rb +33 -0
  34. data/lib/mail/fields/bcc_field.rb +56 -0
  35. data/lib/mail/fields/cc_field.rb +55 -0
  36. data/lib/mail/fields/comments_field.rb +41 -0
  37. data/lib/mail/fields/common/address_container.rb +16 -0
  38. data/lib/mail/fields/common/common_address.rb +125 -0
  39. data/lib/mail/fields/common/common_date.rb +42 -0
  40. data/lib/mail/fields/common/common_field.rb +50 -0
  41. data/lib/mail/fields/common/common_message_id.rb +44 -0
  42. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  43. data/lib/mail/fields/content_description_field.rb +19 -0
  44. data/lib/mail/fields/content_disposition_field.rb +69 -0
  45. data/lib/mail/fields/content_id_field.rb +63 -0
  46. data/lib/mail/fields/content_location_field.rb +42 -0
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  48. data/lib/mail/fields/content_type_field.rb +198 -0
  49. data/lib/mail/fields/date_field.rb +55 -0
  50. data/lib/mail/fields/from_field.rb +55 -0
  51. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  52. data/lib/mail/fields/keywords_field.rb +44 -0
  53. data/lib/mail/fields/message_id_field.rb +83 -0
  54. data/lib/mail/fields/mime_version_field.rb +53 -0
  55. data/lib/mail/fields/optional_field.rb +13 -0
  56. data/lib/mail/fields/received_field.rb +67 -0
  57. data/lib/mail/fields/references_field.rb +55 -0
  58. data/lib/mail/fields/reply_to_field.rb +55 -0
  59. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  60. data/lib/mail/fields/resent_cc_field.rb +55 -0
  61. data/lib/mail/fields/resent_date_field.rb +35 -0
  62. data/lib/mail/fields/resent_from_field.rb +55 -0
  63. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  64. data/lib/mail/fields/resent_sender_field.rb +62 -0
  65. data/lib/mail/fields/resent_to_field.rb +55 -0
  66. data/lib/mail/fields/return_path_field.rb +64 -0
  67. data/lib/mail/fields/sender_field.rb +67 -0
  68. data/lib/mail/fields/structured_field.rb +51 -0
  69. data/lib/mail/fields/subject_field.rb +16 -0
  70. data/lib/mail/fields/to_field.rb +55 -0
  71. data/lib/mail/fields/unstructured_field.rb +179 -0
  72. data/lib/mail/fields.rb +35 -0
  73. data/lib/mail/header.rb +264 -0
  74. data/lib/mail/mail.rb +255 -0
  75. data/lib/mail/message.rb +1972 -0
  76. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  77. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  78. data/lib/mail/network/delivery_methods/smtp.rb +136 -0
  79. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  80. data/lib/mail/network/retriever_methods/imap.rb +213 -0
  81. data/lib/mail/network/retriever_methods/pop3.rb +194 -0
  82. data/lib/mail/network/retriever_methods/test_retriever.rb +31 -0
  83. data/lib/mail/network.rb +10 -0
  84. data/lib/mail/parsers/address_lists.rb +64 -0
  85. data/lib/mail/parsers/address_lists.treetop +19 -0
  86. data/lib/mail/parsers/content_disposition.rb +535 -0
  87. data/lib/mail/parsers/content_disposition.treetop +46 -0
  88. data/lib/mail/parsers/content_location.rb +139 -0
  89. data/lib/mail/parsers/content_location.treetop +20 -0
  90. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  91. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  92. data/lib/mail/parsers/content_type.rb +967 -0
  93. data/lib/mail/parsers/content_type.treetop +68 -0
  94. data/lib/mail/parsers/date_time.rb +114 -0
  95. data/lib/mail/parsers/date_time.treetop +11 -0
  96. data/lib/mail/parsers/envelope_from.rb +194 -0
  97. data/lib/mail/parsers/envelope_from.treetop +32 -0
  98. data/lib/mail/parsers/message_ids.rb +45 -0
  99. data/lib/mail/parsers/message_ids.treetop +15 -0
  100. data/lib/mail/parsers/mime_version.rb +144 -0
  101. data/lib/mail/parsers/mime_version.treetop +19 -0
  102. data/lib/mail/parsers/phrase_lists.rb +45 -0
  103. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  104. data/lib/mail/parsers/received.rb +71 -0
  105. data/lib/mail/parsers/received.treetop +11 -0
  106. data/lib/mail/parsers/rfc2045.rb +464 -0
  107. data/lib/mail/parsers/rfc2045.treetop +36 -0
  108. data/lib/mail/parsers/rfc2822.rb +5318 -0
  109. data/lib/mail/parsers/rfc2822.treetop +410 -0
  110. data/lib/mail/parsers/rfc2822_obsolete.rb +3757 -0
  111. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  112. data/lib/mail/part.rb +116 -0
  113. data/lib/mail/parts_list.rb +43 -0
  114. data/lib/mail/patterns.rb +34 -0
  115. data/lib/mail/utilities.rb +211 -0
  116. data/lib/mail/version.rb +24 -0
  117. data/lib/mail/version_specific/ruby_1_8.rb +97 -0
  118. data/lib/mail/version_specific/ruby_1_9.rb +87 -0
  119. data/lib/mail.rb +80 -0
  120. data/lib/tasks/corpus.rake +125 -0
  121. data/lib/tasks/treetop.rake +10 -0
  122. metadata +255 -0
@@ -0,0 +1,105 @@
1
+ module Mail
2
+ class AttachmentsList < Array
3
+
4
+ def initialize(parts_list)
5
+ @parts_list = parts_list
6
+ @content_disposition_type = 'attachment'
7
+ parts_list.map { |p|
8
+ if p.content_type == "message/rfc822"
9
+ Mail.new(p.body).attachments
10
+ elsif p.parts.empty?
11
+ p if p.attachment?
12
+ else
13
+ p.attachments
14
+ end
15
+ }.flatten.compact.each { |a| self << a }
16
+ self
17
+ end
18
+
19
+ def inline
20
+ @content_disposition_type = 'inline'
21
+ self
22
+ end
23
+
24
+ # Returns the attachment by filename or at index.
25
+ #
26
+ # mail.attachments['test.png'] = File.read('test.png')
27
+ # mail.attachments['test.jpg'] = File.read('test.jpg')
28
+ #
29
+ # mail.attachments['test.png'].filename #=> 'test.png'
30
+ # mail.attachments[1].filename #=> 'test.jpg'
31
+ def [](index_value)
32
+ if index_value.is_a?(Fixnum)
33
+ self.fetch(index_value)
34
+ else
35
+ self.select { |a| a.filename == index_value }.first
36
+ end
37
+ end
38
+
39
+ def []=(name, value)
40
+ default_values = { :content_type => "#{set_mime_type(name)}; filename=\"#{name}\"",
41
+ :content_transfer_encoding => "#{guess_encoding}",
42
+ :content_disposition => "#{@content_disposition_type}; filename=\"#{name}\"" }
43
+
44
+ if value.is_a?(Hash)
45
+
46
+ default_values[:body] = value.delete(:content) if value[:content]
47
+
48
+ default_values[:body] = value.delete(:data) if value[:data]
49
+
50
+ encoding = value.delete(:transfer_encoding) || value.delete(:encoding)
51
+ if encoding
52
+ if Mail::Encodings.defined? encoding
53
+ default_values[:content_transfer_encoding] = encoding
54
+ else
55
+ raise "Do not know how to handle Content Transfer Encoding #{encoding}, please choose either quoted-printable or base64"
56
+ end
57
+ end
58
+
59
+ if value[:mime_type]
60
+ default_values[:content_type] = value.delete(:mime_type)
61
+ @mime_type = MIME::Types[default_values[:content_type]].first
62
+ default_values[:content_transfer_encoding] = guess_encoding
63
+ end
64
+
65
+ hash = default_values.merge(value)
66
+ else
67
+ default_values[:body] = value
68
+ hash = default_values
69
+ end
70
+
71
+ if hash[:body].respond_to? :force_encoding and hash[:body].respond_to? :valid_encoding?
72
+ if not hash[:body].valid_encoding? and default_values[:content_transfer_encoding].downcase == "binary"
73
+ hash[:body].force_encoding("BINARY")
74
+ end
75
+ end
76
+
77
+ attachment = Part.new(hash)
78
+ attachment.add_content_id(hash[:content_id])
79
+
80
+ @parts_list << attachment
81
+ end
82
+
83
+ # Uses the mime type to try and guess the encoding, if it is a binary type, or unknown, then we
84
+ # set it to binary, otherwise as set to plain text
85
+ def guess_encoding
86
+ if @mime_type && !@mime_type.binary?
87
+ "7bit"
88
+ else
89
+ "binary"
90
+ end
91
+ end
92
+
93
+ def set_mime_type(filename)
94
+ # Have to do this because MIME::Types is not Ruby 1.9 safe yet
95
+ if RUBY_VERSION >= '1.9'
96
+ new_file = String.new(filename).force_encoding(Encoding::BINARY)
97
+ ext = new_file.split('.'.force_encoding(Encoding::BINARY)).last
98
+ filename = "file.#{ext}".force_encoding('US-ASCII')
99
+ end
100
+ @mime_type = MIME::Types.type_for(filename).first
101
+ end
102
+
103
+ end
104
+ end
105
+
data/lib/mail/body.rb ADDED
@@ -0,0 +1,286 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+
4
+ # = Body
5
+ #
6
+ # The body is where the text of the email is stored. Mail treats the body
7
+ # as a single object. The body itself has no information about boundaries
8
+ # used in the MIME standard, it just looks at it's content as either a single
9
+ # block of text, or (if it is a multipart message) as an array of blocks o text.
10
+ #
11
+ # A body has to be told to split itself up into a multipart message by calling
12
+ # #split with the correct boundary. This is because the body object has no way
13
+ # of knowing what the correct boundary is for itself (there could be many
14
+ # boundaries in a body in the case of a nested MIME text).
15
+ #
16
+ # Once split is called, Mail::Body will slice itself up on this boundary,
17
+ # assigning anything that appears before the first part to the preamble, and
18
+ # anything that appears after the closing boundary to the epilogue, then
19
+ # each part gets initialized into a Mail::Part object.
20
+ #
21
+ # The boundary that is used to split up the Body is also stored in the Body
22
+ # object for use on encoding itself back out to a string. You can
23
+ # overwrite this if it needs to be changed.
24
+ #
25
+ # On encoding, the body will return the preamble, then each part joined by
26
+ # the boundary, followed by a closing boundary string and then the epilogue.
27
+ class Body
28
+
29
+ def initialize(string = '')
30
+ @boundary = nil
31
+ @preamble = nil
32
+ @epilogue = nil
33
+ @charset = nil
34
+ @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
35
+ @parts = Mail::PartsList.new
36
+ if string.blank?
37
+ @raw_source = ''
38
+ else
39
+ # Do join first incase we have been given an Array in Ruby 1.9
40
+ if string.respond_to?(:join)
41
+ @raw_source = string.join('')
42
+ elsif string.respond_to?(:to_s)
43
+ @raw_source = string.to_s
44
+ else
45
+ raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
46
+ end
47
+ end
48
+ @encoding = (only_us_ascii? ? '7bit' : '8bit')
49
+ set_charset
50
+ end
51
+
52
+ # Matches this body with another body. Also matches the decoded value of this
53
+ # body with a string.
54
+ #
55
+ # Examples:
56
+ #
57
+ # body = Mail::Body.new('The body')
58
+ # body == body #=> true
59
+ #
60
+ # body = Mail::Body.new('The body')
61
+ # body == 'The body' #=> true
62
+ #
63
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
64
+ # body.encoding = 'base64'
65
+ # body == "The body" #=> true
66
+ def ==(other)
67
+ if other.class == String
68
+ self.decoded == other
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ # Accepts a string and performs a regular expression against the decoded text
75
+ #
76
+ # Examples:
77
+ #
78
+ # body = Mail::Body.new('The body')
79
+ # body =~ /The/ #=> 0
80
+ #
81
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
82
+ # body.encoding = 'base64'
83
+ # body =~ /The/ #=> 0
84
+ def =~(regexp)
85
+ self.decoded =~ regexp
86
+ end
87
+
88
+ # Accepts a string and performs a regular expression against the decoded text
89
+ #
90
+ # Examples:
91
+ #
92
+ # body = Mail::Body.new('The body')
93
+ # body.match(/The/) #=> #<MatchData "The">
94
+ #
95
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
96
+ # body.encoding = 'base64'
97
+ # body.match(/The/) #=> #<MatchData "The">
98
+ def match(regexp)
99
+ self.decoded.match(regexp)
100
+ end
101
+
102
+ # Accepts anything that responds to #to_s and checks if it's a substring of the decoded text
103
+ #
104
+ # Examples:
105
+ #
106
+ # body = Mail::Body.new('The body')
107
+ # body.include?('The') #=> true
108
+ #
109
+ # body = Mail::Body.new("VGhlIGJvZHk=\n")
110
+ # body.encoding = 'base64'
111
+ # body.include?('The') #=> true
112
+ def include?(other)
113
+ self.decoded.include?(other.to_s)
114
+ end
115
+
116
+ # Allows you to set the sort order of the parts, overriding the default sort order.
117
+ # Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
118
+ # type coming after.
119
+ def set_sort_order(order)
120
+ @part_sort_order = order
121
+ end
122
+
123
+ # Allows you to sort the parts according to the default sort order, or the sort order you
124
+ # set with :set_sort_order.
125
+ #
126
+ # sort_parts! is also called from :encode, so there is no need for you to call this explicitly
127
+ def sort_parts!
128
+ @parts.each do |p|
129
+ p.body.set_sort_order(@part_sort_order)
130
+ @parts.sort!(@part_sort_order)
131
+ p.body.sort_parts!
132
+ end
133
+ end
134
+
135
+ # Returns the raw source that the body was initialized with, without
136
+ # any tampering
137
+ def raw_source
138
+ @raw_source
139
+ end
140
+
141
+ def get_best_encoding(target)
142
+ target_encoding = Mail::Encodings.get_encoding(target)
143
+ target_encoding.get_best_compatible(encoding, raw_source)
144
+ end
145
+
146
+ # Returns a body encoded using transfer_encoding. Multipart always uses an
147
+ # identiy encoding (i.e. no encoding).
148
+ # Calling this directly is not a good idea, but supported for compatibility
149
+ # TODO: Validate that preamble and epilogue are valid for requested encoding
150
+ def encoded(transfer_encoding = '8bit')
151
+ if multipart?
152
+ self.sort_parts!
153
+ encoded_parts = parts.map { |p| p.encoded }
154
+ ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
155
+ else
156
+ be = get_best_encoding(transfer_encoding)
157
+ dec = Mail::Encodings::get_encoding(encoding)
158
+ enc = Mail::Encodings::get_encoding(be)
159
+ if transfer_encoding == encoding and dec.nil?
160
+ # Cannot decode, so skip normalization
161
+ raw_source
162
+ else
163
+ # Decode then encode to normalize and allow transforming
164
+ # from base64 to Q-P and vice versa
165
+ enc.encode(dec.decode(raw_source))
166
+ end
167
+ end
168
+ end
169
+
170
+ def decoded
171
+ if !Encodings.defined?(encoding)
172
+ raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
173
+ else
174
+ Encodings.get_encoding(encoding).decode(raw_source)
175
+ end
176
+ end
177
+
178
+ def to_s
179
+ decoded
180
+ end
181
+
182
+ def charset
183
+ @charset
184
+ end
185
+
186
+ def charset=( val )
187
+ @charset = val
188
+ end
189
+
190
+ def encoding(val = nil)
191
+ if val
192
+ self.encoding = val
193
+ else
194
+ @encoding
195
+ end
196
+ end
197
+
198
+ def encoding=( val )
199
+ if val == "text" || val.blank? then
200
+ val = "8bit"
201
+ end
202
+ @encoding = (val == "text") ? "8bit" : val
203
+ end
204
+
205
+ # Returns the preamble (any text that is before the first MIME boundary)
206
+ def preamble
207
+ @preamble
208
+ end
209
+
210
+ # Sets the preamble to a string (adds text before the first MIME boundary)
211
+ def preamble=( val )
212
+ @preamble = val
213
+ end
214
+
215
+ # Returns the epilogue (any text that is after the last MIME boundary)
216
+ def epilogue
217
+ @epilogue
218
+ end
219
+
220
+ # Sets the epilogue to a string (adds text after the last MIME boundary)
221
+ def epilogue=( val )
222
+ @epilogue = val
223
+ end
224
+
225
+ # Returns true if there are parts defined in the body
226
+ def multipart?
227
+ true unless parts.empty?
228
+ end
229
+
230
+ # Returns the boundary used by the body
231
+ def boundary
232
+ @boundary
233
+ end
234
+
235
+ # Allows you to change the boundary of this Body object
236
+ def boundary=( val )
237
+ @boundary = val
238
+ end
239
+
240
+ def parts
241
+ @parts
242
+ end
243
+
244
+ def <<( val )
245
+ if @parts
246
+ @parts << val
247
+ else
248
+ @parts = Mail::PartsList.new[val]
249
+ end
250
+ end
251
+
252
+ def split!(boundary)
253
+ self.boundary = boundary
254
+ parts = raw_source.split("--#{boundary}")
255
+ # Make the preamble equal to the preamble (if any)
256
+ self.preamble = parts[0].to_s.strip
257
+ # Make the epilogue equal to the epilogue (if any)
258
+ self.epilogue = parts[-1].to_s.sub('--', '').strip
259
+ parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
260
+ self
261
+ end
262
+
263
+ def only_us_ascii?
264
+ raw_source.each_byte {|b| return false if (b == 0 || b > 127)}
265
+ true
266
+ end
267
+
268
+ def empty?
269
+ !!raw_source.to_s.empty?
270
+ end
271
+
272
+ private
273
+
274
+ def crlf_boundary
275
+ "\r\n\r\n--#{boundary}\r\n"
276
+ end
277
+
278
+ def end_boundary
279
+ "\r\n\r\n--#{boundary}--\r\n"
280
+ end
281
+
282
+ def set_charset
283
+ only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Thanks to Nicolas Fouché for this wrapper
4
+ #
5
+ require 'singleton'
6
+
7
+ module Mail
8
+
9
+ # The Configuration class is a Singleton used to hold the default
10
+ # configuration for all Mail objects.
11
+ #
12
+ # Each new mail object gets a copy of these values at initialization
13
+ # which can be overwritten on a per mail object basis.
14
+ class Configuration
15
+ include Singleton
16
+
17
+ def initialize
18
+ @delivery_method = nil
19
+ @retriever_method = nil
20
+ super
21
+ end
22
+
23
+ def delivery_method(method = nil, settings = {})
24
+ return @delivery_method if @delivery_method && method.nil?
25
+ @delivery_method = lookup_delivery_method(method).new(settings)
26
+ end
27
+
28
+ def lookup_delivery_method(method)
29
+ case method
30
+ when nil
31
+ Mail::SMTP
32
+ when :smtp
33
+ Mail::SMTP
34
+ when :sendmail
35
+ Mail::Sendmail
36
+ when :file
37
+ Mail::FileDelivery
38
+ when :test
39
+ Mail::TestMailer
40
+ else
41
+ method
42
+ end
43
+ end
44
+
45
+ def retriever_method(method = nil, settings = {})
46
+ return @retriever_method if @retriever_method && method.nil?
47
+ @retriever_method = lookup_retriever_method(method).new(settings)
48
+ end
49
+
50
+ def lookup_retriever_method(method)
51
+ case method
52
+ when nil
53
+ Mail::POP3
54
+ when :pop3
55
+ Mail::POP3
56
+ when :imap
57
+ Mail::IMAP
58
+ when :test
59
+ Mail::TestRetriever
60
+ else
61
+ method
62
+ end
63
+ end
64
+
65
+ def param_encode_language(value = nil)
66
+ value ? @encode_language = value : @encode_language ||= 'en'
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ class NilClass #:nodoc:
4
+ def to_crlf
5
+ ''
6
+ end
7
+
8
+ def to_lf
9
+ ''
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ class String #:nodoc:
3
+ def to_crlf
4
+ gsub(/\n|\r\n|\r/) { "\r\n" }
5
+ end
6
+
7
+ def to_lf
8
+ gsub(/\n|\r\n|\r/) { "\n" }
9
+ end
10
+
11
+ unless method_defined?(:ascii_only?)
12
+ # Provides all strings with the Ruby 1.9 method of .ascii_only? and
13
+ # returns true or false
14
+ US_ASCII_REGEXP = %Q{\x00-\x7f}
15
+ def ascii_only?
16
+ !(self =~ /[^#{US_ASCII_REGEXP}]/)
17
+ end
18
+ end
19
+
20
+ def not_ascii_only?
21
+ !ascii_only?
22
+ end
23
+
24
+ unless method_defined?(:bytesize)
25
+ alias :bytesize :length
26
+ end
27
+ end