I'm trying to send multiple emails based on a boolean value in my database. The app is a simple scheduling app and user can mark their shift as "replacement_needed" and this should send out emails to all the users who've requested to receive these emails. Trouble is, it only every seems to send to one email. Here's my current code:
def request_replacement(shift)
#shift = shift
#user = shift.user
#recipients = User.where(:replacement_emails => true).all
#url = root_url
#recipients.each do |r|
#name = r.fname
mail(:to => r.email,
:subject => "A replacement clerk has been requested")
end
end
In the Rails guides (Action Mailer Basics) it says the following regarding multiple emails:
The list of emails can be an array of email addresses or a single string with the addresses separated by commas.
So both "test1#gmail.com, test1#gmail.com" and ["test1#gmail.com", "test1#gmail.com"] should work.
See more at: http://guides.rubyonrails.org/action_mailer_basics.html
You can just send one email for multiple recipients like this.
def request_replacement(shift)
#shift = shift
#user = shift.user
#recipients = User.where(:replacement_emails => true)
#url = root_url
emails = #recipients.collect(&:email).join(",")
mail(:to => emails, :subject => "A replacement clerk has been requested")
end
This will take all your #recipients email addresses and join them with ,. I think you can also pass an array to the :to key but not sure.
The only problem is you won't be able to use #name in your template. :(
I have the same problem. I don't know what is the deal is. I sidestep it by:
instead of calling
Mailer.request_replacement(shift).deliver
from my controller,
I'd define a class method on the mailer, and call that. That method would then iterate through the list and call deliver "n" times. That seems to work:
class Mailer
def self.send_replacement_request(shift)
#recipients = ...
#recipients.each do |recipient|
request_replacement(recipient, shift).deliver
end
end
def request_replacement(recipient, shift)
...
mail(...)
end
end
and from the controller, call
Mailer.send_replacement_request(shift)
To prevent each recipient from seeing the other email addresses:
#recipients.each{ |recipient| Mailer.request_replacement(recipient, shift).deliver }
I'm using Rails 5 and I have the same situation, the email was sent only to the last recipient but also it was sent just as plain text not as HTML email.
After trying some advices, I ended up fixing it in this way:
The mailer:
class BrochureMailer < ApplicationMailer
default from: "info#site.com"
def newsletter(sponsor, brochures_list)
#sponsor = sponsor
#brochures = brochures_list
mail(
to: #sponsor.email,
subject: "Interesting subject!"
)
end
end
The controller where the mailer is invoked:
class Admin::DashboardController < Admin::BaseController
def send_newsletter
sponsors = params[:sponsor_ids]
brochures = params[:brochure_ids]
sponsors = Sponsor.where(id: sponsors)
brochures = Brochure.where(id: brochures).to_a
# Send Newsletter email to the given Sponsors
sponsors.each do |sponsor|
BrochureMailer.newsletter(sponsor, brochures).deliver_later
end
redirect_back(fallback_location: admin_root_path, success: 'Newsletter sent!')
end
end
And in the view, something like this:
<% #brochures.each do |brochure| %>
<table width="280" border="0" cellpadding="0" cellspacing="0" align="left" valign="top" class="floater">
<tr>
<td align="center" valign="top">
<a target="_blank" href="<%= brochure_url(brochure) %>">
<img border="0" vspace="0" hspace="0" src="<%= brochure.image.blank? ? 'default.png' : brochure.image.url(public: true) %>" width="250" height="142">
<b><%= brochure.title %></b>
</a>
<br>
<%= brochure.description.truncate(60) %>
</td>
</tr>
</table>
<% end %>
And it works like a charm!
I'm not sure if this is the correct way or the most optimal way to go but just consider it as a second possibility.
I hope it could be useful for somebody else.
Related
I am working with a nested model a question has multiple answers and only one can be marked as correct how can i validate to check that only one question was marked as correct. The correct is a boolean field.
#question model
validate :one_correct_answers
def one_correct_answers
if self.choices.correct_choices > 1
errors.add(:base, "please select only one correct answer")
end
end
In Question model
class Question
has_many :choices
accepts_nested_attributes_for :choices, :reject_if => ->(choice){ choice[:value].blank? }
validate :only_one_correct_answer
private
def only_one_correct_answer
unless (choices.select{ |choice| choice.correct }.size == 1)
errors.add(:choices, "You must provide only 1 correct answer")
end
end
end
In Form HTML
<input name="question[choices_attributes][0][correct]" type="checkbox">
<input name="question[choices_attributes][1][correct]" type="checkbox">
<input name="question[choices_attributes][2][correct]" type="checkbox">
<input name="question[choices_attributes][3][correct]" type="checkbox"> ... till n
and in QuestionsController
#question = Question.new(params[:question])
#question.valid? => will automatically call Question#only_one_correct_answer and add errors,if any.
I hope, this will help you. :)
First, your checkboxes should look like this:
<input id="something_" name="something[]" type="checkbox" value="<%= some_id %>">
This way, when you submit the form, the params should look like and array of the checked checkboxes:
params: something => [1, 2]
Then, on your controller you set the variable correct_choices to this array, and validate with your custom validator.
How do you delete multiple records using checkboxes in Rails 3?
routes.rb:
resources :blog_posts do
collection do
delete 'destroy_multiple'
end
end
index.html.erb:
<%= form_tag destroy_multiple_blog_posts_path, method: :delete do %>
<table>
...
<td><%= check_box_tag "blog_posts[]", blog_post.id %></td>
...
</table>
<%= submit_tag "Delete selected" %>
<% end %>
blog_posts_controller.rb:
def destroy_multiple
BlogPost.destroy(params[:blog_posts])
respond_to do |format|
format.html { redirect_to blog_posts_path }
format.json { head :no_content }
end
end
Assuming you want to display a list of records in a table, each with a check box, and have a delete button that will cause all checked records to be deleted.
First, you have to create names for the checkboxes that contain the record id, you could do this:
<%= check_box_tag("delete[#{#thing.id}]",1) %>
That will create HTML that will include the following
<input id='delete[1000]' type='checkbox' value='1' name='delete[1000]'>
So when you post a form, if you've checked the box for the records with id's 1001 and 1002, your post will contain:
"delete[1001]"=>"1"
"delete[1002]"=>"1"
So inside your controller, you could do this
params[:delete].each do |id|
Thing.find(id.to_i).destroy
end
Send the ids of all checked elements on controller. I am assuming u have send ids to be deleted for Foo class to be deleted
ids = params[:ids]
Foo.where("id in (#{ids}")).destroy
or
ids = params[:ids].split(",")
Foo.where(id => ids).destroy
Use destroy, don't use delete if you have any dependencies.
I have a part file where I want to show all my products for a specific partner.
Here's the html.erb:
<% unless #products.nil? %>
<% #products.each do |prod|%>
<tr id="p_<%= prod.id%>">
<td><%= prod.name %></td>
<td><%= prod.price %></td>
<td><%= number_field_tag "product_qty_input[#{prod.id}]", get_offer_product_qty(#offer.id, prod.id),:min => 0, :max => 99 %></td>
<td>X</td>
</tr>
<% end %>
<% end %>
But I keep getting the error: undefined method `each' for "2,1":String
it says it's on this line: <% #products.each do |prod|%>
But I don't see the problem..
Here's my controller:
def select_products
#partner = Partner.find(params[:partner_id])
if params[:id] == "-1"
#offer = nil
else
#offer = Offer.find(params[:id])
end
#select_callback = url_for( #offer.nil? ? new_partner_offer_path(#partner) : [:edit, #partner, #offer] )
#products = #partner.active_products
#num_select = PRODS_PER_OFFER
respond_to do |format|
format.html { render :template => "products/select"}
end
end
You see I have #products = #partner.active_products method, but i changed that to see if it got there, and it's not, must be something before...
I would apreciate any help.
Thanks in advance
UPDATE
Here is the method active_products :
def active_products
self.products.where("active IS NOT NULL AND active = true")
end
It should be returning the object not string
What am I doing wrong?
Thanks #thesis
UPDATE 2
Guys, Thanks a lot for the help, and with the assistence of #thesis I figured it out.
It was actualy something that I haven't thought about before, and it wasn't describe here in the question.
My sessionkeeper helped me keep my form as I selected products in another page!
That was meesing it up!
Your problem is very simple. You have to fix your active_products method, as it returns String. In your case, string is "2,1" but you have to return collection of products, to iterate it with each method.
For additional help, please update your question and add active_products instance method from Partner model.
Do you really have products as numbers "2,1".
#partner.active_products should ideally return ActiveRecord::Relation, hence it should iterate objects of Product class.
Please go through http://guides.rubyonrails.org/
And before that, you may like to search for 'ruby philosophy' and 'rails philosophy'
I know how to pass parameters the dumb way. For example,
<%= link_to "Order", new_order_item_path(:item_id => #item.id) %>
The OrderItemsController receives it as params[:item_id] = id.
Problem:
#order_item = OrderItem.new(params)
raises an exception (Can't mass-assign protected attributes: action, controller). I can get around this with the following code.
#order_item = OrderItem.new
#order_item.item_id = params[:item_id]
I know the controller requires params[:order_item][:item_id] for new to work the first way. My question is, how do I get new_order_item_path to generate url? I know this isn't a major problem, but it just bugs me that I don't know the cleaner/proper way to do this. I have tried searching, but only received unrelated questions/answers/results.
Thanks
You didn't really specify if you didn't want to use it or not, but in your model, you could make the attribute item_id accessible like so:
class OrderItem < ActiveRecord::Base
attr_accessible :item_id
...
In this way,
#order_item = OrderItem.new(params)
would work.
Hope this helps.
How about this:
# Controller
def get_item_edit_method
#order = OrderItem.find("your criteria")
##order = OrderItem.new # if new
#item = Item.new()
end
def post_item_edit_method
#order = OrderItem.new(params) # should work now
#order.save
end
# End controller
<!-- view -->
<% #order.item = #item %>
<%= link_to "Order", new_order_item_path(#order) %>
<!-- end view -->
I have a HABTM-relation between the models "Snippets" and "Tags". Currently, when i save a snippet with a few tags, every tag is saved as a new record.
Now i want to check if a tag with the same name already exists and if that´s the case, i don´t want a new record, only an entry in snippets_tags to the existing record.
How can i do this?
snippet.rb:
class Snippet < ActiveRecord::Base
accepts_nested_attributes_for :tags, :allow_destroy => true, :reject_if => lambda { |a| a.values.all?(&:blank?) }
...
end
_snippet.html.erb:
<% f.fields_for :tags do |tag_form| %>
<span class="fields">
<%= tag_form.text_field :name, :class => 'tag' %>
<%= tag_form.hidden_field :_destroy %>
</span>
<% end %>
Ok, i´m impatient… after a while i found a solution that works for me. I don´t know if this is the best way, but i want to show it though.
I had to modify the solution of Ryan Bates Railscast "Auto-Complete Association", which handles a belongs_to-association to get it working with HABTM.
In my snippet-form is a new text field named tag_names, which expects a comma-separated list of tags.
Like Ryan, i use a virtual attribute to get and set the tags. I think the rest is self-explanatory, so here´s the code.
View "_snippet.html.erb"
<div class="float tags">
<%= f.label :tag_names, "Tags" %>
<%= f.text_field :tag_names %>
</div>
Model "snippet.rb":
def tag_names
# Get all related Tags as comma-separated list
tag_list = []
tags.each do |tag|
tag_list << tag.name
end
tag_list.join(', ')
end
def tag_names=(names)
# Delete tag-relations
self.tags.delete_all
# Split comma-separated list
names = names.split(', ')
# Run through each tag
names.each do |name|
tag = Tag.find_by_name(name)
if tag
# If the tag already exists, create only join-model
self.tags << tag
else
# New tag, save it and create join-model
tag = self.tags.new(:name => name)
if tag.save
self.tags << tag
end
end
end
end
This is just the basic code, not very well tested and in need of improvement, but it seemingly works and i´m happy to have a solution!