【问题标题】:Rails 4.2: using deliver_later with a tableless modelRails 4.2:将 Deliver_later 与无表模型一起使用
【发布时间】:2015-03-09 23:52:21
【问题描述】:

我正在尝试使用 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 的东西,但我认为最好事先解决这个序列化问题。

感谢任何帮助!谢谢:)

【问题讨论】:

    标签: ruby-on-rails rails-activejob


    【解决方案1】:

    为了将您的类与ActiveJob 一起使用(这就是deliver_later 的委托),它需要能够通过对象的ID 来唯一地标识对象。另外,后面反序列化的时候需要通过ID来查找(在mailer/job中不需要手动反序列化)。

    class Message
      ...
      include GlobalID::Identification
      ...
    
      def id
        ...
      end
    
      def self.find(id)
        ...
      end
    end
    

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

    【讨论】:

    • 我最终只使用了ActiveRecord 及其底层。
    • @DaniG2k 您是如何将 ActiveRecord 与无表模型一起使用的?
    • @Marklar 我认为他是说他使用了基础表。
    【解决方案2】:

    避免必须使用 ActiveRecord 备份对象或创建不必要的表的简单解决方案:

    除了将 Message 对象传递给 contact_form 方法之外,您还可以将消息参数传递给 contact_form 方法,然后在该方法中初始化 Message 对象。

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

    例如:

    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
    

    【讨论】:

    • 这是一个很好的解决方案。谢谢!
    • 这在 Rails 5 中不起作用:Unsupported argument type: ActionController::Parameters
    【解决方案3】:

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

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

    环境

    • 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
    

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2016-02-15
        • 2015-11-30
        • 2015-08-02
        • 2010-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-28
        • 2012-10-30
        相关资源
        最近更新 更多