Beginner rails error I'm trying to send emails out to ALL current users when an article is updated.
I have sendgrid and devise set up with my app and am able to get the mailer to work through rails console. But, for some reason, I receive an undefined method email for #<User::ActiveRecord_Relation:0x007f8685aebec0> when updating an article.
ArticleNotificationMailer
class ArticleNotificationMailer < ApplicationMailer
default from: 'you#example.com'
def new_article(user, article)
#user = user
#article = article
mail(
to: #user.email,
subject: "New update to article #{article.title}"
)
end
end
new_article.html.erb
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-type" />
</head>
<body>
<h1>New article on website "<%= #article.title %>"</h1>
<p>
<%= #article.body %>
</p>
<p>
<%= link_to "View Comment on site", article_url(#article, anchor: "updates=#{#article.id}") %>
</p>
</body>
</html>
ArticleController
I'm using ArticleNotificationMailer.new_article(#user, #article).deliver
def update
respond_to do |format|
if #article.update(article_params)
ArticleNotificationMailer.new_article(#user, #article).deliver
format.html { redirect_to #article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: #article }
else
format.html { render :edit }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
Error Message
NoMethodError in ArticlesController#update
undefined method `email' for #<User::ActiveRecord_Relation:0x007f8685aebec0>
mail(
to: #user.email,
subject: "New post to articles #{article.title}"
)
end
Rails Console
>> u = User.last
>> a = Article.first
>> ActionNotificationMailer.new_article(u, a).deliver_now
ArticleNotificationMailer.new_article(#user, #article).deliver
Seems like #user was initialized by User.where() in your controller. User.where returns an instance of User::ActiveRecord_Relation which is in fact rails-enhanced array. And errors comes up when you are trying to call email on this array.
Just use User.find if you need to find only one record.
Try passing in the id of the elements.
class ArticleNotificationMailer < ApplicationMailer
default from: 'you#example.com'
def new_article(user_id, article_id)
#user = User.where(id: user_id)
#article = Article.where(id: article_id)
mail(
to: #user.email,
subject: "New update to article #{article.title}"
)
end
end
In your console
>> u = User.last
>> a = Article.first
>> ActionNotificationMailer.new_article(u.id, a.id).deliver_now
I figured out how to fix this.
I added the code below to article.rb and added #article.send_notifications! to my update controller.
def send_notifications!
user = User.all
user.each do |user|
ArticleNotificationMailer.new_article(user, self).deliver_now
end
end
Related
Running
Rails 4.1.8 |
Ruby 2.1.5p273
I'm trying to display the current username for notes that are created from current user using:
<%= "#{note.user.first_name.capitalize} #{note.user.last_name.capitalize[0]}" %> in show.html.erb.
<% #notes.each do |note| %>
<tr>
<td>
<h4>
<%= "#{note.user.first_name.capitalize} #{note.user.last_name.capitalize[0]}" %>
</h4>
<p><%= note.created_at.strftime("%-m/%-d/%y") %></p>
</td>
<td>
<p><%= h(note.comment).gsub(/\n/, '<br/>').html_safe %></p>
If I take <%= "#{note.user.first_name.capitalize} #{note.user.last_name.capitalize[0]}" %> the app works just fine.
I also checked the NotesController for note as well and I can't seem to find the issue.
class NotesController < ApplicationController
before_action :set_note, only: [:edit, :update, :destroy]
before_action :set_account
before_action :authenticate_user!
before_action :check_user, only: [:edit, :update, :destroy]
# GET /notes/new
def new
#note = Note.new
end
# GET /notes/1/edit
def edit
end
# POST /notes
# POST /notes.json
def create
#note = Note.new(note_params)
#note.user_id = current_user.id
#note.account_id = #account.id
respond_to do |format|
if #note.save
format.html { redirect_to #account, notice: 'note was successfully created.' }
format.json { render :show, status: :created, location: #note }
else
format.html { render :new }
format.json { render json: #note.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /notes/1
# PATCH/PUT /notes/1.json
def update
respond_to do |format|
if #note.update(note_params)
format.html { redirect_to account_path(#account), notice: 'note was successfully updated.' }
format.json { render :show, status: :ok, location: #note }
else
format.html { render :edit }
format.json { render json: #note.errors, status: :unprocessable_entity }
end
end
end
# DELETE /notes/1
# DELETE /notes/1.json
def destroy
#note.destroy
respond_to do |format|
format.html { redirect_to account_path(#account), notice: 'note was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_note
#note = note.find(params[:id])
end
def set_account
#account = Account.find(params[:account_id])
end
def check_user
unless (#note.user == current_user) || (current_user.admin?)
redirect_to root_url, alert: "Sorry, this note belongs to someone else"
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def note_params
params.require(:note).permit(:comment)
end
end
I'm sure it's something small I'm missing, I just can't see it.
You have a note that does not have a user associated with it, or one of the note users does not have first_name or a last_name (we would had known if you had posted the error message you are getting). If you want to guard against this, you have to make sure the user, first_name and last_name are not nil before calling methods on them:
In model:
def display_user_name
if user.nil?
"No user"
else
"#{user.first_name.present? ? user.first_name : '<missing>'} "\
"#{user.last_name.present? ? user.last_name.capitalize[0] : '<missing>'}"
end
end
In template:
<%= note.display_user_name %>
OR, you can use the great andand gem, then in your template you will have:
"#{note.user.andand.first_name.andand.capitalize} #{note.user.andand.last_name.andand.capitalize[0]}"
Just note that it will return " " (a string with a space in it) if the note has not user or has no first name and no last name.
Try this:
in Note class app/models/note.rb:
def user_name
#user_name ||= user.present? ? "#{user.first_name.capitalize} #{user.last_name.capitalize[0]}" : "No user" # or Anonymous(whichever suites your requirement)
end
then in your show.html.erb:
<%= note.user_name %>
Here:
You have one place to edit for any future references of user name in your application.
You do not repeat yourself(DRY) when you need to show user name of a note.
Easy to test.
I figured out the issue. I had serveral "notes" that had a few "null" values in the database which which is why it couldn't associated with the current_user.
After deleting those notes with null values, the problem was corrected.
i have a rails model registrations that has the following fields
attr_accessible :address, :company, :name, :phone, :email
i have successfully been able to send a mail to the user via the email fielded in by the user and that works succesfully using action mailer
def create
#registration = Registration.new(params[:registration])
respond_to do |format|
if #registration.save
UserMailer.registration_confirmation(#registration).deliver
format.html { redirect_to root_path, notice:" Thanks! #{#registration.name}, Your registration have been
confirmed & your seat reserved" }
format.json { render :show, status: :created, location: #registration }
else
format.html { render action: "new" }
format.json { render json: #registration.errors, status: :unprocessable_entity }
end
end
end
and the registration mailer is as thus
def registration_confirmation(registration)
#registration = registration
#greeting = "Hi"
mail(to: #registration.email, subject: 'Welcome')
end
which works very well...
All i want to achieve is to be able to send a mail to another email address e.g (admin#gmail.com) stating that a user as registered and also showing the registration details ... thanks
I would generate a new mailer specifically for notifications that should be sent to the administrator.
rails g mailer AdminMailer registration_notice
You could then edit the AdminMailer registration_notice to be similar to your UserMailer, but with a different recipient:
def registration_notice(registration)
#registration = registration
mail(to: 'admin#gmail.com', subject: 'A new user has registered')
end
Put whatever registration details you would like to include into the views for registration_notice.html.erb (or text).
Then just add the call to the mailer in the create action of the controller, right after the call to the UserMailer:
def create
#registration = Registration.new(params[:registration])
respond_to do |format|
if #registration.save
UserMailer.registration_confirmation(#registration).deliver
AdminMailer.registration_notice(#registration).deliver
# etc
end
end
end
You'd probably also want to consider sending the mails in the background instead of making the user wait for the create request to finish, but that's beyond the scope of this question.
I'm trying so send an email like this
mail(:to => #user.email, :from => from_email, :subject => t('orders.invoice.email_subject'), :content_type => "text/html") do |format|
format.html { render partial: "notifier/follow_up_email.#{I18n.locale}.html.erb", locals: {storefront: storefront, order: order} }
end
Unfortunately, rails is only looking for follow_up_email.en.html and not follow_up_email.en.html.erb - What am I doing wrong?
render partial: ... should not contain file extensions such as en.html.erb since they are automagically added.
http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html
But why do you use partials for mails, couldn't you create a proper view?
UPDATE
It's all described in the guide on http://guides.rubyonrails.org/v3.2.16/action_mailer_basics.html
app/mailers/user_mailer.rb:
class UserMailer < ActionMailer::Base
default from: "no-reply#memyselfandi.com"
def notification(user, storefront, order)
#user, #storefront, #order = user, storefront, order
I18n.locale = #user.locale # see note below
mail to: #user.email, subject: I18n.t('orders.invoice.email_subject')
end
end
app/views/user_mailer/notification.en.html.erb:
<html><body>
This is a mail for <%= #user.email %> yadda yadda. Use #storefront and #order here.
</body></html>
app/views/user_mailer/notification.de.html.erb:
<html><body>
Das ist ein mail für <%= #user.email %> bla bla.
</body></html>
The line I18n.locale = #user.locale is only necessary if you want to send async mails e.g. when triggering rake tasks from a cronjob.
I'm rather confused about this; I have a custom route here.
I have a groups/:id/new_caretaker. This has a form on it. Whenever that form is POSTed it should go to the same page; But to a different method.
However, if I post the form it says Missing template groups/create_caretaker, application/create_caretaker
How can I fix this?
Here's my form:
<%= form_tag(controller: "groups", action: "create_caretaker", method: "post") do %>
<div class="field">
<%= text_field_tag('email') %>
</div>
<%= submit_tag "Opslaan"%>
<% end %>
And my routes:
get "/groups/:id/new_caretaker" => "groups#new_caretaker", :as => :new_caretaker
post "/groups/:id/new_caretaker" => "groups#create_caretaker"
Added groups.controller methods:
Note: new_caretaker gets #group from a :before_action
def new_caretaker
end
def create_caretaker
email = params[:email]
if !email.blank?
userToAdd = User.find_by_email(email)
if userToAdd.blank?
#User doesn't exist
else
#User does exist
respond_to do |format|
if #group.users.find_by_id(userToAdd)
#theAlert = 'Deze gebruiker zit al in de groep en is niet toegevoegd'
format.html { render action: 'new_caretaker' }
format.json { render json: #theAlert, status: :unprocessable_entity }
else
#group.users << userToAdd
format.html { redirect_to #group, notice: 'De begeleider is toegevoegd.' }
end
end
end
end
end
The if !email.blank? => false and if userToAdd.blank? => true branches don't render or redirect. Therefor rails if looking for a template with the name of that action.
I'd suggest to enhance you routing editing routes.rb:
resources :groups do
member do
get 'new_caretaker'
post 'create_caretaker'
end
end
And then you call this redirect:
redirect_to group_new_caretaker_path(#group)
in the controller when needed.
This way, you'll also get a params[:group_id] param in the new_caretaker controller method to be able to handle group-related data (if needed).
The fix: I did not render anything in the if, just in the else. Some good tips in the answers though.
I have a database of objects (tools) in my Ruby on Rails project. When I use "rails dbconsole" and
select * from tools;
it returns a whole list of tool objects. But when I try to view the following page, it gives me an error.
Page:
<!DOCTYPE html>
<html lang="en">
<%= stylesheet_link_tag "tools", :media => "all" %>
<body>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
</body>
</html>
Error:
undefined method `each' for nil:NilClass
When I change the code to add an if statement against nil objects, the page works (without displaying any tools).
<% if #tools.nil? %>
<% else %>
<%= #tools.each do |tool| %>
<%= link_to(tool(image_tag.image_url)) %>
<% end %>
<% end %>
So it seems like #tools doesn't have any values in it, but when I look at it in the dbconsole, there are tools there. I can't figure this out, and I've spent the past few days googling for answers, so any and all ideas would be welcome!
EDIT: Added tools_controller.rb
class ToolsController < ApplicationController
before_filter :check_authentication
def check_authentication
unless session[:user_id]
session[:intended_action] = action_name
session[:intended_controller] = controller_name
redirect_to new_session_url
end
end
def new
#tool = Tool.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #tool }
end
end
def show
end
def index
#tools = Tool.all
end
# GET /tools/1/edit
def edit
#tool = Tool.find(params[:id])
end
# POST /tools
# POST /tools.json
def create
#tool = Tool.new(params[:tool])
respond_to do |format|
if #tool.save
format.html { redirect_to #tool, :notice => 'tool was successfully created.' }
format.json { render :json => #tool, :status => :created, :location => #tool }
else
format.html { render :action => "new" }
format.json { render :json => #tool.errors, :status => :unprocessable_entity }
end
end
end
end
Loading #tools in a before_filter for every action as #nbarraille has suggested is a bad idea, because there are many (probably most) actions where you will definitely not need the full set of tools (e.g. create and destroy). The line #tools = Tool.all hits your database so you should minimize the number of times you use it.
For the case you have here, you only need to change your show action to get this to work:
def show
#tools = Tool.all
end
However, note that normally the show action is for displaying a single resource (tool), not the whole list of resources (which is normally done in the index action). It looks like you're deviating from the normal way of doing things, is there any particular reason why?
In order for the #tools variable to be accessible from your view, you need to declare it in your controller, like this:
#tools = Tool.all
If you want it to be only accessible from one page, just declare it in the according method.
Here is an example, assuming you want to make the variable available for your home/index page:
class HomeController < ApplicationController
def index
#tools = Tool.all
respond_to do |format|
format.html # index.html.erb
end
end
end
If you want it to be accessible in all your pages, you can declare it in the before_filter method of your ApplicationController.
Here is how to do this:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :load_variables
# Load variables to be used everywhere
def load_variables
#tools = Tool.all
end
end