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
Related
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?
I'm using ruby to send transactional emails via Mandrill.
As part of my product, I want to be able to send the same email to two recipients and have them see each other's email address.
(Like an introduction mail between two people).
So I filled he "to" field with both emails, and on my dashboard is seems that both are sent.
But unfortunately only one of the recipients receive the mail and the details of the second recipient is hidden.
In conclusion, I have two problems:
Only one recipient gets the mail
Hidden details of the second recipient.
I approached Mandrill support and this is what they replied:
If you'd like to enable this option globally for all of the messages you send, then you'll want to ensure that you have the
"Expose The List Of Recipients When Sending To Multiple Addresses"
option enabled in your Sending Defaults.
If, instead of making that change globally, you'd like to enable it
for individual messages, you'll want to use the
X-MC-PreserveRecipients (SMTP header), or the preserve_recipients (API
parameter), and set it to 'true'.
If you set this option to true, we'll expose the list of recipients to
each other as you would see happen when sending mail from a typical
email client program.
It worked!
If you want both recipients to be able to see each other, you can pass an array of e-mails in the to option.
If you do not want either of them to see each other, you can, in a loop over the users, send said e-mail.
If using ActionMailer it can be done like so:
mail(
to: ['person1#gmail.com', 'person2#gmail.com']
)
Or in a loop:
[user1, user2].each do |user|
UserMailer.some_email(user).deliver_now
end
mail(
to: user.email
)
Post your code, I have an idea of what your problem may be. Remember that a method in an ActionMailer class should only return mail() and must not be looped over inside of that method.
tldr: do everything unrelated to e-mail outside mailer, pass through necessary data as params to the method, end method with mail() call.
I've been going nuts trying to write an automated test for my user sign up page. Users will be charged a recurring subscription via Stripe. They input their basic details (email, password, etc) and their credit card details on the same form, then the following flow happens:
(On the client-side) stripe.js makes an AJAX request to Stripe's servers, which (assuming everything is valid) returns a credit card token.
My javascript fills in a hidden input in the HTML form with the credit card token, and submits the form to my Rails server.
(Now on the server-side): I validate the user's basic details. If they're invalid, return (because there's no point charging them via Stripe if e.g. their email address is invalid so they can't create an account anyway.)
If they're valid, attempt to create a Stripe::Customer object, add the right subscription and charge them using Stripe's ruby gem etc.
All of this works perfectly fine... except I can't figure out how to test it. Testing step #4 is easy enough as it takes place on the server-side so I can mock out the Stripe calls with a gem like VCR.
Step #1 is what's giving me trouble. I've tried to test this using both puffing-billy and the stripe-ruby-mock gem, but nothing works. Here's my own javascript (simplified):
var stripeResponseHandler = function (status, response) {
console.log("response handler called");
if (response.error) {
// show the errors on the form
} else {
// insert the token into the form so it gets submitted to the server
$("#credit_card_token").val(response.id);
// Now submit the form.
$form.get(0).submit();
}
}
$form.submit(function (event) {
// Disable the submit button to prevent repeated clicks
$submitBtn.prop("disabled", true);
event.preventDefault();
console.log("creating token...");
Stripe.createToken(
// Get the credit card details from the form
// and input them here.
}, stripeResponseHandler);
// Prevent the form from submitting the normal way.
return false;
});
Just to reiterate, this all works fine when I test it manually. But my automated tests fail:
Failure/Error: expect{submit_form}.to change{User.count}.by(1)
expected result to have changed by 1, but was changed by 0
When I try to use the gem puffing-billy, it seems to be caching stripe.js itself (which is loaded from Stripe's own servers at js.stripe.com, not served from my own app, as Stripe don't support this.), but the call initiated by Stripe.createToken isn't being cached. In fact, when I log into my Stripe server logs, it doesn't seem that the call is even been made (or at least Stripe isn't receiving it.)
Note those console.log statements in my JS above. When I run my test suite, the line "creating token..." gets printed, but "response handler called." doesn't. Looks like the response handler is never being called.
I've left out some details because this question is already very long, but can add more on request. What am I doing wrong here? How can I test my sign up page?
UPDATE See [my comment on this Github issue] on stripe-ruby-mock for more info on what I've tried and failed.
If I understand correctly...
Capybara won't know about your ajax requests. You should be able to stub out AJAX requests with Sinatra. Have it return a fixtures much the same as VCR.
Here's an article on it.
https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http
You need to boot the Sinatra app in Capybara and then match the URLs in your ajax calls.
Something like:
class FakeContinousIntegration < Sinatra::Base
def self.boot
instance = new
Capybara::Server.new(instance).tap { |server| server.boot }
end
get '/some/ajax'
# send ajax back to capybara
end
end
When you boot the server, it will return the address and port which you can write to a config that your js can use.
#server = App.boot
Then I use the address and port to config the JS app
def write_js_config
config['api'] = "http://#{#server.host}:#{#server.port}"
config.to_json
end
In spec_helper.rb send in the config to the js so your script points to your sinatra app. Mine compiles with gulp. So I just build the config into to is before the tests run:
system('gulp build --env capybara')
I've had tests which worked on manual fail in Capybara/poltergeist due to timeout. In my case, the solution was to wait for all AJAX requests to finish. Reference
Not sure whether Stripe.js uses JQuery internally, try checking for a condition set by stripeResponseHandler.
In addition to the wait_for_ajax trick mentioned, it looks like you are calling expect before your database was updated. One way to check that would be to add a breakpoint in your code(binding.pry), and check if it is a race condition issue or not.
Also, as per Capybara's documentation, introducing an expectation of a UI change makes it 'smartly' wait for ajax calls to finish:
expect(page).not_to have_content('Enter credit card details')
I just learned/started using Sidekiq today to handle background processing of incoming email messages and attachments, but am a bit lost on the best way to get the email body and attachments into the worker for processing.
My RoR app is hosted on Heroku and receives incoming emails via Mailgun to a controller, which then kicks off my worker. Within the worker is a call to a 3rd party API to upload my email messages and attachments (think DropBox.)
Mailgun pre-parses everything and sends it over as parameters, but from what I understand about Sidekiq, I don't want to pass along entire objects such as the email body and/or attachments as shown here.
#attach_count = params["attachment-count"]
#from = params["from"]
#subject = params["subject"]
#msgbody = params["body-html"]
ProcessEmailWorker.perform_async(#id, #attach_count, #from, #subject, #msgbody)
What's the best practice for getting these items over to my worker?
I assume Mailgun is POSTing to your controller.
You can send the POST body as a single string parameter to Sidekiq and have it re-parse everything.
You can save the data to Redis or your database for processing in Sidekiq.
You can send the email content as a Hash of Strings:
{ 'subject' => ..., 'body' => ... }
After speaking with another developer I chose to do the following:
Set up a route in Mailgun to store the incoming email message, but to post a notification to my controller
Have my controller grab the incoming message ID and pass that along to my worker
From within my worker, use the message ID to perform a GET to Mailgun to retrieve the stored message (and its attachments)
Process the message/attachments and upload them to my cloud storage provider.
I am using ruby 2.0.0 and Rails 4.0.
I am sending a text message out via my rails app to my end user.
When they respond, I want to redirect them to an api call:
www.myapp.com/api/verify_text_response
Within that API call, I want to see what the text message sent from the end user to my url is. Ideally, I would receive a param of "text_response" that I could then do what I wanted to with.
How can I redirect a reply from my end_user to the above url, and capture the phone number it came from as well as the message sent to me in my params? If this isn't possible, how can I use TwiML to do something similar?
End Goal
For what it's worth, this is what I'm trying to accomplish:
I send a text message out - "Would you like to subscribe?"
The end user responds "Yes" or "No".
I change the subscribed attribute on my subscription model to true or false.
I send another text message saying either "You are subscribed." or "You are not subscribed.".
Item's #3 and #4 are based on the user responding "Yes" or "No" in item #2.
The twilio guide here for Ruby is the most useful documentation out there. They recommend using the Twilio gem in your Rails application by adding the twilio-ruby gem to your Gemfile.
All you need to do to is add the following code to one of your controller's actions (the one that is routed to by www.myapp.com/api/verify_text_response:
def receive_text
# Not exactly sure this is the right parameter, but it's easy to test
sender_message = params[:Body]
response = if (sender_message == "Yes")
"You are subscribed."
else
"You are not subscribed."
end
twiml = Twilio::TwiML::Response.new do |r|
r.Message(response)
end
twiml.text
end
To make your Rails application accessible to Twilio, follow the directions found on this page:
Copy and paste the URL of your server into the "SMS" URL of a number
on the Numbers page of your Twilio Account. On the page for that
number, change the Method from "POST" to "GET".
I wasn't exactly sure from looking at the documentation which parameter holds the user's response (I thought it was probably params[:Body]), but the best way to figure out is simply by printing out the parameters that the controller receives when you send a text message to your Twilio number.