Permalink
Browse files

Refactored Attachment handling

  • Loading branch information...
1 parent 911034a commit a7dd291d60c4ec96408f21f990a238364932ca27 @mikel committed Jan 21, 2010
View
@@ -1,3 +1,7 @@
+== Thu Jan 21 05:27:17 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>
+
+* Major change to attachments, add_file now only accepts {:filename => 'full/path/to/file.png'} or {:filename => 'file.png', :content => 'string of file content'}
+
== Fri Jan 15 09:20:51 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>
* Rewrote all network classes to not use singletons. Means different Mail::Message objects can have different delivery methods.
View
@@ -244,7 +244,7 @@ or
to 'you@test.lindsaar.net'
subject 'Here is the image you wanted'
body File.read('body.txt')
- add_file {:filename => 'somefile.png', :data => File.read('/somefile.png')}
+ add_file {:filename => 'somefile.png', :content => File.read('/somefile.png')}
end
mail.deliver!
@@ -463,7 +463,7 @@ will try and guess the mime_type for you.
@mail = Mail.new
file_data = File.read('path/to/myfile.pdf')
- @mail.add_file(:filename => 'myfile.pdf', :data => file_data)
+ @mail.attachments['myfile.pdf'] = file_data
@mail.parts.first.attachment? #=> true
@mail.attachments.first.mime_type #=> 'application/pdf'
@mail.attachments.first.decoded == File.read('path/to/myfile.pdf') #=> true
View
@@ -56,6 +56,8 @@ module Mail # :doc:
require 'mail/message'
require 'mail/part'
require 'mail/header'
+ require 'mail/parts_list'
+ require 'mail/attachments_list'
require 'mail/body'
require 'mail/field'
require 'mail/field_list'
View
@@ -1,107 +0,0 @@
-# encoding: utf-8
-module Mail
- # An attachment is any data you want to attach to a mail message that is
- # not part of the body and can be extracted to a file in it's own right
- #
- # This includes images, sound files, text files, documents, anything really,
- # the only requisite is that it has a file name and an encoding.
- #
- # If you do not pass in an encoding when creating a new attachment, then
- # Mail::Attachment will assume that the data you pass in is raw data and
- # encode it with base64.
- #
- # If you pass in an encoding, Mail::Attachment will assume that the data
- # is encoded data and attempt to decode the data string with the encoding
- # you supply.
- #
- # So, raw data should be given in with no encoding, pre encoded data should
- # be given with an encoding type.
- #
- # Attachment will always encode with Base64, it's safe, it works, maybe in
- # the future we will allow you to encode with different types. If you really
- # want to encode with a different encoder, then pre encode the data and pass
- # it in with an encoding. Mail::Attachment will happily initialize for you,
- # however, if it doesn't understand the encoding, it will not decode for you
- # and raise an error, but if you can encode it, we assume you know how to
- # decode it, so just get back the encoded source (with #encoded) and then
- # decode at your leisure.
- class Attachment
-
- include Utilities
-
- # Raised when attempting to decode an unknown encoding type
- class UnknownEncodingType < StandardError #:nodoc:
- end
-
- def initialize(options_hash)
- case
- when options_hash[:data]
- if options_hash[:filename].respond_to?(:force_encoding)
- encoding = options_hash[:filename].encoding
- filename = File.basename(options_hash[:filename].force_encoding(Encoding::BINARY))
- @filename = filename.force_encoding(encoding)
- else
- @filename = File.basename(options_hash[:filename])
- end
-
- add_file(options_hash[:data], options_hash[:encoding])
- when options_hash[:filename]
- @filename = File.basename(options_hash[:filename])
- data = File.read(options_hash[:filename])
- add_file(data, options_hash[:encoding])
- end
- set_mime_type(@filename, options_hash[:mime_type])
- end
-
- def filename
- @filename
- end
-
- alias :original_filename :filename
-
- def encoded
- @encoded_data
- end
-
- def decoded
- if Mail::Encodings.defined?(@encoding)
- Mail::Encodings.get_encoding(@encoding).decode(@encoded_data)
- else
- raise UnknownEncodingType, "Don't know how to decode #{@encoding}, please call #encoded and decode it yourself."
- end
- end
-
- alias :read :decoded
-
- def mime_type
- @mime_type.to_s
- end
-
- private
-
- def set_mime_type(filename, mime_type)
- unless mime_type
- # Have to do this because MIME::Types is not Ruby 1.9 safe yet
- if RUBY_VERSION >= '1.9'
- new_file = String.new(filename).force_encoding(Encoding::BINARY)
- ext = new_file.split('.'.force_encoding(Encoding::BINARY)).last
- filename = "file.#{ext}".force_encoding('US-ASCII')
- end
- @mime_type = MIME::Types.type_for(filename).first
- else
- @mime_type = mime_type
- end
- end
-
- def add_file(data, encoding)
- if encoding # We are being given encoded data
- @encoded_data = data
- @encoding = encoding.to_s
- else # this is raw data
- @encoded_data = Mail::Encodings::Base64.encode(data)
- @encoding = 'base64'
- end
- end
-
- end
-end
@@ -0,0 +1,76 @@
+module Mail
+ class AttachmentsList < Array
+
+ def initialize(parts_list)
+ @parts_list = parts_list
+ parts_list.map { |p|
+ if p.parts.empty?
+ p if p.attachment?
+ else
+ p.attachments
+ end
+ }.flatten.compact.each { |a| self << a }
+ self
+ end
+
+ # Returns the attachment by filename or at index.
+ #
+ # mail.attachments['test.png'] = File.read('test.png')
+ # mail.attachments['test.jpg'] = File.read('test.jpg')
+ #
+ # mail.attachments['test.png'].filename #=> 'test.png'
+ # mail.attachments[1].filename #=> 'test.jpg'
+ def [](index_value)
+ if index_value.is_a?(Fixnum)
+ self.fetch(index_value)
+ else
+ self.select { |a| a.filename == index_value }.first
+ end
+ end
+
+ def []=(name, value)
+ default_values = { :content_type => "#{set_mime_type(name)}",
+ :content_transfer_encoding => 'Base64',
+ :content_disposition => "attachment; filename=\"#{name}\"" }
+
+ if value.is_a?(Hash)
+
+ default_values[:body] = value.delete(:content) if value[:content]
+
+ default_values[:body] = value.delete(:data) if value[:data]
+
+ # Only force encode base64 if the user has not specified an encoding
+ if value[:transfer_encoding]
+ default_values[:content_transfer_encoding] = value.delete(:transfer_encoding)
+ elsif value[:encoding]
+ default_values[:content_transfer_encoding] = value.delete(:encoding)
+ else
+ default_values[:body] = Mail::Encodings::Base64.encode(default_values[:body])
+ end
+
+ if value[:mime_type]
+ default_values[:content_type] = value.delete(:mime_type)
+ end
+
+ hash = default_values.merge(value)
+ else
+ default_values[:body] = Mail::Encodings::Base64.encode(value)
+ hash = default_values
+ end
+
+ @parts_list << Part.new(hash)
+ end
+
+ def set_mime_type(filename)
+ # Have to do this because MIME::Types is not Ruby 1.9 safe yet
+ if RUBY_VERSION >= '1.9'
+ new_file = String.new(filename).force_encoding(Encoding::BINARY)
+ ext = new_file.split('.'.force_encoding(Encoding::BINARY)).last
+ filename = "file.#{ext}".force_encoding('US-ASCII')
+ end
+ @mime_type = MIME::Types.type_for(filename).first
+ end
+
+ end
+end
+
View
@@ -31,7 +31,7 @@ def initialize(string = '')
@preamble = nil
@epilogue = nil
@part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
- @parts = []
+ @parts = Mail::PartsList.new
if string.blank?
@raw_source = ''
else
@@ -103,15 +103,7 @@ def set_sort_order(order)
#
# sort_parts! is also called from :encode, so there is no need for you to call this explicitly
def sort_parts!
- order = @part_sort_order
- @parts = @parts.sort do |a, b|
- # OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
- # single email message... please show me a use case and I'll put more work into this method,
- # in the meantime, it works :)
- a_order = order.index(a[:content_type].string.downcase) || 10000
- b_order = order.index(b[:content_type].string.downcase) || 10000
- a_order <=> b_order
- end
+ @parts.sort!(@part_sort_order)
end
# Returns the raw source that the body was initialized with, without
@@ -203,7 +195,7 @@ def <<( val )
if @parts
@parts << val
else
- @parts = [val]
+ @parts = Mail::PartsList.new[val]
end
end
@@ -214,7 +206,7 @@ def split!(boundary)
self.preamble = parts[0].to_s.strip
# Make the epilogue equal to the epilogue (if any)
self.epilogue = parts[-1].to_s.sub('--', '').strip
- @parts = parts[1...-1].to_a.map { |part| Mail::Part.new(part) }
+ parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
self
end
@@ -1,5 +1,9 @@
# encoding: utf-8
module Mail
+ # Raised when attempting to decode an unknown encoding type
+ class UnknownEncodingType < StandardError #:nodoc:
+ end
+
module Encodings
include Mail::Patterns
Oops, something went wrong.

0 comments on commit a7dd291

Please sign in to comment.