I have a RubyOnRails 3.2.x application that sends out mails using Actionmailer.
The code goes something like this:
class Mymailer < ActionMailer::Base
def sendout
mail(:to->"someone#somewhere.com", :from ...........
end
end
Instead of sending out that email, I want it to be rendered to a string which I then plan to process differently
PS: In my specific case I want to pass that string to a Node.JS server who will do the actual sending of the mail, I am using RubyOnRails to handle the multi language support and number formatting (yes I have multiple templates for the different languages that I support).
Since Ruby won't be doing the email sending, there's no need to use the Mailer.
Ideally you could generate a JSON string representation of the email like:
# at the top of the file
require 'json'
# then in your method
json_string = {
:to => "email#example.com",
:from =>"sender#example.com",
:body => "this is the body"
}.to_json
Then post this string to your node.js server from (for example) your controller or whatever is currently calling your mailer.
However, since you want to render the email using templates which pull in DB fields and use the i18n functionality of Rails, you could use the Mailer but render the result to a string like follows:
mail(to: "mail#example.com") do |format|
format.html do
string = render_to_string("your_template")
call_node_js(string)
end
end
If what you want is getting the whole mail representation, including headers, then you might want to browse the source code to see what happens behind the curtain.
A good starting point is the ActionMailer::Base method #mail (Rails 4):
http://api.rubyonrails.org/classes/ActionMailer/Base.html#method-i-mail
Anyway, if the sending is handled by Node.js, you don't need to do it. Just build a JSON object like Olly suggested and pass it through.
Related
Good day everyone,
I created a mailer to send an email to my client. As of right now im still testing it, but I couldn't make it to work. I've read redis, sidekiq, rails_mailer and still nothing. I can see that the mail is in the queue of sidekiq UI but I cant receive the email.
Here's the flow of my code.
User will check the text box on the view if they wanted to send an email to a client.
I a method will be triggered on the controller. Heres my code.
def send_workorder_message
if params.has_key?(:to_send_email)
WorkorderMessage::WorkorderMessageJob.perform_in(10.seconds, #curr_user, params[:message])
end
endv
then a workorder job is created. heres the code.
class WorkorderMessage::WorkorderMessageJob
# include SuckerPunch::Job
include Sidekiq::Worker
sidekiq_options queue: 'mailers'
def perform(user, message)
Spree::WorkorderMailer.workorder_send_to_email(user, message).deliver_now
# ActiveRecord::Base.connection_pool.with_connection do
# end
end
end
after that it will trigger the WorkorderMailer heres the code.
class WorkorderMailer < BaseMailer
def workorder_send_to_email(to_user, message)
ActiveRecord::Base.connection_pool.with_connection do
subject = "sample message mailer"
#message = message
#user = to_user
mail(
to: #user.email,
# 'reply-to': Spree::Store.current.support_address,
from: Spree::Store.current.support_address,
subject: subject
)
end
end
end
when I use the preview mailer I can see the UI working fine.
Also I've noticed that on sidekiq view I see this User Obj. I that normal?
According to the Sidekiq documentation, the arguments you pass must be primitives that cleanly serialize to JSON, and not full Ruby objects, like the user you are passing here:
Complex Ruby objects do not convert to JSON, by default it will
convert with to_s and look like #<Quote:0x0000000006e57288>. Even if
they did serialize correctly, what happens if your queue backs up and
that quote object changes in the meantime? Don't save state to
Sidekiq, save simple identifiers. Look up the objects once you
actually need them in your perform method.
The arguments you pass to perform_async must be composed of simple
JSON datatypes: string, integer, float, boolean, null(nil), array and
hash. This means you must not use ruby symbols as arguments. The
Sidekiq client API uses JSON.dump to send the data to Redis. The
Sidekiq server pulls that JSON data from Redis and uses JSON.load to
convert the data back into Ruby types to pass to your perform method.
Don't pass symbols, named parameters or complex Ruby objects (like
Date or Time!) as those will not survive the dump/load round trip
correctly.
I would suggest you change it to lookup the User by ID within the job, and only pass the ID instead of the entire user object.
# pass #curr_user.id instead of #curr_user
WorkorderMessage::WorkorderMessageJob.perform_in(10.seconds, #curr_user.id, params[:message])
# accept the ID instead of user here
def perform(user_id, message)
# get the user object here
user = User.find(user_id)
# send the mail
mail(
to: user.email,
#...
end
I noticed that Mail gem doesn't get loaded outside ActionMailer context, but the gem is present in the proper gem group of Gemfile and then "loaded" upon Rails initialization.
To create a Mail object in a controller using Mail.new I need to put
require 'mail'
at the top of example_controller.rb, before
class ExampleController < ApplicationController
Could someone explain to me why?
Background:
I need to build an app whose functionality is primarily based on receiving emails of various types. I need to expose only one email address, where people will send all of these emails.
My mail server will pipe the raw email to a ruby script that sends the raw email as a POST request to my app.
Then, I need to identify which kind of email has arrived inferring that from its content (primarily attachments).
For example, let's have emails of types A, B and C. My email processing function will call one of three methods once it identifies the type of the email.
Where should I put the processing function?
You shouldn't be creating mail objects in your controllers. You should create a mailer and then call that mailer from your controller to send any emails you need to.
For example, say you have the following mailer in app/mailers/customer_mailer.rb:
class CustomerMailer < ActionMailer::Base
def send_reminder(user)
#user = user
mail to: user.email, subject: 'Reminder'
end
end
You can then call this from your controller as needed:
class ExampleController < ApplicationController
def some_action
#user = User.find(params[:id])
CustomerMailer.send_reminder(#user).deliver_now
end
end
This way your controller can focus on the implementations specific to controlling the request, and leave the mailer to worry about how to send email.
It's worth noting you also have access to a deliver_later method if you're using a background job runner.
Take a look at the documentation for more details: http://guides.rubyonrails.org/action_mailer_basics.html
Is there a gem to do this, and if not, what is the best approach. I'm assuming i'd store the emails in a newsletter database, and would want a form that emails everyone at once. thanks!
No gem for this that I know of.
Actually, due to processing issues, the best way would be to use external bulk email service provider via provider's API.
However, building your own newsletter system is no different from building your regular MVC. Only addition are mailers and mailer views.
(1) So you create model that deals with registration data (:name, :email, :etc...) and model that deals with newsletter itself.
(2) Controller that deals with it (CRUD) + (:send). Send takes each recipient, sends data to mailer which creates email and then sends it.
def send
#newsletter = Newsletter.find(:params['id'])
#recipients = Recipient.all
#recipients.each do |recipient|
Newsletter.newsletter_email(recipient, #newsletter).deliver
end
end
(3) Mailer builds an email, makes some changes in each email if you want to do something like that and returns it to controller action send for delivery.
class Newsletter < ActionMailer::Base
default :from => "my_email#example.com", :content_type => "multipart/mixed"
def newsletter_email(recipient, newsletter)
# these are instance variables for newsletter view
#newsletter = newsletter
#recipient = recipient
mail(:to => recipient.email, :subject => newsletter.subject)
end
end
(4) Ofc, you need mailer views which are just like regular views. Well in multipart there is:
newsletter_email.html.erb (or haml)
newsletter_email.txt.erb
When you want to send both html and text only emails. Instance variables are defined in mailer ofc.
And... that is it. Well you could use delayed job to send them since sending more than several hundred emails can take a while. Several ms times n can be a lot of time.
Have fun.
Please check the maktoub gem there is a blog post over it.
No gem that I know of too and building on #Krule's answer, here's a screencast of setting up mailers in Rails.
How to create, preview and send email from your rails app
I was looking for something similar when I found this. I think with a bit of customization, it can easily be used to create newsletter emails too.
Save money! Spend somewhere else.
I set my app to receive incoming emails via a post from a service. The controller that receives the posts looks a little like this:
class IncomingMailsController < ApplicationController
require 'mail'
skip_before_filter :verify_authenticity_token
def create
message = Mail.new(params[:message])
message_plain = (params[:plain])
Rails.logger.info 'message2.plain:'
Rails.logger.info message2
render :text => 'success', :status => 200 # a status of 404 would reject the mail
end
end
That successfully is delivering the entire email message, replies,forward history etc. The issue is I'd like to be able to extract just the actual reply text.
Currently I get:
That's not a bad idea. Lets try that out.
On Nov 17, 2010, at 4:18 PM, XXXXX # XXXXXXXX wrote:
> There's a new reply:
And I'd like to know how rails devs get just the reply:
That's not a bad idea. Lets try that out.
Ideas? Thanks
There's no guaranteed way to get the entire message however it's common practice to make use of a separator and then use some code to parse out the response line.
If you take a look at the code in the open source project teambox for example you see the something really similar to the following:
def strip_responses(body)
# For GMail. Matches "On 19 August 2010 13:48, User <proj+conversation+22245#app.teambox.com<proj%2Bconversation%2B22245#app.teambox.com>> wrote:"
body.strip.
gsub(/\n[^\r\n]*\d{2,4}.*\+.*\d#app.teambox.com.*:.*\z/m, '').
split("---------separator---------").first.
split("<div class='email'").first.
strip
end
Not a perfect gem, but you could try this gem from Github: Email Reply Parser
Have a look at the extended_email_reply_parser, which also includes github's email_reply_parser.
Install
Add it to the Gemfile:
# Gemfile
gem 'extended_email_reply_parser'
Usage
Then, you can parse the email reply like this:
message_plain = ExtendedEmailReplyParser.parse message
Example
Applied to your code:
class IncomingMailsController < ApplicationController
skip_before_filter :verify_authenticity_token
def create
message = Mail.new(params[:message])
message_plain = ExtendedEmailReplyParser.parse(message)
render :text => 'success', :status => 200 # a status of 404 would reject the mail
end
end
email_reply_parser vs. extended_email_reply_parser
The email_reply_parser is used and developed by github. It's small and efficient, but can't handle some edge cases, where the sender's email client doesn't properly format the previous conversation as quote.
The extended_email_reply_parser is an extendible wrapper around github's parser. It's not as efficient as the original one. But, it's easier to extend if the typical emails you're parsing are not handled quite right out of the box.
Resources
email_reply_parser
extended_email_reply_parser
Examples for extending the parser
I'd like to send emails from a table every night at midnight, so I've created a model to track all communications to be sent. This Communication model stores who the letter is to, from, and the contents of the letter to be sent as html. The user enters a criteria to match for whom the letter is intended: state, country, favorite color, and so which returns a list of acceptable matches.
This list of acceptable matches is iterated across and a new Communication is generated for each. How do I render the contents of a partial view to a string to store in the database for delivery at midnight.
#communication = Communication.new(params[:communication])
#communication.body = //Render Partial with local variables to string here
January 8, 2010: I've implemented the solution proposed by neutrino, and my code looks like this:
#communication.message_body = render_to_string(
:partial => "letters/small_letter",
:locals => { :quote => #quote})
The drawback to this approach is that it requires a context so it must be rendered within a controller, this rules out generating the email using a rake file. As others have pointed out the approach of using a Communication model may introduce some unnecessary complexity. At this time since the conditions I use to generate the list of recipients is never stored I've opted to continue to use a communication model.
As (almost) always in Rails, there's a method for that: ActionController::Base#render_to_string
In Rails 3, this method was moved to AbstractController::Rendering#render_to_string.
You may want to look at techniques to render a template outside of a controller, such as:
http://geek.swombat.com/rails-rendering-templates-outside-of-a-contro
That being said, it's probably better to make use of the built-in email sending class ActionMailer::Base which can render templates for you. Storing the parameters in your Communication model is probably better than storing your unrolled HTML, as these parameters can be serialized and later pushed back into a "send_X_notification" type call.
These can be built using the generator:
script/generate mailer communications_mailer
you could try this (in the rails console):
class StaticRender < ActionController::Base
def self.render_erb(template_path, params)
view = ActionView::Base.new(ActionController::Base.view_paths, {})
class << view
include ApplicationHelper
end
view.render(:file => "#{template_path}.html.haml", :locals => params, :layout => false)
end
end
StaticRender.render_erb("home", {})
you need to have the view "home" for that....
Have you tried action mailer? It will get your views rendered and provide active record support also.