2015-01-12 11 views
14

Sto provando a configurare un modulo di contatto utilizzando il metodo deliver_later di Rails 4.2. Tuttavia, posso solo consegnare deliver_now al lavoro, poiché deliver_later sta tentando di serializzare il mio oggetto e fallisce ogni volta.Rails 4.2: uso di deliver_later con un modello senza tablature

Ecco la mia messa a punto:

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 

L'errore che ottengo quando si chiama 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 

Idealmente mi piacerebbe che questo sia un processo in background. Aggiungerò presto qualcosa come Sidekiq ma penso che sia meglio che risolvo il problema di serializzazione in anticipo.

Qualsiasi aiuto è apprezzato! Grazie :)

risposta

10

Per utilizzare la classe con (ovvero ciò che è deliver_later delegati), deve essere in grado di identificare univocamente l'oggetto tramite il suo ID. Inoltre, è necessario trovarlo più tardi dall'ID durante la deserializzazione (non è necessario deserializzare manualmente nel lavoro di posta/lavoro).

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

    def id 
    ... 
    end 

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

ActiveRecord sarebbe fornire con questi metodi, ma dal momento che non lo si utilizza, è necessario implementare da soli. Spetta a te decidere dove archiviare il record, ma onestamente penso che staresti meglio usando ActiveRecord e la tabella sottostante.

+0

Ho finito per utilizzare solo 'ActiveRecord' e il suo sottostante. – DaniG2k

+1

@ DaniG2k come hai usato ActiveRecord con un modello senza tablature? – Marklar

+1

@Marklar Penso che stia dicendo che ha usato un tavolo sottostante. – Nick

0

È necessario serializzare l'oggetto prima di passare a AJ e deserializzare nel mailer.

8

Una soluzione semplice che evita di dover eseguire l'oggetto con ActiveRecord o creare un tavolo inutile:

Invece di passare l'oggetto del messaggio al metodo contact_form, è anche possibile passare il messaggio params al metodo contact_form e poi inizializzare l'oggetto Message all'interno di quel metodo.

Questo risolverà il problema senza dover creare una tabella, perché si sta inizializzando l'oggetto nello spazio di memoria del lavoratore in ritardo.

Ad esempio:

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

Questa è un'ottima soluzione. Grazie! –

+0

Questo non funziona in Rails 5: 'Tipo di argomento non supportato: ActionController :: Parameters' – Vadim

2

Ho avuto un problema simile oggi e risolto nel modo seguente.

  1. Convertire un oggetto tableless in un pungiglione JSON
  2. passarlo ad un mailer
  3. Convertire la stringa JSON hash

Ambiente

  • 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 

Spero che questo vi aiuterà a nessuno.

Problemi correlati