Rails 4.2:使用deliver_later和无表格模型

时间:2015-01-12 08:50:41

标签: ruby-on-rails rails-activejob

我正在尝试使用Rails 4.2的deliver_later方法设置联系表单。但是,我只能使deliver_now工作,因为deliver_later正在尝试序列化我的对象并且每次都失败。

这是我的设置:

messages_controller.rb

class MessagesController < ApplicationController
  def new
    @message = Message.new
  end

  def create
    @message = Message.new(params[:message])
    if @message.valid?
      ContactMailer.contact_form(@message).deliver_later
      redirect_to root_path, notice: "Message sent! Thank you for contacting us."
    else
      render :new
    end
  end
end

contact_mailer.rb

class ContactMailer < ApplicationMailer
  default :to => Rails.application.secrets['email']

  def contact_form(msg)
    @message = msg
    mail(:subject => msg.subject, from: msg.email)
  end
end

message.rb

class Message
    include ActiveModel::Model
    include ActiveModel::Conversion

    ## Not sure if this is needed ##
    include ActiveModel::Serialization

    extend ActiveModel::Naming

    attr_accessor :name, :subject, :email, :body

    validates_presence_of :email, :body
    validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
    validates_length_of :body, :maximum => 1000

    def initialize(attributes = {})
      attributes.each { |name, value| send("#{name}=", value) }
    end

    ## Not sure if this is needed ##
    def attribtues
      {'name' => nil, 'subject' => nil, 'email' => nil, 'body' => nil}
    end
end

调用ContactMailer.contact_form(@message).deliver_later时收到的错误是:

ActiveJob::SerializationError in MessagesController#create 

Unsupported argument type: Message
Extracted source (around line #10): 
if @message.valid?
  ContactMailer.contact_form(@message).deliver_later
  redirect_to root_path, notice: "Message sent! Thank you for contacting us."
else
  render :new

理想情况下,我希望这是一个后台流程。我将很快添加类似Sidekiq的东西,但我认为最好事先修复此序列化问题。

任何帮助表示赞赏!谢谢:))

4 个答案:

答案 0 :(得分:11)

为了将您的类与ActiveJobdeliver_later代表所使用的)一起使用,它需要能够通过其ID唯一地标识该对象。此外,它需要在反序列化时通过ID找到它(在邮件程序/作业中不需要手动反序列化)。

class Message
  ...
  include GlobalID::Identification
  ...

  def id
    ...
  end

  def self.find(id)
    ...
  end
end

ActiveRecord会为您提供这些方法,但由于您没有使用它,您需要自己实现它。由你来决定你想要存储记录的位置,但老实说,我认为你最好使用ActiveRecord和下面的表格。

答案 1 :(得分:8)

一个简单的解决方案,可以避免使用ActiveRecord支持对象或创建不必要的表:

您也可以将消息参数传递给contact_form方法,然后在该方法中初始化Message对象,而不是将Message对象传递给contact_form方法。

这将解决问题而无需创建表,因为您正在初始化延迟作业工作者的内存空间中的对象。

例如:

<强> messages_controller.rb

MessagesController < ApplicationController
    def new
        @message = Message.new
    end

    def create
        @message = Message.new(params[:message])

        if @message.valid?
            ContactMailer.contact_form(params[:message]).deliver_later
            redirect_to root_path, notice: "Message sent! Thank you for contacting us."
        else
            render :new
        end
    end
end

<强> contact_mailer.rb

class ContactMailer < ApplicationMailer
    default :to => Rails.application.secrets['email']

    def contact_form(msg_params)
        @message = Message.new(msg_params)
        mail(:subject => msg.subject, from: msg.email)
    end
end

答案 2 :(得分:3)

我今天遇到了类似的问题并解决了如下问题。

  1. 将无表格对象转换为JSON sting
  2. 将其传递给邮寄者
  3. 将json字符串转换为hash
  4. 环境

    • Rails 5.0.2

    <强> messages_controller.rb

    class MessagesController < ApplicationController
    
      # ...
    
      def create
        @message = Message.new(message_params)
        if @message.valid?
          ContactMailer.contact_form(@message.serialize).deliver_later
          redirect_to root_path, notice: "Message sent! Thank you for contacting us."
        else
          render :new
        end
      end
    
      # ...
    end
    

    <强> contact_mailer.rb

    class ContactMailer < ApplicationMailer
      default :to => Rails.application.secrets['email']
    
      def contact_form(message_json)
        @message = JSON.parse(message_json).with_indifferent_access
    
        mail(subject: @message[:subject], from: @message[:email])
      end
    end
    

    <强> message.rb

    class Message
      include ActiveModel::Model
    
      attr_accessor :name, :subject, :email, :body
    
      validates_presence_of :email, :body
      validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
      validates_length_of :body, :maximum => 1000
    
      # Convert an object to a JSON string
      def serialize
        ActiveSupport::JSON.encode(self.as_json)
      end
    end
    

    希望这对任何人都有帮助。

答案 3 :(得分:0)

在传递给AJ并在邮件程序中反序列化之前,您需要序列化对象。