Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NoMethodError in Rails::MailersController#preview undefined method `activation_token=' for nil:NilClass

Cannot seem to find a working answer for this. I'm on Chapter 10, section 10.1.2 of the Rails Tutorial and can't seem to get the mailer preview working. All the answers I've found dealing with the error are related to different sections of the tutorial, and I'm assuming the error I'm making is staring me in the face. I've gone through and copy/pasted the code from the tutorial into the relevant files and so far haven't been able to see a difference between what I typed and what was in the tutorial. So far, the suggestions have been to add or remove the argument user from the function definitions, but that hasn't solved the problem. The url triggering the error is http://localhost:3000/rails/mailers/user_mailer/account_activation. http://localhost:3000/rails/mailers/user_mailer/ is having no issues, and neither is http://localhost:3000/rails/mailers/user_mailer/password_reset (which I haven't done any customization of yet). What am I missing?

Here's the error:

    NoMethodError in Rails::MailersController#preview
    undefined method `activation_token=' for nil:NilClass

Extracted source:

def account_activation
  user = User.first
  user.activation_token = User.new_token # highlighted line
  UserMailer.account_activation(user)
end

From what I can tell, the files involved here are:

user_mailer.rb:

     class UserMailer < ApplicationMailer

       # Subject can be set in your I18n file at config/locales/en.yml
       # with the following lookup:
       #
       #   en.user_mailer.account_activation.subject
       #
       def account_activation(user)
         @user = user
         mail to: user.email, subject: "Account activation"
       end

       def password_reset
         @greeting = "Hi"

         mail to: "[email protected]"
       end
     end

user_mailer_preview.rb:

     # Preview all emails at http://localhost:3000/rails/mailers/user_mailer
     class UserMailerPreview < ActionMailer::Preview

       # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
       def account_activation
         user = User.first
         user.activation_token = User.new_token
         UserMailer.account_activation(user)
       end

       # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset
       def password_reset
         UserMailer.password_reset
       end

     end

development.rb:

    Rails.application.configure do

      config.cache_classes = false

      # Do not eager load code on boot.
      config.eager_load = false

      # Show full error reports and disable caching.
      config.consider_all_requests_local       = true
      config.action_controller.perform_caching = false

      # Don't care if the mailer can't send.
      config.action_mailer.raise_delivery_errors = true
      config.action_mailer.delivery_method = :test
      host = 'localhost:3000'
      config.action_mailer.default_url_options = { host: host }

      # Print deprecation notices to the Rails logger.
      config.active_support.deprecation = :log

      # Raise an error on page load if there are pending migrations.
      config.active_record.migration_error = :page_load

      # Debug mode disables concatenation and preprocessing of assets.
      # This option may cause significant delays in view rendering with a large
      # number of complex assets.
      config.assets.debug = true

      # Asset digests allow you to set far-future HTTP expiration dates on all assets,
      # yet still be able to expire them through the digest params.
      config.assets.digest = true

      # Adds additional error checking when serving assets at runtime.
      # Checks for improperly declared sprockets dependencies.
      # Raises helpful error messages.
      config.assets.raise_runtime_errors = true

      # Raises error for missing translations
      # config.action_view.raise_on_missing_translations = true
    end

and for completeness, here are the views and my user model:

account_activation.html.erb:

    <h1>Sample App</h1>

    <p>Hi <%= @user.name %>,</p>

    <p>
    Welcome to the Sample App! Click on the link below to activate your account:
    </p>

    <%= link_to "Activate", edit_account_activation_url(@user.activation_token,
                                                        email: @user.email) %>

account_activation.text.erb: Hi <%= @user.name %>,

    Welcome to the Sample App. Click on the link below to activate your account:

    <%= edit_account_activation_url(@user.activation_token, email: @user.email) %>

user.rb:

    class User < ActiveRecord::Base
      attr_accessor :remember_token, :activation_token
      before_save   :downcase_email
      before_create :create_activation_digest
      validates :name,  presence: true, length: { maximum: 50 }
      VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
      validates :email, presence: true, length: { maximum: 255 },
                        format: { with: VALID_EMAIL_REGEX },
                        uniqueness: { case_sensitive: false }
      has_secure_password
      validates :password, length: { minimum: 6 }, allow_blank: true

      # Returns the hash digest of the given string.
      def User.digest(string)
        cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                      BCrypt::Engine.cost
        BCrypt::Password.create(string, cost: cost)
      end

      # Returns a random token
      def User.new_token
        SecureRandom.urlsafe_base64
      end

      # Remembers a user in the database for use in persistent sessions
      def remember
        self.remember_token = User.new_token
        update_attribute(:remember_digest, User.digest(remember_token))
      end

      # Returns true if the given token matches the digest.
      def authenticated?(remember_token)
        return false if remember_digest.nil?
        BCrypt::Password.new(remember_digest).is_password?(remember_token)
      end

      # Forgets a user.
      def forget
        update_attribute(:remember_digest, nil)
      end

      private

        # Converts email to all lower-case.
        def downcase_email
          self.email = email.downcase
        end

        # Creates and assigns the activation token and digest.
        def create_activation_digest
          self.activation_token  = User.new_token
          self.activation_digest = User.digest(activation_token)
        end
    end
like image 854
Alec Wilson Avatar asked Mar 06 '15 19:03

Alec Wilson


1 Answers

Wow, lot's of debugging and confusion to discover a really simple problem: I wasn't logged in, and hadn't defined any kind of error message for those trying to access that page if they weren't logged in (hence the user.activation_token method triggering a Nil:NilClass error). Seems like a good example of why TDD can help out a ton.

like image 106
Alec Wilson Avatar answered Sep 20 '22 21:09

Alec Wilson