2015-01-12 4 views
14

Ich versuche, ein Kontaktformular mit Rails 4.2 Deliver_later Methode einzurichten. Allerdings kann ich deliver_now nur zum Arbeiten bringen, da deliver_later versucht, mein Objekt zu serialisieren und jedes Mal fehlschlägt.Schienen 4.2: Verwenden von deliver_later mit einem tableless Modell

Hier ist mein Setup:

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 

Der Fehler, den ich bekommen, wenn ContactMailer.contact_form(@message).deliver_later Aufruf ist:

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 

Im Idealfall würde Ich mag diese ein Hintergrundprozess sein. Ich werde bald etwas wie Sidekiq hinzufügen, aber ich denke, es ist das Beste, wenn ich dieses Serialisierungsproblem vorher behebe.

Jede Hilfe wird geschätzt! Danke :)

Antwort

10

Um Ihre Klasse mit ActiveJob zu verwenden (das ist, was deliver_later delegiert), muss es in der Lage sein, das Objekt anhand seiner ID eindeutig zu identifizieren. Außerdem muss es später beim Deserialisieren von der ID gefunden werden (im Mailer/Job ist keine manuelle Deserialisierung erforderlich).

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

    def id 
    ... 
    end 

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

ActiveRecord würden Sie mit diesen Methoden zur Verfügung stellen, aber da man sie nicht verwenden, müssen Sie sie selbst implementieren. Es liegt an Ihnen zu entscheiden, wo Sie die Aufnahme speichern möchten, aber ehrlich gesagt, ich denke, Sie wären besser dran mit und der Tabelle darunter.

+0

Ich landete nur mit 'ActiveRecord' und seine zugrunde liegende. – DaniG2k

+1

@ DaniG2k Wie haben Sie ActiveRecord mit einem tabellenlosen Modell verwendet? – Marklar

+1

@Marklar Ich denke, er sagt, dass er einen zugrunde liegenden Tisch verwendet hat. – Nick

0

Sie müssen das Objekt serialisieren, bevor Sie an AJ übergeben und im Mailer deserialisiert werden.

8

Eine einfache Lösung, die das Objekt mit Active oder erstellen eine unnötige Tabelle vermeidet nach hinten:

Anstelle des Leitens des Message-Objekts an die CONTACT_FORM Methode, können Sie auch die Nachricht params an die CONTACT_FORM Methode übergeben und dann initialisieren Sie das Nachrichtenobjekt innerhalb dieser Methode.

Dadurch wird das Problem gelöst, ohne dass eine Tabelle erstellt werden muss, da Sie das Objekt im Arbeitsspeicher des verzögerten Jobs initialisieren.

Zum Beispiel:

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 
+0

Dies ist eine großartige Lösung. Vielen Dank! –

+0

In Rails 5 funktioniert das nicht: 'Nicht unterstützter Argumenttyp: ActionController :: Parameters' – Vadim

2

Ich hatte ein ähnliches Problem heute und löste es wie folgt.

  1. Wandeln tableless Objekt in einen JSON Stachel
  2. Pass es zu einem Mailer
  3. umrechnen JSON-String

Umwelt

  • Rails Hash 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 

Hope this jemand helfen.