mime 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +256 -0
- data/Rakefile +34 -0
- data/lib/mime.rb +32 -0
- data/lib/mime/composite_media_type.rb +169 -0
- data/lib/mime/content_types.rb +96 -0
- data/lib/mime/discrete_media_factory.rb +69 -0
- data/lib/mime/discrete_media_type.rb +79 -0
- data/lib/mime/error.rb +32 -0
- data/lib/mime/header_container.rb +34 -0
- data/lib/mime/headers/internet.rb +90 -0
- data/lib/mime/headers/mime.rb +118 -0
- data/lib/mime/media_type.rb +45 -0
- data/lib/mime/message.rb +51 -0
- data/lib/mime/parser.rb +16 -0
- data/test/mime_test.rb +386 -0
- data/test/scaffold/application.msg +8 -0
- data/test/scaffold/audio.msg +8 -0
- data/test/scaffold/book.pdf +0 -0
- data/test/scaffold/data.xml +17 -0
- data/test/scaffold/image.jpg +0 -0
- data/test/scaffold/image.msg +0 -0
- data/test/scaffold/index.html +6 -0
- data/test/scaffold/main.css +0 -0
- data/test/scaffold/mini.mov +0 -0
- data/test/scaffold/multipart_alternative.msg +17 -0
- data/test/scaffold/multipart_alternative_related.msg +0 -0
- data/test/scaffold/multipart_form_data_file.msg +0 -0
- data/test/scaffold/multipart_form_data_file_and_text.msg +0 -0
- data/test/scaffold/multipart_form_data_mixed.msg +0 -0
- data/test/scaffold/multipart_form_data_text.msg +40 -0
- data/test/scaffold/multipart_mixed_inline_and_attachment.msg +0 -0
- data/test/scaffold/multipart_mixed_inline_and_attachment2.msg +0 -0
- data/test/scaffold/multipart_related.msg +0 -0
- data/test/scaffold/plain_text_email.msg +9 -0
- data/test/scaffold/ruby.png +0 -0
- data/test/scaffold/song.mp3 +0 -0
- data/test/scaffold/text.msg +7 -0
- data/test/scaffold/unknown.yyy +1 -0
- data/test/scaffold/video.msg +8 -0
- metadata +92 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mime/error'
|
2
|
+
require 'mime/header_container'
|
3
|
+
require 'mime/headers/mime'
|
4
|
+
|
5
|
+
module MIME
|
6
|
+
|
7
|
+
#
|
8
|
+
# Abstract top-level media type class.
|
9
|
+
#
|
10
|
+
class MediaType
|
11
|
+
|
12
|
+
include Headers::MIME
|
13
|
+
|
14
|
+
attr_reader :headers
|
15
|
+
attr_accessor :body
|
16
|
+
protected :body, :body=
|
17
|
+
|
18
|
+
def initialize body, content_type
|
19
|
+
AbstractClassError.no_instantiation(self, MediaType)
|
20
|
+
|
21
|
+
@headers = HeaderContainer.new
|
22
|
+
@body = body
|
23
|
+
self.content_id = unique_id
|
24
|
+
self.content_type = content_type
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Transform the the MediaType object into a MIME message.
|
29
|
+
#
|
30
|
+
def to_s
|
31
|
+
"#{headers}\r\n\r\n#{body}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
#
|
37
|
+
# Generate a globally unique identifier for use in boundaries and IDs.
|
38
|
+
#
|
39
|
+
def unique_id
|
40
|
+
"#{object_id.abs}#{rand}"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/mime/message.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'mime/headers/internet'
|
3
|
+
require 'mime/headers/mime'
|
4
|
+
|
5
|
+
|
6
|
+
module MIME
|
7
|
+
|
8
|
+
#
|
9
|
+
# Construct textual messages using the RFC 2822 Internet message format.
|
10
|
+
#
|
11
|
+
class Message
|
12
|
+
|
13
|
+
include Headers::Internet
|
14
|
+
include Headers::MIME
|
15
|
+
|
16
|
+
# HeaderContainer access
|
17
|
+
attr_reader :headers
|
18
|
+
|
19
|
+
attr_accessor :body
|
20
|
+
|
21
|
+
#
|
22
|
+
# Return a Message object with body optionally set to +body+.
|
23
|
+
#
|
24
|
+
def initialize body = nil
|
25
|
+
@body = body
|
26
|
+
@headers = HeaderContainer.new
|
27
|
+
self.date = Time.now.rfc2822
|
28
|
+
self.message_id = "#{rand(1E9)}@#{__id__.abs}"
|
29
|
+
self.mime_version = "1.0 (Ruby MIME v#{VERSION})"
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Return the Internet message formatted representation of the instance.
|
34
|
+
#
|
35
|
+
def to_s
|
36
|
+
#--
|
37
|
+
# In an RFC 2822 message, the header and body sections must be separated
|
38
|
+
# by two line breaks (CRLF). One line break is deliberately missing,
|
39
|
+
# allowing a body supplier to append headers to the top-level message
|
40
|
+
# header section. Consequently, the body supplier is responsible for
|
41
|
+
# handling the body-header separation. Furthermore, if the +body+ is
|
42
|
+
# empty, the header section will be properly terminated, creating a
|
43
|
+
# standards compliant message.
|
44
|
+
#++
|
45
|
+
|
46
|
+
"#{headers}\r\n#{body}\r\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/mime/parser.rb
ADDED
data/test/mime_test.rb
ADDED
@@ -0,0 +1,386 @@
|
|
1
|
+
require 'mime'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class MIMETest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
include MIME
|
7
|
+
|
8
|
+
def test_make_top_level_rfc2822_message
|
9
|
+
data = "\r\nmessage body"
|
10
|
+
rfc2822_msg = Message.new
|
11
|
+
rfc2822_msg.body = data
|
12
|
+
expected_msg =
|
13
|
+
"Message-ID: <993708956@989739608>\r\n"\
|
14
|
+
"Date: Wed, 24 Oct 2008 15:45:31 -0700\r\n"\
|
15
|
+
"MIME-Version: 1.0 (Ruby MIME v0.1)\r\n"\
|
16
|
+
"\r\nmessage body\r\n"
|
17
|
+
|
18
|
+
assert_equal_mime_message expected_msg, rfc2822_msg.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_make_audio_message
|
22
|
+
audio = '0110000101110101011001000110100101101111'
|
23
|
+
audio_media = AudioMedia.new(audio, 'audio/midi')
|
24
|
+
audio_media.content_transfer_encoding = 'binary'
|
25
|
+
|
26
|
+
mime_msg = Message.new(audio_media).to_s
|
27
|
+
expected_mime_msg = IO.read(sd('/audio.msg'))
|
28
|
+
|
29
|
+
assert_equal_mime_message expected_mime_msg, mime_msg
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_make_application_message
|
33
|
+
application = '011000100110100101101110011000010111001001111001'
|
34
|
+
application_media = ApplicationMedia.new(application)
|
35
|
+
application_media.content_transfer_encoding = 'binary'
|
36
|
+
|
37
|
+
mime_msg = Message.new(application_media).to_s
|
38
|
+
expected_mime_msg = IO.read(sd('/application.msg'))
|
39
|
+
|
40
|
+
assert_equal_mime_message expected_mime_msg, mime_msg
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_make_image_message
|
44
|
+
image = IO.read(sd('/image.jpg'))
|
45
|
+
image_media = ImageMedia.new(image)
|
46
|
+
image_media.content_type = 'image/jpeg'
|
47
|
+
image_media.content_transfer_encoding = 'binary'
|
48
|
+
|
49
|
+
mime_msg = Message.new(image_media).to_s
|
50
|
+
expected_mime_msg = IO.read(sd('/image.msg'))
|
51
|
+
|
52
|
+
assert_equal_mime_message expected_mime_msg, mime_msg
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_make_text_message
|
56
|
+
text = 'a plain text message'
|
57
|
+
mime_msg = Message.new(TextMedia.new(text)).to_s
|
58
|
+
expected_mime_msg = IO.read(sd('/text.msg'))
|
59
|
+
|
60
|
+
assert_equal_mime_message expected_mime_msg, mime_msg
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_make_video_message
|
64
|
+
video = '0111011001101001011001000110010101101111'
|
65
|
+
video_media = VideoMedia.new(video)
|
66
|
+
video_media.content_type = 'video/mpeg'
|
67
|
+
video_media.content_transfer_encoding = 'binary'
|
68
|
+
|
69
|
+
mime_msg = Message.new(video_media).to_s
|
70
|
+
expected_mime_msg = IO.read(sd('/video.msg'))
|
71
|
+
|
72
|
+
assert_equal_mime_message expected_mime_msg, mime_msg
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_no_instantiation_of_abstract_classes
|
76
|
+
e = AbstractClassError
|
77
|
+
assert_raise(e) {MediaType.new(nil, nil)}
|
78
|
+
assert_raise(e) {DiscreteMediaType.new(nil)}
|
79
|
+
assert_raise(e) {CompositeMediaType.new(nil)}
|
80
|
+
assert_raise(e) {MultipartMedia.new(nil)}
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_multipart_form_data_with_text_entities
|
84
|
+
t1 = TextMedia.new('this is t1')
|
85
|
+
t2 = TextMedia.new('this is t2', 'text/enhanced')
|
86
|
+
t3 = open(sd('/index.html')) {|f| TextMedia.new(f.read, 'text/html')}
|
87
|
+
t4 = open(sd('/data.xml')) {|f| TextMedia.new(f.read, 'text/xml')}
|
88
|
+
|
89
|
+
form_data = MultipartMedia::FormData.new
|
90
|
+
form_data.add_entity t1, 'txt1'
|
91
|
+
form_data.add_entity t2, 'txt2'
|
92
|
+
form_data.add_entity t3, 'txt3'
|
93
|
+
form_data.add_entity t3, 'txt3'
|
94
|
+
|
95
|
+
expected = IO.read(sd('/multipart_form_data_text.msg'))
|
96
|
+
|
97
|
+
assert_equal_mime_message expected, form_data.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_multipart_form_data_with_file_entities
|
101
|
+
img1 = sd('/image.jpg')
|
102
|
+
img2 = sd('/ruby.png')
|
103
|
+
f1 = open(img1) {|f| ImageMedia.new(f.read, 'image/jpeg')}
|
104
|
+
f2 = open(img2) {|f| ImageMedia.new(f.read, 'image/png')}
|
105
|
+
|
106
|
+
f1.content_transfer_encoding = 'binary'
|
107
|
+
f2.content_transfer_encoding = 'binary'
|
108
|
+
|
109
|
+
form_data = MultipartMedia::FormData.new
|
110
|
+
form_data.add_entity f1, 'file1', img1
|
111
|
+
form_data.add_entity f2, 'file2', img2
|
112
|
+
|
113
|
+
expected = IO.read(sd('/multipart_form_data_file.msg'))
|
114
|
+
|
115
|
+
assert_equal_mime_message expected, form_data.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_multipart_form_data_with_file_and_text_entities
|
119
|
+
t1 = TextMedia.new('this is t1')
|
120
|
+
t2 = TextMedia.new('this is t2', 'text/enhanced')
|
121
|
+
|
122
|
+
img1 = sd('/image.jpg')
|
123
|
+
f1 = open(img1) {|f| ImageMedia.new(f.read, 'image/jpeg')}
|
124
|
+
f1.content_transfer_encoding = 'binary'
|
125
|
+
|
126
|
+
form_data = MultipartMedia::FormData.new
|
127
|
+
form_data.add_entity t1, 'txt1'
|
128
|
+
form_data.add_entity t2, 'txt2'
|
129
|
+
form_data.add_entity f1, 'img1', img1
|
130
|
+
|
131
|
+
expected = IO.read(sd('/multipart_form_data_file_and_text.msg'))
|
132
|
+
|
133
|
+
assert_equal_mime_message expected, form_data.to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_construction_of_plain_text_email_message
|
137
|
+
email_msg = Message.new
|
138
|
+
email_msg.to = {
|
139
|
+
'person1@domain.com' => 'Harry',
|
140
|
+
'person2@domain.com' => nil,
|
141
|
+
'person3@domain.com' => 'Mary'
|
142
|
+
}
|
143
|
+
email_msg.cc = {'Head Honcho' => 'bossman@domain.com'}
|
144
|
+
email_msg.from = 'person4@domain.com'
|
145
|
+
email_msg.subject = 'This is an important email'
|
146
|
+
#TODO - what do we do about this body thing???????? raw_body= and body=
|
147
|
+
email_msg.body = "\r\nThis is the all important email body"
|
148
|
+
#email_msg.body = TextMedia.new("This is the all important email body")
|
149
|
+
|
150
|
+
expected = IO.read(sd('/plain_text_email.msg'))
|
151
|
+
|
152
|
+
assert_equal_mime_message expected, email_msg.to_s
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_content_type_detection
|
156
|
+
(o = Object.new).extend(ContentTypes)
|
157
|
+
|
158
|
+
# test using file path
|
159
|
+
assert_equal 'application/pdf', o.file_type('book.pdf')
|
160
|
+
assert_equal 'audio/mpeg', o.file_type('/tmp/song.mp3')
|
161
|
+
assert_equal 'text/css', o.file_type('/tmp/main.css')
|
162
|
+
assert_equal 'video/quicktime', o.file_type('mini.mov')
|
163
|
+
assert_equal 'application/octet-stream', o.file_type('dsk.iso')
|
164
|
+
assert_equal nil, o.file_type('file.yyy')
|
165
|
+
|
166
|
+
# test using file object
|
167
|
+
img_file = sd('/ruby.png')
|
168
|
+
img_type = open(img_file) {|f| o.file_type(f)}
|
169
|
+
assert_equal 'image/png', img_type
|
170
|
+
assert_not_equal 'image/jpeg', img_type
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_object_instantiation_using_discrete_media_factory
|
174
|
+
application_file = sd('/book.pdf')
|
175
|
+
audio_file = sd('/song.mp3')
|
176
|
+
text_file = sd('/data.xml')
|
177
|
+
video_file = sd('/mini.mov')
|
178
|
+
image_file = sd('/image.jpg')
|
179
|
+
unknown_file = sd('/unknown.yyy')
|
180
|
+
|
181
|
+
dmf = DiscreteMediaFactory
|
182
|
+
|
183
|
+
# test using file path
|
184
|
+
assert_kind_of ApplicationMedia, dmf.create(application_file)
|
185
|
+
assert_kind_of AudioMedia, dmf.create(audio_file)
|
186
|
+
assert_kind_of TextMedia, dmf.create(text_file)
|
187
|
+
assert_kind_of VideoMedia, dmf.create(video_file)
|
188
|
+
|
189
|
+
# test using file object
|
190
|
+
open(image_file) do |image_file_obj|
|
191
|
+
assert_kind_of ImageMedia, dmf.create(image_file_obj)
|
192
|
+
end
|
193
|
+
open(text_file) do |text_file_obj|
|
194
|
+
assert_kind_of TextMedia, dmf.create(text_file_obj)
|
195
|
+
end
|
196
|
+
|
197
|
+
# raise for unknown file path and File object
|
198
|
+
assert_raises(UnknownContentError) {dmf.create(unknown_file)}
|
199
|
+
open(unknown_file) do |unknown_file_obj|
|
200
|
+
assert_raises(UnknownContentError) {dmf.create(unknown_file_obj)}
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_discrete_media_factory_creates_path_singleton_method
|
205
|
+
pdf_file_path = sd('/book.pdf')
|
206
|
+
|
207
|
+
media_obj = DiscreteMediaFactory.create(pdf_file_path)
|
208
|
+
assert_equal pdf_file_path, media_obj.path
|
209
|
+
|
210
|
+
open(pdf_file_path) do |pdf_file_obj|
|
211
|
+
media_obj = DiscreteMediaFactory.create(pdf_file_obj)
|
212
|
+
end
|
213
|
+
assert_equal pdf_file_path, media_obj.path
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_multipart_alternative_message_construction
|
217
|
+
txt_data = "*Header*\nmessage\n"
|
218
|
+
htm_data = "<html><body><h1>Header</h1><p>message</p></body></html>\n"
|
219
|
+
txt_msg = TextMedia.new(txt_data)
|
220
|
+
htm_msg = TextMedia.new(htm_data)
|
221
|
+
|
222
|
+
txt_msg.content_type = 'text/plain; charset=us-ascii'
|
223
|
+
htm_msg.content_type = 'text/html; charset=iso-8859-1'
|
224
|
+
|
225
|
+
alt_msg = MultipartMedia::Alternative.new
|
226
|
+
alt_msg.add_entity htm_msg
|
227
|
+
alt_msg.add_entity txt_msg
|
228
|
+
|
229
|
+
expected = IO.read(sd('/multipart_alternative.msg'))
|
230
|
+
|
231
|
+
assert_equal_mime_message expected, alt_msg.to_s
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_multipart_mixed_with_inline_and_attachment
|
235
|
+
mixed_msg = MultipartMedia::Mixed.new
|
236
|
+
|
237
|
+
open(sd('/image.jpg')) do |img_file|
|
238
|
+
img_msg = ImageMedia.new(img_file.read, 'image/jpeg')
|
239
|
+
mixed_msg.attach_entity(img_msg, 'filename' => img_file.path)
|
240
|
+
end
|
241
|
+
mixed_msg.inline_entity(TextMedia.new('This is plain text'))
|
242
|
+
|
243
|
+
expected = IO.read(sd('/multipart_mixed_inline_and_attachment.msg'))
|
244
|
+
|
245
|
+
assert_equal_mime_message expected, mixed_msg.to_s
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_multipart_mixed_message_construction_using_media_factory
|
249
|
+
img1 = sd('/image.jpg')
|
250
|
+
img2 = sd('/ruby.png')
|
251
|
+
txt = sd('/index.html')
|
252
|
+
bot_img = DiscreteMediaFactory.create(img1)
|
253
|
+
top_img = DiscreteMediaFactory.create(img2)
|
254
|
+
top_txt = DiscreteMediaFactory.create(txt)
|
255
|
+
|
256
|
+
mixed_msg = MultipartMedia::Mixed.new
|
257
|
+
mixed_msg.attach_entity(bot_img)
|
258
|
+
mixed_msg.attach_entity(top_img)
|
259
|
+
mixed_msg.inline_entity(top_txt)
|
260
|
+
|
261
|
+
expected = IO.read(sd('/multipart_mixed_inline_and_attachment2.msg'))
|
262
|
+
|
263
|
+
assert_equal_mime_message expected, mixed_msg.to_s
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_multipart_form_data_with_mixed_entity
|
267
|
+
txt = TextMedia.new('Joe Blow')
|
268
|
+
img1 = DiscreteMediaFactory.create(sd('/image.jpg'))
|
269
|
+
img2 = DiscreteMediaFactory.create(sd('/ruby.png'))
|
270
|
+
|
271
|
+
mixed_msg = MultipartMedia::Mixed.new
|
272
|
+
mixed_msg.attach_entity(img2)
|
273
|
+
mixed_msg.attach_entity(img1)
|
274
|
+
|
275
|
+
form = MultipartMedia::FormData.new
|
276
|
+
form.add_entity(mixed_msg, 'pics')
|
277
|
+
form.add_entity(txt, 'field1')
|
278
|
+
|
279
|
+
# similar to example 6 in RFC1867
|
280
|
+
expected = IO.read(sd('/multipart_form_data_mixed.msg'))
|
281
|
+
|
282
|
+
assert_equal_mime_message expected, form.to_s
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_multipart_related_html_message_with_embedded_image
|
286
|
+
img = DiscreteMediaFactory.create(sd('/ruby.png'))
|
287
|
+
img.content_transfer_encoding = 'binary'
|
288
|
+
|
289
|
+
html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
|
290
|
+
<html>
|
291
|
+
<body>
|
292
|
+
<h1>HTML multipart/related message</h1>
|
293
|
+
<p>txt before pix</p>
|
294
|
+
<img alt="cool ruby" src="cid:#{img.content_id}"/>
|
295
|
+
<p>txt after pix</p>
|
296
|
+
</body>
|
297
|
+
</html>
|
298
|
+
html
|
299
|
+
html_msg.content_transfer_encoding = '7bit'
|
300
|
+
|
301
|
+
related_msg = MultipartMedia::Related.new
|
302
|
+
related_msg.inline_entity(img)
|
303
|
+
related_msg.add_entity(html_msg)
|
304
|
+
|
305
|
+
expected = IO.read(sd('/multipart_related.msg'))
|
306
|
+
|
307
|
+
assert_equal_mime_message expected, related_msg.to_s
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_multipart_alternative_with_related_html_entity
|
311
|
+
img = DiscreteMediaFactory.create(sd('/ruby.png'))
|
312
|
+
img.content_transfer_encoding = 'binary'
|
313
|
+
|
314
|
+
html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
|
315
|
+
<html>
|
316
|
+
<body>
|
317
|
+
<h1>HTML multipart/alternative message</h1>
|
318
|
+
<p>txt before pix</p>
|
319
|
+
<img alt="cool ruby" src="cid:#{img.content_id}"/>
|
320
|
+
<p>txt after pix</p>
|
321
|
+
</body>
|
322
|
+
</html>
|
323
|
+
html
|
324
|
+
html_msg.content_transfer_encoding = '7bit'
|
325
|
+
|
326
|
+
text_msg = TextMedia.new(<<-text)
|
327
|
+
*HTML multipart/alternative message*
|
328
|
+
txt before pix
|
329
|
+
<cool ruby image>
|
330
|
+
txt after pix
|
331
|
+
text
|
332
|
+
text_msg.content_transfer_encoding = '7bit'
|
333
|
+
|
334
|
+
related_msg = MultipartMedia::Related.new
|
335
|
+
related_msg.inline_entity(img)
|
336
|
+
related_msg.add_entity(html_msg)
|
337
|
+
|
338
|
+
alt_msg = MultipartMedia::Alternative.new
|
339
|
+
alt_msg.add_entity(related_msg)
|
340
|
+
alt_msg.add_entity(text_msg)
|
341
|
+
|
342
|
+
expected = IO.read(sd('/multipart_alternative_related.msg'))
|
343
|
+
|
344
|
+
assert_equal_mime_message expected, alt_msg.to_s
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
private
|
349
|
+
|
350
|
+
#
|
351
|
+
# Test the equality of the normalized +expected+ and +actual+ MIME messages.
|
352
|
+
#
|
353
|
+
def assert_equal_mime_message expected, actual
|
354
|
+
assert_equal normalize_message(expected), normalize_message(actual)
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# Make messages comparable by removing *-ID header values, library version
|
359
|
+
# comment in MIME-Version header, Date header value, multipart/related
|
360
|
+
# content IDs, and boundaries.
|
361
|
+
#
|
362
|
+
def normalize_message message
|
363
|
+
# these are very delicate REs that are inter-dependent, be careful
|
364
|
+
match_id_headers = /-ID: <[^>]+>\r\n/
|
365
|
+
match_boundaries = /Boundary_\d+\.\d+/
|
366
|
+
match_lib_version = / \(Ruby MIME v\d\.\d\)/
|
367
|
+
match_date_header = /^Date: .*\d{4}\r\n/
|
368
|
+
match_related_cid = /cid:\d+\.\d+/
|
369
|
+
|
370
|
+
message.
|
371
|
+
gsub(match_related_cid, "cid").
|
372
|
+
gsub(match_id_headers, "-ID:\r\n").
|
373
|
+
gsub(match_boundaries, "Boundary_").
|
374
|
+
sub(match_date_header, "Date:\r\n").
|
375
|
+
sub(match_lib_version, " (Ruby MIME v0.0)")
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# Return the absolute path of +file+ under the test/scaffold directory.
|
380
|
+
#
|
381
|
+
def sd file
|
382
|
+
@scaffold_dir ||= File.join(File.dirname(__FILE__), 'scaffold')
|
383
|
+
@scaffold_dir + file
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|