NoMethodError in Users#unsubscribe

2019-09-13 11:21发布

I am working on implementing unsubscribe link to my rails mailer. Unfortunately, my code breaks with this:

NoMethodError in Users#unsubscribe - undefined method `unsubscribe_hash' for nil:NilClass

which points to /app/views/users/unsubscribe.html.erb line #3

<h4>Unsubscribe from Mysite Emails</h4>
<p>By unsubscribing, you will no longer receive email...</p>
<%= simple_form_for(@user, unsubscribe_path(id: @user.unsubscribe_hash)) do |f| %>
    <%= f.hidden_field(:subscription, value: false) %>
    <%= f.submit 'Unsubscribe' %>
    <%= link_to 'Cancel', root_url %>
<% end %>

my user_controller is as shown below

class UsersController < ApplicationController
  protect_from_forgery

  def new
    @user = User.new
  end

  def create
    @user = User.new(secure_params)
    if @user.save
      flash[:notice] = "Thanks! You have subscribed #{@user.email} for Jobs Alert."
    else
      flash[:notice] = 'Error Subscribing! Kindly check your email and try again.'
    end
    redirect_to root_path
  end

  def unsubscribe
    user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
    @user = User.find_by_unsubscribe_hash(user)
  end

  def update
    @user = User.find(params[:id])
    if @user.update(secure_params)
      flash[:notice] = 'Subscription Cancelled'
      redirect_to root_url
    else
      flash[:alert] = 'There was a problem'
      render :unsubscribe
    end
  end

  private

  def secure_params
    params.require(:user).permit(:email, :subscription)
  end

end

Route.rb

  resources :users, only: [:new, :create]
  get 'users/:unsubscribe_hash/unsubscribe' => 'users#unsubscribe', as: :unsubscribe
  patch 'users/update'

user.rb

class User < ActiveRecord::Base

  before_create :add_unsubscribe_hash, :add_true_to_users_table

  validates :email, :uniqueness => true
  validates_presence_of :email
  validates_format_of :email, :with => /\A[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i

  private

  def add_unsubscribe_hash
    self.unsubscribe_hash = SecureRandom.hex
  end

  def add_true_to_users_table
    self.subscription = true
  end

end

unsubscribe link in the email which calls unsubscribe action

# app/views/job_notifier/send_post_email.html.erb
...
<%= link_to "Unsubscribe", unsubscribe_url(id: @unsubscribe) %>.

diagrammatic view of the error

enter image description here

Its seems that I am missing something, do I need to define something in my users_controller? I have never in my life being able to solve NoMethodError or I don't understand what it's all about.

2条回答
ゆ 、 Hurt°
2楼-- · 2019-09-13 11:51

Whenever you see any error messages in ruby of the format:

undefined method 'method_name' for nil:NilClass

you are being told you are trying to call something on a nil object and so the focus of your attention should be on why is that object nil. You are also told in your log where the error occurs - in your case it refers to the line, which refers to @user in @user.unsubscribe_hash in your form declaration.

So @user is nil and in this case it's nil because your controller responsible for rendering the form isn't setting @user:

user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
@user = User.find_by_unsubscribe_hash(user)

Now quite why you are attempting to find the user and then pass that user into the second line to find @user is beyond me, but anyway the real issue is that you have no user that matches params[:unsubscribe_hash]

So the issue is related to whatever is invoking your unsubscribe action ... you have neglected to add that to your question so I cannot help with that but that is where your focus start.

查看更多
霸刀☆藐视天下
3楼-- · 2019-09-13 12:09

You get that error because @user (set in UsersController#unsubscribe) is nil. That is what the "for nil:NilClass" in undefined methodunsubscribe_hash' for nil:NilClass` is referring to.

This method doesn't seem correct:

def unsubscribe
  user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
  @user = User.find_by_unsubscribe_hash(user)
end

You are looking up a user by unsubscribe_hash and assigning it to user, and then looking up user by unsubscribe_hash again but passing in user as the value to find_by_unsubscribe_hash.

I believe something like this is more as intended:

def unsubscribe
  @user = User.find_by_unsubscribe_hash(params[:unsubscribe_hash])
end
查看更多
登录 后发表回答