I have a table of emails. And i need that Each user received email.
SO i made:
script/generate mailer Notifier
Next.
class Notifier < ActionMailer::Base
def newgrants_notification(respondent)
recipients user.email
from "lala#lala.com"
subject "Hi!"
body (:respondent => respondent)
end
end
In app/views/notifier/newgrants_notification.erb
wrote : Hello!
and this my controller where i create question
#question = Question.create(:text => params[:question][:text], :security => rand(888).to_i)
if success = #question.save
respondents = Respondent.find(:all)
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(respondents).deliver #this is right??
end
what mistakes i did? messages aren't coming ;(
HI
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(res).deliver
end
When you are sending mail,the mail id is passed as a parameter so respondents is replaced with res.
You are passing through your array of respondents, when you use each, the variable in the pipes (|res|) is the one to use to refer to the singular object in the loop.
Notifier.newgrants_notification(res).deliver
In development mode all email are not send. It's all log only in your log file. So if you test on this environment is normal. Check on your log if you see it :)
Related
I have a Rails API which will receive requests that contain email details (to, subject, from etc) in the request of the body.
Since these emails are sent to customers, I set up the route, controller action and mailer as follows:
routes.rb
resource :customers do
member do
post 'send_checklist'
end
end
customers_controller.rb
def send_checklist
CustomerMailer.send_checklist(customer_params[:email]).deliver_now
end
customer_mailer.rb
class CustomerMailer < ApplicationMailer
def send_checklist(data)
mail(
to: data.to,
from: 'support#abc.com',
subject: data[:subject],
body: data[:body],
content_type: 'text/html'
)
end
end
json structure in body of post request
"customer": {
"mail": {
"to":"test#test.com, test1#test.com"
"subject":"Test message"
"body":"This is the email message"
}
}
This works, however I cannot validate that the 'to' property has at least one email address and that each email address is a valid email.
How can I validate the 'to' property and return a 422 unprocessible entity error from the controller action?
So in psuedo-code the action would be something like this:
def send_checklist
if data_is_valid
send_email
respond with 200 ok (or 204 No Content?)
else
respond with 422
end
end
I am thinking that somehow this validation logic should go into the CustomerMailer class itself, or maybe I should create a Mail model class with attr_accessors (to, subject, body) with validators? Not sure what is the correct solution, hence the question.
EDIT - proposed solution, which fails
Based on the first two proposed answers, I wrote this code:
customers_controller.rb
def send_checklist
from_user = "#{current_api_user.name} <#{current_api_user.email}>"
if CustomerMailer.emails_valid?(customer_params[:mail][:to])
CustomerMailer.send_checklist(from_user, customer_params[:mail]).deliver_now
render json: nil, status: :ok
else
render json: ErrorSerializer.serialize('Invalid email address found'), status: :unprocessable_entity
end
end
customer_mailer.rb
def emails_valid?(emails_list)
Rails.logger.debug "*** HERE ***" + emails_list
emails = emails_list.split(/\s+,\s+/)
Rails.logger.debug emails
is_valid = true
emails.each do |email|
Rails.logger.debug email
is_valid = false unless email=~ /([^\s]+)#([^\s]+)/
end
return is_valid
end
The emails_valid? is not called by the controller - nothing appears in the log from that method. It seems the controller does not call it and the if statement always returns true.
On further investigation, it seems I cannot put the emails_valid? method in the mailer class because of the way ActionMailer works as explained here.
EDIT - clarification
Just in case it is not clear, this is not a normal mail sending flow that you see in a Rails application:
This is an API, there are no rails forms.
The client sends a post request with the JSON shown above - the "To" field contains a string of comma-separated email addresses.
There is NO model here. None of the data sent in the request is stored in the database, it simply used to construct and send an email.
The only 'relationship' involved is that the post request is sent to customers/id - this is so later I can log that an action (send_checklist) was taken for this customer.
If you've a model to store email then you could check the param by querying the model
emails_arr = params[:to].split(",").map(&:strip)
This will give you an array of email from the params. Then you could use query to check whether the DB has the email present in the array. Assuming you've a Customer model you could do
Customer.where(email: email_arr).pluck(:email).join(",")
Pass this to the mailer's to param.
If you don't have a Customer model to query then you could use simple regex like below to check the validity of the email.
/\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
Ruby Regex docs for your reference
Assuming these users aren't existing users in your system this is how I would approach it.
in your Controller:
def send_checklist
from_user = "#{current_api_user.name} <#{current_api_user.email}>"
render json: ErrorSerializer.serialize('Invalid email address found'), status: :unprocessable_entity unless email_valid?(customer_params[:mail][:to])
CustomerMailer.send_checklist(from_user, customer_params[:mail]).deliver_now
render json: nil, status: :ok
end
private
def email_valid?(emails_list)
emails = emails_list.split(/\s+,\s+/)
true unless emails.any? {|email| email=!~ /([^\s]+)#([^\s]+)/ }
end
Or something to that effect. I am also not sure why you would need to validate multiple email addresses based on your code but I noticed your solution was taking a list so I made mine take a list as well. Your use case is a bit uncommon I think because most of the time the email is validated on saving to the db then the mail is sent after that but you don't have that step. You may also need to cast customer_params[:mail][:to] to an array if it is not coming over as such.
I would like to send an email to a user once the status of their pdform is updated. I already have some stuff written out on how I want this done.
In my pdform.rb model
after_update :sendemail
def sendemail
if status_changed?
end
end
I already have emails being sent out when the user creates a new form, however, I am not sure how to send an email in the model.
The controller has this mailer function that works correctly. How could I send this in this model?
NewPdform.notify_user(current_user, #pdform).deliver
Any help would be greatly appreciated. Still getting the hang of ActiveRecord.
Update:
In my pdforms_controller update method I have added the following variable.
update_user = #pdform.user
I added an attr_accessor in pdform.rb (the model)
attr_accessor :update_user
after_update :sendemail
def sendemail
NewPdform.notify_update(update_user).deliver
end
And in my mailer
def notify_update(user)
#user = user
mail(to: #user.email, subject: "some change occured")
end
I solved my own issue after using my brain more extensively.
In the call to the mailer function instead of passing the parameter of pdform, which is the name of the class anyways, just pass self.
def sendemail
NewPdform.notify_update(self).deliver
end
I've got an app where users submit weeks which can be approved or denied, and in my weeks controller I have the following lines meant to iterate over the selected weeks, find their corresponding users and send each user an email:
elsif params[:commit] == "Reject selected weeks"
user_week = Week.where(id: params[:weeks_ids])
user_week.update_all(approved?: false)
# fetch the set of user_emails by converting the user_weeks to user_ids
users = User.find(user_week.pluck(:user_id))
users.each do |user|
#iterate over the users and send each one an email
UserMailer.send_rejection(user).deliver
end
flash[:info] = "Selected weeks were Rejected."
end
redirect_to weeks_path
When I attempt to reject a week, I receive the following error message:
undefined method `send_rejection' for UserMailer:Class
I'm adding on to pre-existing code and have little knowledge of MVC, so the only issues I can think of would be with placing the mailer method in the wrong file or sending an incorrect type of arg to the mailer method.
Here is "send_rejection", the mailer contained in my user model.
def send_rejection(user)
UserMailer.reject_timesheet(user).deliver_now
end
The corresponding method in my user_mailer.rb file:
def reject_timesheet(user)
#greeting = "Hi"
mail to: user.email, subject: "Rejected Timesheet"
end
New to rails and not sure where I'm going wrong.
This is not a problem of MVC, one question I'd probably ask is why are you not calling the reject_timesheet directly instead of send_rejection.
You're getting the error because as you said the method is defined in the user model, so in order to call the method, you'd need to do:
user.send_rejection
In which case I doubt you'd be needing to pass a user argument to the send_rejection, as you could just do:
class User
def send_rejection
UserMailer.reject_timesheet(self).deliver_now
end
end
then in your controller:
...
users.each do |user|
#iterate over the users and send each one an email
user.send_rejection
end
...
I believe you could also clean up your codebase a bit and possibly refactor some logic, but basically this approach should resolve your errors.
Let me know if that helps
I'm wondering how I could conditionally abandon sending mail within the action ActionMailer action itself.
class SomeMailer < ActionMailer::Base
...
def some_emails
some_models = Model.where(:a => 1)
if !some_models.blank?
mail(...)
else
# What to add here?
# render :nothing => true doesn't work
end
end
end
Now invoking this through SomeMailer.some_emails.deliver! returns an
ArgumentError: A sender (Return-Path, Sender or From) required to send a message
Set perform_deliveries to false, like so:
emails = get_email_list_somehow
if emails.present?
mail options.merge(:bcc => emails)
else
self.message.perform_deliveries = false
end
This will quietly not try to send and should stop the error from happening.
In Rails 3.2.9 you can finally conditionally call mail(). Here's the related GitHub thread. Now the OP's code can be reworked like this:
class SomeMailer < ActionMailer::Base
...
def some_emails
some_models = Model.where(:a => 1)
unless some_models.blank?
mail(...)
end
end
end
The strange thing is, that with Rails 3.1.rc4 and WEBrick, it works fine on my local WEBrick webserver. But as soon as I push to Heroku cedar stack, their WEBrick throws the
ArgumentError: A sender (Return-Path, Sender or From)
You have to remove the conditional statements as stated in above answer. That fixes it so that it also works on Heroku, not just your local machine
I had this same problem. There is no real way to do it within the ActionMailer action so I did the following in my cron task:
users.each do |user|
begin
UserMailer.event_second_reminder_group_user_email(user).deliver
puts " - sending reminder email to user #{user.email}"
rescue
end
end
puts "Complete!"
Now if an error is thrown, it doesn't break the app!
Instead put your conditions in the place where you are making the call to SomeMailer.some_emails.deliver!
Currently in my tests I do something like this to test if an email is queued to be sent
assert_difference('ActionMailer::Base.deliveries.size', 1) do
get :create_from_spreedly, {:user_id => #logged_in_user.id}
end
but if i a controller action can send two different emails i.e. one to the user if sign up goes fine or a notification to admin if something went wrong - how can i test which one actually got sent. The code above would pass regardless.
As of rails 3 ActionMailer::Base.deliveries is an array of Mail::Message's. From the mail documentation:
# mail['from'] = 'mikel#test.lindsaar.net'
# mail[:to] = 'you#test.lindsaar.net'
# mail.subject 'This is a test email'
# mail.body = 'This is a body'
#
# mail.to_s #=> "From: mikel#test.lindsaar.net\r\nTo: you#...
From that it should be easy to test your mail's in an integration
mail = ActionMailer::Base.deliveries.last
assert_equal 'mikel#test.lindsaar.net', mail['from'].to_s
assert_equal 'you#test.lindsaar.net', mail['to'].to_s
When using the ActionMailer during tests, all mails are put in a big array called deliveries. What you basically are doing (and is sufficient mostly) is checking if emails are present in the array.
But if you want to specifically check for a certain email, you have to know what is actually stored in the array. Luckily the emails themselves are stored, thus you are able to iterate through the array and check each email.
See ActionMailer::Base to see what configuration methods are available, which you can use to determine what emails are present in the array. Some of the most suitable methods for your case probably are
recipients: Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the To: header.
subject: The subject of your email. Sets the Subject: header.
Using current Rspec syntax, I ended up using the following:
last_email = ActionMailer::Base.deliveries.last
expect(last_email.to).to eq ['test#example.com']
expect(last_email.subject).to have_content 'Welcome'
The context of my test was a feature spec where I wanted to make sure a welcome email was sent to a user after signing up.
As of 2020 (Rails 6 era, probably introduced earlier) you can do the following:
(using a SystemTest example) TL;DR: use assert_emails from ActionMailer::TestHelper and ActionMailer::Base.deliveries.last to access the mail itself.
require "application_system_test_case"
require 'test_helper'
require 'action_mailer/test_helper'
class ContactTest < ApplicationSystemTestCase
include ActionMailer::TestHelper
test "Send mail via contact form on landing page" do
visit root_url
fill_in "Message", with: 'message text'
# Asserting a mail is sent
assert_emails 1 do
click_on "Send"
end
# Asserting stuff within that mail
last_email = ActionMailer::Base.deliveries.last
assert_equal ['whatever'], last_email.reply_to
assert_equal "contact", last_email.subject
assert_match /Mail from someone/, last_email.body.to_s
end
end
Official doc:
ActionMailer Guide/Testing
Testing Guide/ActionMailer
Note
Instead of manually checking the content of the mail as in the system test above, you can also test whether a specific mailer action was used, like this:
assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
And some other handy assertion, see https://api.rubyonrails.org/v6.0.3.2/classes/ActionMailer/TestHelper.html#method-i-assert_emails .
The test framework shoulda has an excellent helper which lets you assert certain conditions about an email that was sent. Yes, you could do it yourself with ActionMailer.deliveries, but shoulda makes it all one neat little block
A little late, but it may help others:
You could use Email-spec, a collection of Rspec/Minitest matchers and Cucumber steps.
Here is the best way I've found to do it.
1) Include the action mailer callbacks plugin like this:
script/plugin install git://github.com/AnthonyCaliendo/action_mailer_callbacks.git
I don't really use the plugin's main features, but it does provide the nice functionality of being able to figure out which method was used to send an email.
2) Now you can put some methods in your test_helper.rb like this:
def assert_sent(method_name)
assert sent_num_times(method_name) > 0
end
def assert_not_sent(method_name)
assert sent_num_times(method_name) == 0
end
def assert_sent_once(method_name)
assert sent_num_times(method_name) == 1
end
def sent_num_times(method_name)
count = 0
#emails.each do |email|
count += 1 if method_name == email.instance_variable_get("#method_name")
end
count
end
3) Now you can write sweet tests like this:
require 'test_helper'
class MailingTest < ActionController::IntegrationTest
def setup
#emails = ActionMailer::Base.deliveries
#emails.clear
end
test "should send a mailing" do
assert_difference "Mailing.count", 1 do
feeds(:feed1).generate_mailing
end
assert_sent_once "broadcast"
assert_not_sent "failed_mailing"
end
end
Here "broadcast" and "mailing_failed" are the names of the methods in my ActionMailer::Base class. These are the ones you normally use by calling Mailer.deliver_broadcast(some_data) or Mailer.deliver_failed_mailing(some_data) etc. That's it!