Andreas
Andreas

Reputation: 21

Rails 3: Mail::Message cannot be delivered after serializing

I want to serialize a Mail::Message object in order to store it in the database and send it later. My first attempt was to use the built-in serialize method of ActiveRecord like this:

class Email < ActiveRecord::Base
  serialize :mail

  # ...

end

Unfortunately, trying to save the record resulted in an exception

> foo.save
ArgumentError: wrong number of arguments (1 for 0)
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/mail-2.2.19/lib/mail/message.rb:1714:in `to_yaml'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:55:in `accept'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:36:in `<<'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/psych.rb:165:in `dump'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.7/lib/active_record/base.rb:1720:in `block in arel_attributes_values'
    (...)

So I simply created a custom accessor using the to_yaml / from_yaml functions of Mail::Message. But now I experience this strange behaviour:

mail_original = UserMailer.user_welcome_email(@user)
=> #<Mail::Message:93415800, Multipart: false, Headers: <From: (...) >, <To: (...) >, <Subject: (...) >, <Mime-Version: 1.0>, <Content-Type: text/plain>>
mail_clone = Mail::Message.from_yaml mail_original.to_yaml
=> #<Mail::Message:92940980, Multipart: false, Headers: <Date: (...) >, <From: (...) >, <To: (...) >, <Message-ID: (...) >, <Subject: (...) >, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: quoted-printable>>
mail_original.deliver
=> #<Mail::Message:93415800, Multipart: false, Headers: <Date: (...) >, <From: (...) >, <To: (...) >, <Message-ID: (...) >, <Subject: (...) >, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: quoted-printable>>
mail_clone.deliver
Errno::ECONNREFUSED: Connection refused - connect(2)
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:546:in `initialize'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:546:in `open'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:546:in `tcp_socket'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:555:in `block in do_start'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/timeout.rb:57:in `timeout'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/timeout.rb:87:in `timeout'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:555:in `do_start'
from /home/akrueger/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/net/smtp.rb:525:in `start'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/mail-2.2.19/lib/mail/network/delivery_methods/smtp.rb:128:in `deliver!'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/mail-2.2.19/lib/mail/message.rb:1989:in `do_delivery'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/mail-2.2.19/lib/mail/message.rb:232:in `deliver'
from (irb):68
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.7/lib/rails/commands/console.rb:44:in `start'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.7/lib/rails/commands/console.rb:8:in `start'
from /home/akrueger/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.7/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

Any idea on how to solve this problem? Why does serializing the object to yaml make the Message object undeliverable? Is there maybe another method to properly serialize the Message object to send it later?

Upvotes: 2

Views: 1251

Answers (2)

Andreas
Andreas

Reputation: 31

I realized the mail_clone object did not have proper delivery settings, which caused the failure of the deliver call. Browsing through the Rails sourcecode, I found the following solution:

mail_clone = Mail::Message.from_yaml mail_original.to_yaml
ActionMailer::Base.wrap_delivery_behavior mail_clone
mail_clone.deliver

So this might be a way to serialize the Mail::Message object:

class Email < ActiveRecord::Base
  def mail
    @mail_cached || begin
      m = Mail::Message.from_yaml(self.read_attribute :mail)
      ActionMailer::Base.wrap_delivery_behavior m
      m
    rescue
      nil
    end
  end

  def mail=(val)
    @mail_cached = val
    write_attribute :mail, val.to_yaml
  end

  # ...
end

Upvotes: 3

twmills
twmills

Reputation: 3015

Does specifying the class name help?

serialize :mail, 'Mail::Message' 

Upvotes: 0

Related Questions