Serialization error when sending email with .deliver_later - ruby-on-rails

I have a signup controller action that sends an email confirming registration. It works if I use .deliver_now, but if I try to .deliver_later I get this:
ActiveJob::SerializationError in Users::UsersController#process_signup
Unsupported argument type: Time
My code:
Emails
.with(template_data: processable_data.merge({:user => #u.safe_attributes }))
.signup_inactive
.deliver_later # not even passing a time to deliver the message at
The template_data is a hash with a bunch of user-specific attributes that may be used within the email template, I'm using Liquid for that as they're edited by users. It includes some DateTime values (created_at/updated_at) but ActiveJob is supposed to accept Date/Time/DateTime values afaik?
Am I doing something wrong?

Related

Sending mail 'to' OpenStruct through mailer

I have an app where users can sign up for workshops and admin has a possibility to write an e-mail to all the participants through the app. The fragment of code to send mail message to the group looks like this
workshop.students_all.each do |user|
WorkshopNotifyGroupMailer.notify_user(user, workshop, subject, body).deliver_later
end
so it's nothing extraordinary (User and Workshops are instances of models).
Now, I wanted to add one additional e-mail address to be sent each time a group is notified (just to have a copy how does the sent mail look like). I thought of doing it something like that (to keep the code short):
admin = OpenStruct.new(email: 'admin#email.com', first_name: 'Nameless') #These are fields taken from User instance by mailer
WorkshopNotifyGroupMailer.notify_user(admin, workshop, subject, body).deliver_later
Unfortunately, I receive "Unsupported argument type: OpenStruct" error. Is there a way to send an e-mail which uses an instance of a model using some kind of artificial structure? (In this case just assume admin is not on the user list and won't be)

Rails 4 controller won't call my actionmailer class

In my rails 4.2.5 (ruby 2.2.1) app I have an actionmailer class that is so simple. It doesn't even send mail, just does a printf:
class UserTommail < ActionMailer::Base
def joe
printf("\n***** In Emails.joe")
end
end
But when my controller calls this function, it never does the printf!
def contact_us
printf("\n***** TOMS EMAILLER")
UserTommail.joe()
redirect_to(root_path(), :notice => "Your Contact Us message has been successfully sent.")
printf("\n**** TOMS END")
end
The two printfs in the controller actually print their message, but the one in joe() never does. No errors or anything.
If I sabotage joe() to say joe() in the file user_tommail.rb, I get an error that the function can't be found, so I know the controller knows about it.
What am I doing wrong?
You should call deliver_now to fire the mailer to send the email:
UserTommail.joe.deliver_now
See the full list of available methods in docs.
For Rails 4 to send a an email
UserTommail.joe.deliver_now
or
UserTommail.joe.deliver_later #Enqueues the email to be delivered through Active Job. When the job runs it will send the email using deliver_now.
For Rails 3 to send a an email
UserTommail.joe.deliver
The problem here is that ActionMailer tries to be "clever" about what it does, and won't actually call your mailer method until its return value is needed.
Your mailer method joe will return an ActionMailer::MessageDelivery object, which wraps a Mail::Message object (even though you haven't specifically said you want to send an email). The Mail::Message gets lazily evaluated, meaning it won't be instantiated (and your method won't be called) until it's needed.
One way of forcing the evaluation would be to try and send the returned email with deliver_now or deliver_later, but another way would simply be to inspect the message.
if you had my_email = UserTommail.joe() and then called my_email.message, it would force the method to be run and you would see your printf in the console.

Send an asynchronous email

How can I send an asynchronous email with rails 4? By that I mean, when I click the 'send' button of a contact form, the page doesn't refresh, but the email is validated and sent, and a message is flashed to the user.
I've configured action_mailer correctly, and have a ContactForm mailer with one contact action that takes an email address as a parameter.
As a result,
ContactForm.contact("test#gmail.com").delivers #=> delivers email perfectly
But that's working on the command line. I don't really know the correct way to do this with a link. I mean, I could create a button that naviagates to send_email, and then I could have a route like this:
get 'send_email', to: 'contact#sendemail'
Then I would have a sendemail action which contains this method chain as shown above.
But this isn't asynchronous, and, also, I have no idea how I could validate the email's fields before sending the email, or highlighting invalid fields.
Is Ajax and JSON responses the key to highlighting the fields? What about the validation?
The resque_mailer seems to be a good way to send asyncronous emails. But why do I need this external gem when ajax is handled so well by vanilla rails?
The concept would be to have the form submit remotely. i.e submit to a create method in ContactsController. The method would then call a worker (resque/sidekiq) to send the email.
The create action can also respond to json. The json, response can either be a success or a fail (with errors).
On the AJAX success callback, you can trigger an alert, display div, or whatever notifying the user that the email was sent.
If the json results are returned with erros, then you can display the error message via JS.
This Railscasts Episode #171 Demonstrates sending emails using a background process with the help of DelayedJob

Default ActiveRecord/ActiveModel ::Errors are anonymous

Default ActiveModel::Errors are great, but i am solving problem, that the messages are anonymous. For example there is message should look like an email address. that belongs to email field, but what i want is to know that this error message is format type. And the other message doesn't match confirmation is confirmation type.
#<ActiveModel::Errors:0x000001054abef0 #base=#<User ... >,
#messages={
:password=>["doesn't match confirmation"],
:email=>["should look like an email address."]}>
Is there any gem for better errors, or do any of you have idea of monkey patch?
Thanks
In rails validations, you can add custom messages to be passed up the exception food chain, generated from the model.
ActiveRecord validations

How do I capture the output of an email in Rails and put it in a variable

The output from an email sent from ActionMailer output to the log, I wanted to know if I could get that output into a variable so I can store it in a file.
Ps. I forgot to mention that this is on Rails 2
As McStretch has pointed out, observer is the best way to handle every message that is delivered by a mailer. However, if you'd like to just capture 1 or 2 special cases, you can do the following:
Assuming you have an ActionMailer subclass called MyMailer, and an email called foobar,
# Rails 2.x
mail = MyMailer.create_foobar(...) # instead of MyMailer.deliver_foobar(...)
File.open('filename.txt', 'wb') {|f| f.write(mail.body) }
MyMailer.deliver(mail)
# Rails 3.x
mail = MyMailer.foobar(...) # instead of MyMailer.foobar(...).deliver
File.open('filename.txt', 'wb') {|f| f.write(mail.body) }
mail.deliver
You can use the register_interceptor or register_observer methods on ActionMailer to do something before or after sending the mail, respectively. The ActionMailer docs state:
Action Mailer provides hooks into the
Mail observer and interceptor methods.
These allow you to register objects
that are called during the mail
delivery life cycle.
An observer object must implement the
:delivered_email(message) method which
will be called once for every email
sent after the email has been sent.
An interceptor object must implement
the :delivering_email(message) method
which will be called before the email
is sent, allowing you to make
modifications to the email before it
hits the delivery agents. Your object
should make and needed modifications
directly to the passed in
Mail::Message instance.
Each of these methods provide a Mail::Message as an argument, so you should be able to get the desired data from that object and save it somewhere:
class MyInterceptor
def self.delivering_email(mail)
# do something before sending the email
end
end
class MyObserver
def self.delivered_email(mail)
# do something after sending the email
end
end
Example above from http://blog.envylabs.com/2010/04/new-in-rails3-beta2/

Resources