# class Mail::Body

# Body¶ ↑

The body is where the text of the email is stored. Mail treats the body as a single object. The body itself has no information about boundaries used in the MIME standard, it just looks at its content as either a single block of text, or (if it is a multipart message) as an array of blocks of text.

A body has to be told to split itself up into a multipart message by calling split with the correct boundary. This is because the body object has no way of knowing what the correct boundary is for itself (there could be many boundaries in a body in the case of a nested MIME text).

Once split is called, Mail::Body will slice itself up on this boundary, assigning anything that appears before the first part to the preamble, and anything that appears after the closing boundary to the epilogue, then each part gets initialized into a Mail::Part object.

The boundary that is used to split up the Body is also stored in the Body object for use on encoding itself back out to a string. You can overwrite this if it needs to be changed.

On encoding, the body will return the preamble, then each part joined by the boundary, followed by a closing boundary string and then the epilogue.

### Public Class Methods

new(string = '') click to toggle source

    # File lib/mail/body.rb, line 28
    def initialize(string = '')
      @boundary = nil
      @preamble = nil
      @epilogue = nil
      @charset  = nil
      @part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
      @parts = Mail::PartsList.new
      if Utilities.blank?(string)
        @raw_source = ''
      else
        # Do join first incase we have been given an Array in Ruby 1.9
        if string.respond_to?(:join)
          @raw_source = string.join('')
        elsif string.respond_to?(:to_s)
          @raw_source = string.to_s
        else
          raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
        end
      end
      @encoding = (only_us_ascii? ? '7bit' : '8bit')
      set_charset
    end

### Public Instance Methods

<<( val ) click to toggle source

    # File lib/mail/body.rb, line 249
    def <<( val )
      if @parts
        @parts << val
      else
        @parts = Mail::PartsList.new[val]
      end
    end

==(other) click to toggle source

Matches this body with another body. Also matches the decoded value of this body with a string.

Examples:

    body = Mail::Body.new('The body')
    body == body #=> true

    body = Mail::Body.new('The body')
    body == 'The body' #=> true

    body = Mail::Body.new("VGhlIGJvZHk=\n")
    body.encoding = 'base64'
    body == "The body" #=> true

Calls superclass method

    # File lib/mail/body.rb, line 65
    def ==(other)
      if other.class == String
        self.decoded == other
      else
        super
      end
    end

=~(regexp) click to toggle source

Accepts a string and performs a regular expression against the decoded text

Examples:

    body = Mail::Body.new('The body')
    body =~ /The/ #=> 0

    body = Mail::Body.new("VGhlIGJvZHk=\n")
    body.encoding = 'base64'
    body =~ /The/ #=> 0

    # File lib/mail/body.rb, line 83
    def =~(regexp)
      self.decoded =~ regexp
    end

boundary() click to toggle source

Returns the boundary used by the body

    # File lib/mail/body.rb, line 236
    def boundary
      @boundary
    end

boundary=( val ) click to toggle source

Allows you to change the boundary of this Body object

    # File lib/mail/body.rb, line 241
    def boundary=( val )
      @boundary = val
    end

charset() click to toggle source

    # File lib/mail/body.rb, line 186
    def charset
      @charset
    end

charset=( val ) click to toggle source

    # File lib/mail/body.rb, line 190
    def charset=( val )
      @charset = val
    end

decoded() click to toggle source

    # File lib/mail/body.rb, line 174
    def decoded
      if !Encodings.defined?(encoding)
        raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
      else
        Encodings.get_encoding(encoding).decode(raw_source)
      end
    end

empty?() click to toggle source

    # File lib/mail/body.rb, line 273
    def empty?
      !!raw_source.to_s.empty?
    end

encoded(transfer_encoding = '8bit') click to toggle source

Returns a body encoded using transfer_encoding. Multipart always uses an identiy encoding (i.e. no encoding). Calling this directly is not a good idea, but supported for compatibility TODO: Validate that preamble and epilogue are valid for requested encoding

    # File lib/mail/body.rb, line 149
    def encoded(transfer_encoding = '8bit')
      if multipart?
        self.sort_parts!
        encoded_parts = parts.map { |p| p.encoded }
        ([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
      else
        be = get_best_encoding(transfer_encoding)
        dec = Mail::Encodings::get_encoding(encoding)
        enc = Mail::Encodings::get_encoding(be)
        if dec.nil?
            # Cannot decode, so skip normalization
            raw_source
        else
            # Decode then encode to normalize and allow transforming
            # from base64 to Q-P and vice versa
            decoded = dec.decode(raw_source)
            if defined?(Encoding) && charset && charset != "US-ASCII"
              decoded.encode!(charset)
              decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
            end
            enc.encode(decoded)
        end
      end
    end

encoding(val = nil) click to toggle source

    # File lib/mail/body.rb, line 194
    def encoding(val = nil)
      if val
        self.encoding = val
      else
        @encoding
      end
    end

encoding=( val ) click to toggle source

    # File lib/mail/body.rb, line 202
    def encoding=( val )
      @encoding = if val == "text" || Utilities.blank?(val)
          (only_us_ascii? ? '7bit' : '8bit')
      else
          val
      end
    end

epilogue() click to toggle source

Returns the epilogue (any text that is after the last MIME boundary)

    # File lib/mail/body.rb, line 221
    def epilogue
      @epilogue
    end

epilogue=( val ) click to toggle source

Sets the epilogue to a string (adds text after the last MIME boundary)

    # File lib/mail/body.rb, line 226
    def epilogue=( val )
      @epilogue = val
    end

get_best_encoding(target) click to toggle source

    # File lib/mail/body.rb, line 140
    def get_best_encoding(target)
      target_encoding = Mail::Encodings.get_encoding(target)
      target_encoding.get_best_compatible(encoding, raw_source)
    end

include?(other) click to toggle source

Accepts anything that responds to to_s and checks if it's a substring of the decoded text

Examples:

    body = Mail::Body.new('The body')
    body.include?('The') #=> true

    body = Mail::Body.new("VGhlIGJvZHk=\n")
    body.encoding = 'base64'
    body.include?('The') #=> true

    # File lib/mail/body.rb, line 111
    def include?(other)
      self.decoded.include?(other.to_s)
    end

match(regexp) click to toggle source

Accepts a string and performs a regular expression against the decoded text

Examples:

    body = Mail::Body.new('The body')
    body.match(/The/) #=> #<MatchData "The">

    body = Mail::Body.new("VGhlIGJvZHk=\n")
    body.encoding = 'base64'
    body.match(/The/) #=> #<MatchData "The">

    # File lib/mail/body.rb, line 97
    def match(regexp)
      self.decoded.match(regexp)
    end

multipart?() click to toggle source

Returns true if there are parts defined in the body

    # File lib/mail/body.rb, line 231
    def multipart?
      true unless parts.empty?
    end

only_us_ascii?() click to toggle source

    # File lib/mail/body.rb, line 269
    def only_us_ascii?
      !(raw_source =~ /[^\x01-\x7f]/)
    end

parts() click to toggle source

    # File lib/mail/body.rb, line 245
    def parts
      @parts
    end

preamble() click to toggle source

Returns the preamble (any text that is before the first MIME boundary)

    # File lib/mail/body.rb, line 211
    def preamble
      @preamble
    end

preamble=( val ) click to toggle source

Sets the preamble to a string (adds text before the first MIME boundary)

    # File lib/mail/body.rb, line 216
    def preamble=( val )
      @preamble = val
    end

raw_source() click to toggle source

Returns the raw source that the body was initialized with, without any tampering

    # File lib/mail/body.rb, line 136
    def raw_source
      @raw_source
    end

set_sort_order(order) click to toggle source

Allows you to set the sort order of the parts, overriding the default sort order. Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content type coming after.

    # File lib/mail/body.rb, line 118
    def set_sort_order(order)
      @part_sort_order = order
    end

sort_parts!() click to toggle source

Allows you to sort the parts according to the default sort order, or the sort order you set with :set_sort_order.

sort_parts! is also called from :encode, so there is no need for you to call this explicitly

    # File lib/mail/body.rb, line 126
    def sort_parts!
      @parts.each do |p|
        p.body.set_sort_order(@part_sort_order)
        p.body.sort_parts!
      end
      @parts.sort!(@part_sort_order)
    end

split!(boundary) click to toggle source

    # File lib/mail/body.rb, line 257
    def split!(boundary)
      self.boundary = boundary
      parts = extract_parts

      # Make the preamble equal to the preamble (if any)
      self.preamble = parts[0].to_s.strip
      # Make the epilogue equal to the epilogue (if any)
      self.epilogue = parts[-1].to_s.strip
      parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
      self
    end

to_s() click to toggle source

    # File lib/mail/body.rb, line 182
    def to_s
      decoded
    end

### Private Instance Methods

crlf_boundary() click to toggle source

    # File lib/mail/body.rb, line 302
    def crlf_boundary
      "\r\n--#{boundary}\r\n"
    end

end_boundary() click to toggle source

    # File lib/mail/body.rb, line 306
    def end_boundary
      "\r\n--#{boundary}--\r\n"
    end

extract_parts() click to toggle source

split parts by boundary, ignore first part if empty, append final part when closing boundary was missing

    # File lib/mail/body.rb, line 280
    def extract_parts
      parts_regex = /
        (?:                    # non-capturing group
          \A                |  # start of string OR
          \r\n                 # line break
         )
        (
          --#{Regexp.escape(boundary || "")}  # boundary delimiter
          (?:--)?                             # with non-capturing optional closing
        )
        (?=\s*$)                              # lookahead matching zero or more spaces followed by line-ending
      /x
      parts = raw_source.split(parts_regex).each_slice(2).to_a
      parts.each_with_index { |(part, _), index| parts.delete_at(index) if index > 0 && Utilities.blank?(part) }

      if parts.size > 1
        final_separator = parts[-2][1]
        parts << [""] if final_separator != "--#{boundary}--"
      end
      parts.map(&:first)
    end

set_charset() click to toggle source

    # File lib/mail/body.rb, line 310
    def set_charset
      only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
    end