Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NoMethodError: undefined method `email' for nil:NilClass : can't send email with devise/sendgrid(Rails 3.2)

I am trying to use use actionmailer with sendgrid and devise on Rails 3.2. I browsed many questions on SO like replace smtp by sendmail, create a config/email.yml I don't have one) and many more ideas but nothing works. I must be missing something.

Emails just seem not to be sent in development or production. (sendgrid does not report any email sent and I checked I never receive emails in my "manual tests").

Running Heroku console, I just managed to get this info:

NoMethodError: undefined method `email' for nil:NilClass on the notifier.rb file:

:mail( :to => user.email, :subject => "Thanks for signing up" )

The full trace:

irb(main):007:0> Notifier.welcome_email(@user).deliver --trace
NoMethodError: undefined method `email' for nil:NilClass
from /app/app/mailers/notifier.rb:16:in `welcome_email'
from /app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.13/lib/abstract_controller/base.rb:167:in `process_action'
from /app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.13/lib/abstract_controller/base.rb:121:in `process'
from /app/vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.13/lib/abstract_controller/rendering.rb:45:in `process'
from /app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.13/lib/action_mailer/base.rb:459:in `process'
from /app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.13/lib/action_mailer/base.rb:453:in `initialize'
from /app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.13/lib/action_mailer/base.rb:439:in `new'
from /app/vendor/bundle/ruby/1.9.1/gems/actionmailer-3.2.13/lib/action_mailer/base.rb:439:in `method_missing'
from (irb):7
from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/console.rb:47:in `start'
from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/console.rb:8:in `start'
from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

But I don't understand why it does not recognize the user.email.

Does anybody know what I'm doing wrong?

Here are the files I think are important:

app/mailers/notifier.rb

class Notifier < ActionMailer::Base
  include SendGrid
  sendgrid_category :use_subject_lines
  sendgrid_enable   :ganalytics, :opentrack, :clicktrack
  sendgrid_ganalytics_options :utm_source => "general_notifier", :utm_medium => "email"

default :from => "[email protected]"
# send a signup email to the user, pass in the user object that contains the user's email address
def welcome_email(user)
@user = user
@url  = "http://example.com/login"
mail( :to => user.email, :subject => "Thanks for signing up" )
  end
end

Notifier is called by app/controllers/user_controller.rb:

class UsersController < ApplicationController
before_filter :authenticate_user!

# POST /users
# POST /users.json
def create
@user = User.new(params[:user])

respond_to do |format|
    if @user.save
      # Tell the UserMailer to send a welcome Email after save
      Notifier.welcome_email(@user).deliver

      format.html { redirect_to(@user, :notice => 'User was successfully created.') }
      format.json { render :json => @user, :status => :created, :location => @user }
    else
      format.html { render :action => "new" }
      format.json { render :json => @user.errors, :status => :unprocessable_entity }
    end
    end
  end

def index
  authorize! :index, @user, :message => 'Not authorized as an administrator.'
  @users = User.all
end

def show
  @user = User.find(params[:id])
end

def update
  authorize! :update, @user, :message => 'Not authorized as an administrator.'
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user], :as => :superadmin)
    redirect_to users_path, :notice => "User updated."
  else
    redirect_to users_path, :alert => "Unable to update user."
  end
end

def destroy
  authorize! :destroy, @user, :message => 'Not authorized as an administrator.'
  user = User.find(params[:id])
  unless user == current_user
    user.destroy
    redirect_to users_path, :notice => "User deleted."
  else
    redirect_to users_path, :notice => "Can't delete yourself."
  end
end

My config/Environement.rb files (passwords point to application.yml)

# Load the rails application
require File.expand_path('../application', __FILE__)

# Initialize the rails application
ExampleApp::Application.initialize!

ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
:user_name            => 'SENDGRID_USERNAME',
:password             => 'SENDGRID_PASSWORD',
:domain               => 'SENDGRID_DOMAIN',
:address              => 'smtp.sendgrid.net',
:port                 => 587,
:authentication       => :plain,
:enable_starttls_auto => true
}

also in config/environments/development.rb

  # ActionMailer Config
  config.action_mailer.default_url_options = { :host => 'localhost:3000' }
  config.action_mailer.delivery_method = :smtp
  # change to true to allow email to be sent during development
  config.action_mailer.perform_deliveries = true
  config.action_mailer.raise_delivery_errors = true #useful to have to debug
  config.action_mailer.default :charset => "utf-8"

  config.action_mailer.smtp_settings = {
    :user_name            => 'SENDGRID_USERNAME',
    :password             => 'SENDGRID_PASSWORD',
    :domain               => 'SENDGRID_DOMAIN',
    :address              => 'smtp.sendgrid.net',
    :port                 => 587,
    :authentication       => :plain,
    :enable_starttls_auto => true
  }

And if it might help, my app/models/user.rb with devise:

class User < ActiveRecord::Base
rolify

# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :superadmin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body

validates_presence_of   :name
validates_uniqueness_of :name, :email, :case_sensitive => false

#Add a filter calling assign_role method before user is created
before_create :assign_role

def assign_role
  #assign a default role if no role is assigned
  self.add_role :user if self.roles.first.nil?
end
end

Full local trace when I click on sign up which should launch email to user:

Started GET "/" for 127.0.0.1 at 2013-05-12 11:18:32 +0200
User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 22 LIMIT 1
Processing by StaticPagesController#home as HTML
User Load (0.6ms)  SELECT "users".* FROM "users" 
Rendered static_pages/home.html.erb within layouts/application (2.0ms)
Rendered layouts/_viewports.html.erb (0.0ms)
(0.7ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" =  "users_roles"."role_id" WHERE "users_roles"."user_id" = 22 AND (((roles.name = 'superadmin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))
Rendered layouts/_navigation.html.erb (3.3ms)
Rendered layouts/_header.html.erb (4.0ms)
Rendered layouts/_messages.html.erb (0.1ms)
Rendered layouts/_footer.html.erb (0.5ms)
Completed 200 OK in 129ms (Views: 126.1ms | ActiveRecord: 1.3ms)
[2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set    content-length of the response or set Response#chunked = true
[2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set  content-length of the response or set Response#chunked = true
[2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
 [2013-05-12 11:18:32] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
 [2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-05-12 11:18:33] WARN  Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true

and the logs of my log/development.log

Started GET "/users/sign_up" for 127.0.0.1 at 2013-05-12 11:18:17 +0200
Processing by Devise::RegistrationsController#new as HTML
Rendered devise/shared/_links.erb (0.5ms)
Rendered devise/registrations/new.html.erb within layouts/application (12.6ms)
Rendered layouts/_viewports.html.erb (0.1ms)
Rendered layouts/_navigation.html.erb (1.4ms)
Rendered layouts/_header.html.erb (3.3ms)
Rendered layouts/_messages.html.erb (0.1ms)
Rendered layouts/_footer.html.erb (0.5ms)
Completed 200 OK in 153ms (Views: 151.2ms | ActiveRecord: 0.0ms)


Started POST "/users" for 127.0.0.1 at 2013-05-12 11:18:32 +0200
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓",     
"authenticity_token"=>"xlNUA6fffffYzkFn5bMR18zSiNg=", "user"=>  
{"name"=>"Macro", "email"=>"[email protected]", "password"=>"[FILTERED]", 
"password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
[1m[36m (0.5ms)[0m  [1mBEGIN[0m
[1m[35mUser Exists (0.6ms)[0m  SELECT 1 AS one FROM "users" WHERE "users"."email" =  
'[email protected]' LIMIT 1
[1m[36mUser Exists (0.6ms)[0m  [1mSELECT 1 AS one FROM "users" WHERE 
LOWER("users"."name") = LOWER('Macro') LIMIT 1[0m
[1m[35mUser Exists (0.4ms)[0m  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") 
= LOWER('[email protected]') LIMIT 1
[1m[36mRole Load (0.7ms)[0m  [1mSELECT "roles".* FROM "roles" WHERE "roles"."name" = 
'user' AND "roles"."resource_type" IS NULL AND "roles"."resource_id" IS NULL LIMIT 1[0m
[1m[35mRole Load (0.5ms)[0m  SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 LIMIT 
1 [["id", 6]]
[1m[36mSQL (0.7ms)[0m  [1mINSERT INTO "users" ("created_at", "current_sign_in_at",  
"current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at",    
"last_sign_in_ip", "name", "remember_created_at", "reset_password_sent_at",  
"reset_password_token", "sign_in_count", "updated_at") VALUES ($1, $2, $3, $4, $5, $6,  
$7, $8, $9, $10, $11, $12, $13) RETURNING "id"[0m  [["created_at", Sun, 12 May 2013 
09:18:32 UTC +00:00], ["current_sign_in_at", nil], ["current_sign_in_ip", nil], 
["email", "[email protected]"], ["encrypted_password", 
"$2a$10$WaMqXevJ/zAQeTyJfffffffffffffBtqsCV97G"], ["last_sign_in_at", 
nil], ["last_sign_in_ip", nil], ["name", "Macro"], ["remember_created_at", nil], 
["reset_password_sent_at", nil], ["reset_password_token", nil], ["sign_in_count", 0], 
["updated_at", Sun, 12 May 2013 09:18:32 UTC +00:00]]
[1m[35m (0.3ms)[0m  INSERT INTO "users_roles" ("user_id", "role_id") VALUES (22, 6)
[1m[36m (9.4ms)[0m  [1mCOMMIT[0m
[1m[35m (0.3ms)[0m  BEGIN
[1m[36m (0.7ms)[0m  [1mUPDATE "users" SET "last_sign_in_at" = '2013-05-12 
09:18:32.706773', "current_sign_in_at" = '2013-05-12 09:18:32.706773', "last_sign_in_ip" 
= '127.0.0.1', "current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at" = 
'2013-05-12 09:18:32.707691' WHERE "users"."id" = 22[0m
[1m[35m (8.3ms)[0m  COMMIT
Redirected to http://localhost:3000
Completed 302 Found in 125ms (ActiveRecord: 0.0ms)


Started GET "/" for 127.0.0.1 at 2013-05-12 11:18:32 +0200
[1m[36mUser Load (0.9ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = 22    
LIMIT 1[0m

 Processing by StaticPagesController#home as HTML
 [1m[35mUser Load (0.6ms)[0m  SELECT "users".* FROM "users" 
 Rendered static_pages/home.html.erb within layouts/application (2.0ms)
  Rendered layouts/_viewports.html.erb (0.0ms)
 [1m[36m (0.7ms)[0m  [1mSELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON 
 "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = 22 AND 
 (((roles.name = 'superadmin') AND (roles.resource_type IS NULL) AND (roles.resource_id  
 IS NULL)))[0m
 Rendered layouts/_navigation.html.erb (3.3ms)
 Rendered layouts/_header.html.erb (4.0ms)
 Rendered layouts/_messages.html.erb (0.1ms)
 Rendered layouts/_footer.html.erb (0.5ms)
 Completed 200 OK in 129ms (Views: 126.1ms | ActiveRecord: 1.3ms)
like image 435
Mathieu Avatar asked May 12 '13 08:05

Mathieu


1 Answers

That was a bit misleading, though a bit reckless on my end; the error was never from calling email on nil object (as the title stated), and I should've noticed that you're using Devise so all requests are handled by its RegistrationsController. That said, please change the title, and ignore or delete users_controller; it's not the one that's handling the registration requests.

Then add the following lines to your User model:

after_create { Notifier.welcome_email(self).deliver }

This should do it, given that SendGrid is configured properly :).

like image 61
Ahmad Sherif Avatar answered Sep 24 '22 03:09

Ahmad Sherif