I have a rails code that sends emails. Following is in my controller:
def create
#users = Users.find(:all)
#sub = params[:sub]
#body = params[:body]
#index = 0
#users.each {|i| index++; Notifier.deliver_notification(#users.email_address, #sub, #body, #users.unsubscribe_link);}
flash[:notice] = "Mail was sent to " + #index + " people"
end
I have the following in my Model
class Notifier < ActionMailer::Base
def notification(email, sub, content, link)
recipients email
from "my_email#example.com"
subject sub
body :content => recipient, :link => link
end
end
This all works fine. My Question is:
For example if there is an error in sending mail to one of the pople, even then my flash message will say. Mail was sent to X people
What can I do to ensure that #index gets incremented ONLY when mail is successfully sent?
The deliver_notification method should always return a TMail object regardless of success or failure. There is a raise_delivery_errors setting which will allow the mailer to raise exceptions if there's trouble, but you'll have to rescue these in your block and only increment on success.
Due to the way mail is delivered by ActionMailer, it's often the case you won't know if the message is successful or not. Email is usually queued and delivered at a point in time well beyond your method call, and most errors occur at this point due to any number of difficulties in delivery. It's only wildly malformed email addresses that will be rejected up front, or if the mail delivery mechanism is non-functional.
Edit: Added Exception Tracking
count = 0
#users.each do |user|
begin
Notifier.deliver_notification(
user.email_address,
#sub,
#body,
user.unsubscribe_link
)
count += 1
rescue => e
# Something went wrong, should probably store these and examine them, or
# at the very least use Rails.logger
end
end
flash[:notice] = "Mail was sent to #{count} people"
Your example used index++ which is not supported by Ruby. What you probably want is index += 1. You were also using the #users array directly instead of the individual elements.
You could ask ActionMailer to throw exceptions for you, and then only count those deliveries that don't result in an exception.
ActionMailer::Base.raise_delivery_errors = true
#users.each do |i|
begin
Notifier.deliver_notification(#users.email_address, #sub, #body, #users.unsubscribe_link)
index++
rescue Exception => e
# Do whatever you want with the failed deliveries here
end
end
Related
I am currently working on a ticket where it asks me to filter out any inactive email to be sent to the recipient. Here is the method I am working on:
def self.delivering_email(message)
return if email_to_be_delivered?(message.subject)
email_list = message.to
if email_list.is_a?(String)
email_list = email_list.split(",").map(&:strip)
end
email_list.each { |email|
identity = Identity.find_by(email: email)
next if identity.nil?
# email_list.delete(email) unless identity.try(:preferred_user).active?
email_list.select(email) if identity.try(:preferred_user).active?
}
message.to = email_list
message.perform_deliveries = !email_list.empty?
end
the "# email_list.delete(email) unless identity.try(:preferred_user).active?" I commented out because the QA mentioned that ONLY one inactive email filters out and does not fully filter other inactive emails in the array. I assumed instead of .delete I have to use .select but don't know if it works because I don't have any way to test and reproduce the error on my end, or how to implement it the right way.
Any help will be appreciated.
You're trying to modify an array while you're iterating over it, that may lead to weird behavior. One option is to just use a separate array.
Since you are already iterating with email_list.each you can call next if the current email does not satisfy you, like you already do for identity.nil?.
So it may look smth like
valid_emails = []
email_list.each { |email|
identity = Identity.find_by(email: email)
next if identity.nil? || !identity.try(:preferred_user).active?
valid_emails << email
end
message.to = valid_emails
So, I wrote a program that sends a get request to HappyFox (a support ticket web app) and I get a JSON file, Tickets.json.
I also wrote methods that parse the JSON and return a hash with information that I want, i.e tickets with and without a response.
How do I integrate this with my Rails app? I want my HappyFox View (in rails) to show the output of those methods, and give the user the ability to refresh the info whenever they want.
Ruby Code:
require 'httparty'
def happy_fox_call()
auth = { :username => 'REDACTED',
:password => 'REDACTED' }
#tickets = HTTParty.get("http://avatarfleet.happyfox.com/api/1.1/json/tickets/?size=50&page=1",
:basic_auth => auth)
tickets = File.new("Tickets.json", "w")
tickets.puts #tickets
tickets.close
end
puts "Calling API, please wait..."
happy_fox_call()
puts "Complete!"
require 'json'
$data = File.read('/home/joe/API/Tickets.json')
$tickets = JSON.parse($data)
$users = $tickets["data"][3]["name"]
Count each status in ONE method
def count_each_status(*statuses)
status_counters = Hash.new(0)
$tickets["data"].each do |tix|
if statuses.include?(tix["status"]["name"])
#puts status_counters # this is cool! Run this
status_counters[tix["status"]["name"]] += 1
end
end
return status_counters
end
Count tickets with and without a response
def count_unresponded(tickets)
true_counter = 0
false_counter = 0
$tickets["data"].each do |tix|
if tix["unresponded"] == false
false_counter += 1
else true_counter += 1
end
end
puts "There are #{true_counter} tickets without a response"
puts "There are #{false_counter} ticket with a response"
end
Make a function that creates a count of tickets by user
def user_count(users)
user_count = Hash.new(0)
$tickets["data"].each do |users|
user_count[users["user"]["name"]] += 1
end
return user_count
end
puts count_each_status("Closed", "On Hold", "Open", "Unanswered",
"New", "Customer Review")
puts count_unresponded($data)
puts user_count($tickets)
Thank you in advance!
You could create a new module in your lib directory that handles the API call/JSON parsing and include that file in whatever controller you want to interact with it. From there it should be pretty intuitive to assign variables and dynamically display them as you wish.
https://www.benfranklinlabs.com/where-to-put-rails-modules/
I am writing a chat application, using Jabber, on Ruby on Rails. Sending messages was quite easy to implement, but receiving messages in a loop is quite a challenge.
I want to get callbacks from the server without browser refreshes. I tried to use ActionController::Live for this.
In my client side, my code is:
var source = new EventSource('/chat/events');
source.addEventListener('refresh', function(event)
{
AddMessage(event.data);
});
My controller code is:
def chat_events
# SSE expects the `text/event-stream` content type
response.headers['Content-Type'] = 'text/event-stream'
sse = Reloader::SSE.new(response.stream)
puts "Starting XMMP call"
begin
#xmmpClient = XmppClient.new
#xmmpClient.loginUsingNameAndPassword(****, ***)
#xmmpClient.listen_connections sse
rescue IOError
puts "Error"
# When the client disconnects, we'll get an IOError on write
ensure
puts "closing stream"
sse.close
end
end
And, my XMMP client is:
def listen_connections(writer = nil)
if not #logged_in
puts "Not logged"
raise "Not logged"
end
#robot.send(Jabber::Presence.new.set_show(nil))
loop do
puts "Listening callback"
#robot.add_message_callback do |message|
puts "Got message " + message.inspect
if not writer.nil?
writer.write({ :message => message.body }, :event => 'refresh')
end
end
sleep 2
end
end
What I got:
The chat_events method of the controller is called every second or so.
Because of this, several callbacks are executed at once.
When I got a message, I got it four or five times.
{"message":"fffdsfd"}
{"message":"fffdsfd"}
{"message":"fffdsfd"}
{"message":"fffdsfd"}
And the worst stuff - my server is not responding to other responses, although I use the Puma multi-threaded server.
What is the correct way to implement functionality like this?
I get the solution
puts "."
client.add_message_callback do |message|
if message.type != :error
arr = message.from.to_s.split('#')
puts arr[0]
puts message.body
end
end
while 1
end
I created a batch email system for my website. The problem I have, which is terrible, is it continuously sends out emails. It seems the job is stuck in an infinite loop. Please advise. It is crazy because on my development server only one email is sent per account, but on my production server I received 5 emails. Thus, meaning all users of my site received multiple emails.
Controller:
class BatchEmailsController < ApplicationController
before_filter :authenticate_admin_user!
def deliver
flash[:notice] = "Email Being Delivered"
Delayed::Job.enqueue(BatchEmailJob.new(params[:batch_email_id]), 3, 10.seconds.from_now, :queue => 'batch-email', :attempts => 0)
redirect_to admin_batch_emails_path
end
end
Job in the lib folder:
class BatchEmailJob < Struct.new(:batch_email_id)
def perform
be = BatchEmail.find(batch_email_id)
if be.to.eql?("Contractors")
cs = Contractor.all
cs.each do|c|
begin
BatchEmailMailer.batch_email(be.subject, be.message, be.link_name, be.link_path, be.to, c.id).deliver
rescue Exception => e
Rails.logger.warn "Batch Email Error: #{e.message}"
end
else
ps = Painter.all
ps.each do |p|
begin
BatchEmailMailer.batch_email(be.subject, be.message, be.link_name, be.link_path, be.to, p.id).deliver
rescue Exception => e
Rails.logger.warn "Batch Email Error: #{e.message}"
end
end
end
end
end
Delayed Job Initializer:
Delayed::Worker.max_attempts = 0
Please provide feedback on this approach. I want to send out the batch email to all users, but avoid retrying multiple times if something goes wrong. I added rescue block to catch email exceptions in hope that the batch will skip errors and continue processing. As a last resort do not run again if something else goes wrong.
What one of my apps does which seems to work flawlessly after millions of emails:
1) in an initializer, do NOT let DelayedJob re-attempt a failed job AND ALSO not let DJ delete failed jobs:
Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.max_attempts = 1
2) Scheduling a mass email is 1 job, aka the "master job"
3) When THAT jobs runs, it spawns N jobs where N is the number of emails being sent. So each email gets its own job. (Note: if you use a production email service with 'batch' capability, one "email" might actually be a batch of 100 or 1000 emails.)
4) We have an admin panel that shows us if we have any failed jobs, and if they are, because we don't delete them, we can inspect the failed job and see what happened (malformed email address etc)
If one email fails, the others are un-affected. And no email can ever be sent twice.
I'm using the Mailman gem to process incoming email for my Rails app. My application looks for a YAML document in the plain-text email and then loads it into a Ruby object for further manipulation by the app.
However, I want to be able to plan ahead for email clients that might respond with a multi-part email. I need to get the plain-text part of the email and pass it into the YAML parser.
For some reason, it's still having problems parsing the YAML. I'm guessing because it's not really getting the plain text part here.
Is there a better way to get the text/plain part of an email with Mailman? Should I scrap Mailman and just get down and dirty with ActionMailer instead?
Mailman::Application.run do
default do
begin
message.parts.each do |part|
Mailman.logger.info part.content_type
if part.content_type == 'text/plain; charset=ISO-8859-1' # My poor way of getting the text part
the_yaml = part.body.decoded.scan(/(\-\-\-.*\.\.\.)/m).first.last # Find the YAML doc in the email and assign it to the_yaml
ruby_obj = YAML::load(the_yaml.sub(">", "")) # Remove any >'s automatically added by email clients
if ruby_obj['Jackpots']
ruby_obj['Jackpots'].each do |jackpot|
jp = Jackpot.find(jackpot['jackpot']['id'])
jp.prize = jackpot['jackpot']['prize']
jp.save
end
end
end
end
rescue Exception => e
Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
Mailman.logger.error [e, *e.backtrace].join("\n")
end
end
end
I was able to find a little bit better way to handle getting the text part of the email.
Mailman::Application.run do
default do
begin
if message.multipart?
the_message = message.text_part.body.decoded
else
the_message = message.body.decoded
end
the_yaml = the_message.sub(">", "").scan(/(\-\-\-.*\.\.\.)/m).first.last
ruby_obj = YAML::load(the_yaml)
if ruby_obj['Jackpots']
ruby_obj['Jackpots'].each do |jackpot|
jp = Jackpot.find(jackpot['jackpot']['id'])
jp.prize = jackpot['jackpot']['prize']
jp.save
end
end
rescue Exception => e
Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
Mailman.logger.error [e, *e.backtrace].join("\n")
end
end
end
And then after running it through the debugger and inspecting after the text part was successfully parsed. It would get hung up on the YAML loading. Turns out, a couple of my lines were too long, to the email client inserted a newline, breaking a comment in my YAML, and thus breaking the whole YAML document.