actionmailer_csi 2.3.5.p6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/CHANGELOG +370 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +149 -0
  4. data/Rakefile +99 -0
  5. data/install.rb +30 -0
  6. data/lib/action_mailer/adv_attr_accessor.rb +30 -0
  7. data/lib/action_mailer/base.rb +706 -0
  8. data/lib/action_mailer/helpers.rb +113 -0
  9. data/lib/action_mailer/mail_helper.rb +17 -0
  10. data/lib/action_mailer/part.rb +107 -0
  11. data/lib/action_mailer/part_container.rb +55 -0
  12. data/lib/action_mailer/quoting.rb +61 -0
  13. data/lib/action_mailer/test_case.rb +64 -0
  14. data/lib/action_mailer/test_helper.rb +68 -0
  15. data/lib/action_mailer/utils.rb +7 -0
  16. data/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb +1466 -0
  17. data/lib/action_mailer/vendor/text_format.rb +10 -0
  18. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb +426 -0
  19. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
  20. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
  21. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
  22. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb +67 -0
  23. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
  24. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
  25. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb +960 -0
  26. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb +9 -0
  27. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
  28. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
  29. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
  30. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
  31. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb +6 -0
  32. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
  33. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb +248 -0
  34. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
  35. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb +1478 -0
  36. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb +379 -0
  37. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
  38. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
  39. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
  40. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
  41. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
  42. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
  43. data/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb +39 -0
  44. data/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb +5 -0
  45. data/lib/action_mailer/vendor/tmail.rb +17 -0
  46. data/lib/action_mailer/version.rb +9 -0
  47. data/lib/action_mailer.rb +62 -0
  48. data/lib/actionmailer.rb +2 -0
  49. data/test/abstract_unit.rb +62 -0
  50. data/test/asset_host_test.rb +54 -0
  51. data/test/delivery_method_test.rb +51 -0
  52. data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
  53. data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
  54. data/test/fixtures/auto_layout_mailer/multipart.text.html.erb +1 -0
  55. data/test/fixtures/auto_layout_mailer/multipart.text.plain.erb +1 -0
  56. data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
  57. data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
  58. data/test/fixtures/first_mailer/share.erb +1 -0
  59. data/test/fixtures/helper_mailer/use_example_helper.erb +1 -0
  60. data/test/fixtures/helper_mailer/use_helper.erb +1 -0
  61. data/test/fixtures/helper_mailer/use_helper_method.erb +1 -0
  62. data/test/fixtures/helper_mailer/use_mail_helper.erb +5 -0
  63. data/test/fixtures/helpers/example_helper.rb +5 -0
  64. data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
  65. data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
  66. data/test/fixtures/layouts/spam.html.erb +1 -0
  67. data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
  68. data/test/fixtures/raw_email +14 -0
  69. data/test/fixtures/raw_email10 +20 -0
  70. data/test/fixtures/raw_email12 +32 -0
  71. data/test/fixtures/raw_email13 +29 -0
  72. data/test/fixtures/raw_email2 +114 -0
  73. data/test/fixtures/raw_email3 +70 -0
  74. data/test/fixtures/raw_email4 +59 -0
  75. data/test/fixtures/raw_email5 +19 -0
  76. data/test/fixtures/raw_email6 +20 -0
  77. data/test/fixtures/raw_email7 +66 -0
  78. data/test/fixtures/raw_email8 +47 -0
  79. data/test/fixtures/raw_email9 +28 -0
  80. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  81. data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
  82. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  83. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  84. data/test/fixtures/second_mailer/share.erb +1 -0
  85. data/test/fixtures/templates/signed_up.erb +3 -0
  86. data/test/fixtures/test_mailer/_subtemplate.text.plain.erb +1 -0
  87. data/test/fixtures/test_mailer/body_ivar.erb +2 -0
  88. data/test/fixtures/test_mailer/custom_templating_extension.text.html.haml +6 -0
  89. data/test/fixtures/test_mailer/custom_templating_extension.text.plain.haml +6 -0
  90. data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
  91. data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
  92. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb +10 -0
  93. data/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +10 -0
  94. data/test/fixtures/test_mailer/implicitly_multipart_example.text.plain.erb +2 -0
  95. data/test/fixtures/test_mailer/implicitly_multipart_example.text.yaml.erb +1 -0
  96. data/test/fixtures/test_mailer/included_subtemplate.text.plain.erb +1 -0
  97. data/test/fixtures/test_mailer/rxml_template.builder +2 -0
  98. data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
  99. data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
  100. data/test/fixtures/test_mailer/signed_up_with_url.erb +5 -0
  101. data/test/mail_helper_test.rb +95 -0
  102. data/test/mail_layout_test.rb +123 -0
  103. data/test/mail_render_test.rb +116 -0
  104. data/test/mail_service_test.rb +1081 -0
  105. data/test/quoting_test.rb +99 -0
  106. data/test/test_helper_test.rb +129 -0
  107. data/test/tmail_test.rb +22 -0
  108. data/test/url_test.rb +76 -0
  109. metadata +195 -0
@@ -0,0 +1,261 @@
1
+ # scanner_r.rb
2
+ #
3
+ #--
4
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
26
+ # with permission of Minero Aoki.
27
+ #++
28
+ #:stopdoc:
29
+ require 'tmail/config'
30
+
31
+ module TMail
32
+
33
+ class TMailScanner
34
+
35
+ Version = '1.2.3'
36
+ Version.freeze
37
+
38
+ MIME_HEADERS = {
39
+ :CTYPE => true,
40
+ :CENCODING => true,
41
+ :CDISPOSITION => true
42
+ }
43
+
44
+ alnum = 'a-zA-Z0-9'
45
+ atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
46
+ tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip
47
+ atomchars = alnum + Regexp.quote(atomsyms)
48
+ tokenchars = alnum + Regexp.quote(tokensyms)
49
+ iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
50
+
51
+ eucstr = "(?:[\xa1-\xfe][\xa1-\xfe])+"
52
+ sjisstr = "(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+"
53
+ utf8str = "(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+"
54
+
55
+ quoted_with_iso2022 = /\A(?:[^\\\e"]+|#{iso2022str})+/n
56
+ domlit_with_iso2022 = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
57
+ comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
58
+
59
+ quoted_without_iso2022 = /\A[^\\"]+/n
60
+ domlit_without_iso2022 = /\A[^\\\]]+/n
61
+ comment_without_iso2022 = /\A[^\\()]+/n
62
+
63
+ PATTERN_TABLE = {}
64
+ PATTERN_TABLE['EUC'] =
65
+ [
66
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
67
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
68
+ quoted_with_iso2022,
69
+ domlit_with_iso2022,
70
+ comment_with_iso2022
71
+ ]
72
+ PATTERN_TABLE['SJIS'] =
73
+ [
74
+ /\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
75
+ /\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
76
+ quoted_with_iso2022,
77
+ domlit_with_iso2022,
78
+ comment_with_iso2022
79
+ ]
80
+ PATTERN_TABLE['UTF8'] =
81
+ [
82
+ /\A(?:[#{atomchars}]+|#{utf8str})+/n,
83
+ /\A(?:[#{tokenchars}]+|#{utf8str})+/n,
84
+ quoted_without_iso2022,
85
+ domlit_without_iso2022,
86
+ comment_without_iso2022
87
+ ]
88
+ PATTERN_TABLE['NONE'] =
89
+ [
90
+ /\A[#{atomchars}]+/n,
91
+ /\A[#{tokenchars}]+/n,
92
+ quoted_without_iso2022,
93
+ domlit_without_iso2022,
94
+ comment_without_iso2022
95
+ ]
96
+
97
+
98
+ def initialize( str, scantype, comments )
99
+ init_scanner str
100
+ @comments = comments || []
101
+ @debug = false
102
+
103
+ # fix scanner mode
104
+ @received = (scantype == :RECEIVED)
105
+ @is_mime_header = MIME_HEADERS[scantype]
106
+
107
+ atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[TMail.KCODE]
108
+ @word_re = (MIME_HEADERS[scantype] ? token : atom)
109
+ end
110
+
111
+ attr_accessor :debug
112
+
113
+ def scan( &block )
114
+ if @debug
115
+ scan_main do |arr|
116
+ s, v = arr
117
+ printf "%7d %-10s %s\n",
118
+ rest_size(),
119
+ s.respond_to?(:id2name) ? s.id2name : s.inspect,
120
+ v.inspect
121
+ yield arr
122
+ end
123
+ else
124
+ scan_main(&block)
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ RECV_TOKEN = {
131
+ 'from' => :FROM,
132
+ 'by' => :BY,
133
+ 'via' => :VIA,
134
+ 'with' => :WITH,
135
+ 'id' => :ID,
136
+ 'for' => :FOR
137
+ }
138
+
139
+ def scan_main
140
+ until eof?
141
+ if skip(/\A[\n\r\t ]+/n) # LWSP
142
+ break if eof?
143
+ end
144
+
145
+ if s = readstr(@word_re)
146
+ if @is_mime_header
147
+ yield [:TOKEN, s]
148
+ else
149
+ # atom
150
+ if /\A\d+\z/ === s
151
+ yield [:DIGIT, s]
152
+ elsif @received
153
+ yield [RECV_TOKEN[s.downcase] || :ATOM, s]
154
+ else
155
+ yield [:ATOM, s]
156
+ end
157
+ end
158
+
159
+ elsif skip(/\A"/)
160
+ yield [:QUOTED, scan_quoted_word()]
161
+
162
+ elsif skip(/\A\[/)
163
+ yield [:DOMLIT, scan_domain_literal()]
164
+
165
+ elsif skip(/\A\(/)
166
+ @comments.push scan_comment()
167
+
168
+ else
169
+ c = readchar()
170
+ yield [c, c]
171
+ end
172
+ end
173
+
174
+ yield [false, '$']
175
+ end
176
+
177
+ def scan_quoted_word
178
+ scan_qstr(@quoted_re, /\A"/, 'quoted-word')
179
+ end
180
+
181
+ def scan_domain_literal
182
+ '[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
183
+ end
184
+
185
+ def scan_qstr( pattern, terminal, type )
186
+ result = ''
187
+ until eof?
188
+ if s = readstr(pattern) then result << s
189
+ elsif skip(terminal) then return result
190
+ elsif skip(/\A\\/) then result << readchar()
191
+ else
192
+ raise "TMail FATAL: not match in #{type}"
193
+ end
194
+ end
195
+ scan_error! "found unterminated #{type}"
196
+ end
197
+
198
+ def scan_comment
199
+ result = ''
200
+ nest = 1
201
+ content = @comment_re
202
+
203
+ until eof?
204
+ if s = readstr(content) then result << s
205
+ elsif skip(/\A\)/) then nest -= 1
206
+ return result if nest == 0
207
+ result << ')'
208
+ elsif skip(/\A\(/) then nest += 1
209
+ result << '('
210
+ elsif skip(/\A\\/) then result << readchar()
211
+ else
212
+ raise 'TMail FATAL: not match in comment'
213
+ end
214
+ end
215
+ scan_error! 'found unterminated comment'
216
+ end
217
+
218
+ # string scanner
219
+
220
+ def init_scanner( str )
221
+ @src = str
222
+ end
223
+
224
+ def eof?
225
+ @src.empty?
226
+ end
227
+
228
+ def rest_size
229
+ @src.size
230
+ end
231
+
232
+ def readstr( re )
233
+ if m = re.match(@src)
234
+ @src = m.post_match
235
+ m[0]
236
+ else
237
+ nil
238
+ end
239
+ end
240
+
241
+ def readchar
242
+ readstr(/\A./)
243
+ end
244
+
245
+ def skip( re )
246
+ if m = re.match(@src)
247
+ @src = m.post_match
248
+ true
249
+ else
250
+ false
251
+ end
252
+ end
253
+
254
+ def scan_error!( msg )
255
+ raise SyntaxError, msg
256
+ end
257
+
258
+ end
259
+
260
+ end # module TMail
261
+ #:startdoc:
@@ -0,0 +1,280 @@
1
+ # encoding: utf-8
2
+ =begin rdoc
3
+
4
+ = String handling class
5
+
6
+ =end
7
+ #--
8
+ # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining
11
+ # a copy of this software and associated documentation files (the
12
+ # "Software"), to deal in the Software without restriction, including
13
+ # without limitation the rights to use, copy, modify, merge, publish,
14
+ # distribute, sublicense, and/or sell copies of the Software, and to
15
+ # permit persons to whom the Software is furnished to do so, subject to
16
+ # the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be
19
+ # included in all copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+ #
29
+ # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
30
+ # with permission of Minero Aoki.
31
+ #++
32
+
33
+ class StringInput#:nodoc:
34
+
35
+ include Enumerable
36
+
37
+ class << self
38
+
39
+ def new( str )
40
+ if block_given?
41
+ begin
42
+ f = super
43
+ yield f
44
+ ensure
45
+ f.close if f
46
+ end
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ alias open new
53
+
54
+ end
55
+
56
+ def initialize( str )
57
+ @src = str
58
+ @pos = 0
59
+ @closed = false
60
+ @lineno = 0
61
+ end
62
+
63
+ attr_reader :lineno
64
+
65
+ def string
66
+ @src
67
+ end
68
+
69
+ def inspect
70
+ "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
71
+ end
72
+
73
+ def close
74
+ stream_check!
75
+ @pos = nil
76
+ @closed = true
77
+ end
78
+
79
+ def closed?
80
+ @closed
81
+ end
82
+
83
+ def pos
84
+ stream_check!
85
+ [@pos, @src.size].min
86
+ end
87
+
88
+ alias tell pos
89
+
90
+ def seek( offset, whence = IO::SEEK_SET )
91
+ stream_check!
92
+ case whence
93
+ when IO::SEEK_SET
94
+ @pos = offset
95
+ when IO::SEEK_CUR
96
+ @pos += offset
97
+ when IO::SEEK_END
98
+ @pos = @src.size - offset
99
+ else
100
+ raise ArgumentError, "unknown seek flag: #{whence}"
101
+ end
102
+ @pos = 0 if @pos < 0
103
+ @pos = [@pos, @src.size + 1].min
104
+ offset
105
+ end
106
+
107
+ def rewind
108
+ stream_check!
109
+ @pos = 0
110
+ end
111
+
112
+ def eof?
113
+ stream_check!
114
+ @pos > @src.size
115
+ end
116
+
117
+ def each( &block )
118
+ stream_check!
119
+ begin
120
+ @src.each(&block)
121
+ ensure
122
+ @pos = 0
123
+ end
124
+ end
125
+
126
+ def gets
127
+ stream_check!
128
+ if idx = @src.index(?\n, @pos)
129
+ idx += 1 # "\n".size
130
+ line = @src[ @pos ... idx ]
131
+ @pos = idx
132
+ @pos += 1 if @pos == @src.size
133
+ else
134
+ line = @src[ @pos .. -1 ]
135
+ @pos = @src.size + 1
136
+ end
137
+ @lineno += 1
138
+
139
+ line
140
+ end
141
+
142
+ def getc
143
+ stream_check!
144
+ ch = @src[@pos]
145
+ @pos += 1
146
+ @pos += 1 if @pos == @src.size
147
+ ch
148
+ end
149
+
150
+ def read( len = nil )
151
+ stream_check!
152
+ return read_all unless len
153
+ str = @src[@pos, len]
154
+ @pos += len
155
+ @pos += 1 if @pos == @src.size
156
+ str
157
+ end
158
+
159
+ alias sysread read
160
+
161
+ def read_all
162
+ stream_check!
163
+ return nil if eof?
164
+ rest = @src[@pos ... @src.size]
165
+ @pos = @src.size + 1
166
+ rest
167
+ end
168
+
169
+ def stream_check!
170
+ @closed and raise IOError, 'closed stream'
171
+ end
172
+
173
+ end
174
+
175
+
176
+ class StringOutput#:nodoc:
177
+
178
+ class << self
179
+
180
+ def new( str = '' )
181
+ if block_given?
182
+ begin
183
+ f = super
184
+ yield f
185
+ ensure
186
+ f.close if f
187
+ end
188
+ else
189
+ super
190
+ end
191
+ end
192
+
193
+ alias open new
194
+
195
+ end
196
+
197
+ def initialize( str = '' )
198
+ @dest = str
199
+ @closed = false
200
+ end
201
+
202
+ def close
203
+ @closed = true
204
+ end
205
+
206
+ def closed?
207
+ @closed
208
+ end
209
+
210
+ def string
211
+ @dest
212
+ end
213
+
214
+ alias value string
215
+ alias to_str string
216
+
217
+ def size
218
+ @dest.size
219
+ end
220
+
221
+ alias pos size
222
+
223
+ def inspect
224
+ "#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
225
+ end
226
+
227
+ def print( *args )
228
+ stream_check!
229
+ raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
230
+ args.each do |s|
231
+ raise ArgumentError, 'nil not allowed' if s.nil?
232
+ @dest << s.to_s
233
+ end
234
+ nil
235
+ end
236
+
237
+ def puts( *args )
238
+ stream_check!
239
+ args.each do |str|
240
+ @dest << (s = str.to_s)
241
+ @dest << "\n" unless s[-1] == ?\n
242
+ end
243
+ @dest << "\n" if args.empty?
244
+ nil
245
+ end
246
+
247
+ def putc( ch )
248
+ stream_check!
249
+ @dest << ch.chr
250
+ nil
251
+ end
252
+
253
+ def printf( *args )
254
+ stream_check!
255
+ @dest << sprintf(*args)
256
+ nil
257
+ end
258
+
259
+ def write( str )
260
+ stream_check!
261
+ s = str.to_s
262
+ @dest << s
263
+ s.size
264
+ end
265
+
266
+ alias syswrite write
267
+
268
+ def <<( str )
269
+ stream_check!
270
+ @dest << str.to_s
271
+ self
272
+ end
273
+
274
+ private
275
+
276
+ def stream_check!
277
+ @closed and raise IOError, 'closed stream'
278
+ end
279
+
280
+ end