I'm using Action Mailer for sending email in the rails app using devise. My user mailer.rb file contains this code
class UserMailer < ApplicationMailer
def signup_confirmation(user)
#user = user
mail to: #user.email
end
end
But I get this error
wrong number of arguments (given 0, expected 1)
You have probably something like this. Look at the comment
class MailerController < ApplicationController
def preview
UserMailer.signup_confirmation # miss the user parameter
end
end
The code which is calling your UserMailer.signup_confirmation is apparently not passing the required user argument.
You have to fix the calling code to pass the user to the method. You can find the calling code in the stack trace (i.e. either the "Application trace" or "Full Trace" links on the error page as well as in your log.
Related
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
Having the following mailer previewer code:
class RegistrationMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/registration_mailer/welcome
def welcome
RegistrationMailer.welcome users(:one)
end
end
(full file).
Which is unable to reach my fixtures (users(:one)), return a 500 error status and print out the following error:
NoMethodError: undefined method `users' for #RegistrationMailerPreview
Can we get fixtures entries from mailer previewer?
If yes, I would like to know how to do that.
I have seen that should be possible here, but I can't require test_helper in this file (I don't know why) and I don't understand the difference between ActionMailer::TestCase and ActionMailer::Preview.
If no, is there a way to preview the mail without sending as a parameter User.first, since I could do my tests on a machine on which there is no data filled in the database.
I don't know about the default fixture framework, but I can say using FactoryBot for my fixtures that I was able to use them in my mailer previews simply by prepending the build/create methods with FactoryBot. I didn't need to require anything at the top of the file. i.e.:
class RegistrationMailerPreview < ActionMailer::Preview
def welcome
user = FactoryBot.create(:user)
RegistrationMailer.welcome(user)
end
end
To answer your second question, you could also simply replace the fixture line above with user = User.new(firstname: "Joe"). That would create a new user to use in the preview without persisting it to the database.
Rails 4.1 has a nice way to preview mailers with ActionMailer::Preview. All of my mailers take a user parameter, and I would like to pass in current_user (from Devise) for the preview.
If I try this, it doesn't work.
class SubscriptionsMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/subscriptions_mailer/new
def new
SubscriptionsMailer.new(current_user)
end
end
It returns undefined local variable or method 'current_user' for #<SubscriptionsMailerPreview:0xa6d4ee4>.
I suspect this is because current_user is defined by Devise in ApplicationController, and according to the docs, ActionMailer uses AbstractController::Base. In that case, would storing current_user in a class variable be a bad idea?
Does anyone know how I can use the current_user helper in ActionMailer::Preview?
What would happen if you move your mailer job to the background? How would you get the current user then?
The mailer and its preview should not know about the current_user. The mailer's job is to send the mail to a user it receives. The preview is there to visually demonstrate its behaviour.
Create a new user in your mailer preview, and pass it to the mailer.
def new
user = User.create! # etc...
SubscriptionsMailer.new(user)
end
It doesn't matter who the user is. It matters that it's a user object.
If you want to test that the application will send a mail to the current_user, write a functional test for that.
You are right method defined in Controller won't be available in helper.
These posts can help you:
Where do I put helper methods for ActionMailer views? Access helpers from mailer?
https://www.ruby-forum.com/topic/168949
I'm trying to 'MonkeyPatch' this controller in my implementation so that it can handle a third parameter ('productname').
The original activate method in the gem reads
def activate
if Digest::MD5.hexdigest(params["security_data"] + SaasySimple.config.secret) == params["security_hash"]
SaasySimple.config.model.activate( params['token'], params['id'] )
end
end
My entire new file, placed in lib/monkeys/sassysimple.rb, reads
module SaasySimple
class SubscriptionsController < ApplicationController
def activate
if Digest::MD5.hexdigest(params["security_data"] + SaasySimple.config.secret) == params["security_hash"]
SaasySimple.config.model.activate( params['token'], params['id'], params['productname'] )
end
end
end
end
This isn't working - I'm still getting an error of subscriptions#activate (ArgumentError) "wrong number of arguments (2 for 3)", which I believe is caused because my user method (see below) is expecting productname but not getting it from the un-monkeypatched version. Can someone tell me why putting the file in lib isn't working? Thanks!
This is the user method:
def self.activate(token, id, productname)
user = User.find( id )
user.token = token
user.status = 'active'
user.package = productname
user.save!
end
I'd put the monkeypatch in an config/initializers/sassysimple.rb
Have you verified that the controller really sees all three params?
Is User#activate called anywhere else in the code? e.g. grep for it.
I'm new to Rails and Rspec and I'm using Rspec to test this controller method which includes exception handling:
def search_movies_director
#current_movie = Movie.find(params[:id])
begin
#movies = Movie.find_movies_director(params[:id])
rescue Movie::NoDirectorError
flash[:warning] = "#{#current_movie} has no director info"
redirect_to movies_path
end
end
I can't figure out how to correctly test the said path: after invalid search (when error is received) it should redirect to the homepage. I tried something like this:
describe MoviesController do
describe 'Finding Movies With Same Director' do
#some other code
context 'after invalid search' do
it 'should redirect to the homepage' do
Movie.stub(:find)
Movie.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
get :search_movies_director, {:id => '1'}
response.should redirect_to movies_path
end
end
end
end
After running the test fails with an error: NameError: uninitialized constant Movie::NoDirectorError
How to fake raising an error in this test so it actually checks whether redirect happens?
Thanks!
UPDATE:
As nzifnab explained, it couldn't locate Movie::NoDirectorError. I forgot to define this exception class. So I added it to app/models/movie.rb :
class Movie < ActiveRecord::Base
class Movie::NoDirectorError < StandardError ; end
#some model methods
end
This solved my problem and this test passes.
You're very close. You need to add any_instance in there.
Movie.any_instance.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
edit: I misread the post. The above would work given an instance of Movie, but not for OP's question.
The error indicates it doesn't know where that Movie::NoDirectorError exception class is defined. You might need to specifically require the file where that class is defined or the test may not be able to find it.
Rails will automatically attempt to locate any constant missing constants using a conventional file directory format. It will look for a file in the load_path at movie/no_director_error and movie based on the name of the constant. If the file is not found or the file doesn't define the expected constant than you'll need to specifically require the file yourself.
In Rails 4.1:
verse_selector = double("VerseSelector", select_verses: ActiveRecord::RecordNotFound.new("Verses not found"))
verse_selector.select_verses will now return an ActiveRecord::RecordNotFound