Abdul Mu'min
Abdul Mu'min

Reputation: 29

Rails 5.2.2 Heroku always return app[web.1]: FATAL -- NameError (uninitialized constant AuthenticateUser::JsonWebToken)

I follow tutorial from https://www.pluralsight.com/guides/token-based-authentication-with-ruby-on-rails-5-api with some fixed error its running well..

but after i deploy to heroku, When i want to access authenticate route (sign in user), i got status code 500 in insomia :

{
  "status": 500,
  "error": "Internal Server Error"
}

and in terminal i got this code :

2019-01-30T03:07:19.567264+00:00 app[web.1]: I, [2019-01-30T03:07:19.567151 #9]  INFO -- : [49a401d5-1f31-4a88-8299-3c14d07ef160] Started POST "/authenticate" for 125.161.110.130 at 2019-01-30 03:07:19 +0000
2019-01-30T03:07:19.568759+00:00 app[web.1]: I, [2019-01-30T03:07:19.568673 #9]  INFO -- : [49a401d5-1f31-4a88-8299-3c14d07ef160] Processing by AuthenticationController#authenticate as JSON
2019-01-30T03:07:19.569025+00:00 app[web.1]: I, [2019-01-30T03:07:19.568946 #9]  INFO -- : [49a401d5-1f31-4a88-8299-3c14d07ef160]   Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "authentication"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}}
2019-01-30T03:07:19.609086+00:00 app[web.1]: D, [2019-01-30T03:07:19.608939 #9] DEBUG -- : [49a401d5-1f31-4a88-8299-3c14d07ef160]    (3.5ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
2019-01-30T03:07:19.618191+00:00 app[web.1]: D, [2019-01-30T03:07:19.618074 #9] DEBUG -- : [49a401d5-1f31-4a88-8299-3c14d07ef160]   User Load (3.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`email` = '[email protected]' LIMIT 1
2019-01-30T03:07:19.742727+00:00 app[web.1]: I, [2019-01-30T03:07:19.742595 #9]  INFO -- : [49a401d5-1f31-4a88-8299-3c14d07ef160] Completed 500 Internal Server Error in 173ms (ActiveRecord: 10.4ms)
2019-01-30T03:07:19.743582+00:00 app[web.1]: F, [2019-01-30T03:07:19.743490 #9] FATAL -- : [49a401d5-1f31-4a88-8299-3c14d07ef160]
2019-01-30T03:07:19.743681+00:00 app[web.1]: F, [2019-01-30T03:07:19.743602 #9] FATAL -- : [49a401d5-1f31-4a88-8299-3c14d07ef160] NameError (uninitialized constant AuthenticateUser::JsonWebToken):
2019-01-30T03:07:19.743757+00:00 app[web.1]: F, [2019-01-30T03:07:19.743677 #9] FATAL -- : [49a401d5-1f31-4a88-8299-3c14d07ef160]
2019-01-30T03:07:19.743841+00:00 app[web.1]: F, [2019-01-30T03:07:19.743763 #9] FATAL -- : [49a401d5-1f31-4a88-8299-3c14d07ef160] app/commands/authenticate_user.rb:10:in `call'
2019-01-30T03:07:19.743859+00:00 app[web.1]: [49a401d5-1f31-4a88-8299-3c14d07ef160] app/controllers/authentication_controller.rb:5:in `authenticate'

here my authentication_controller :

class AuthenticationController < ApplicationController
  skip_before_action :authenticate_request
  
  def authenticate
    command = AuthenticateUser.call(params[:email], params[:password])
  
    if command.success?
      render json: { auth_token: command.result }
    else
      render json: { error: command.errors }, status: :unauthorized
    end
  end
end

here my authenticate_user :

class AuthenticateUser
  prepend SimpleCommand

  def initialize(email, password)
    @email = email
    @password = password
  end

  def call
    JsonWebToken.encode(user_id: user.id) if user
  end

  private

  attr_accessor :email, :password

  def user
    user = User.find_by_email(email)
    return user if user && user.authenticate(password)

    errors.add :user_authentication, 'invalid credentials'
    nil
  end
end

here json_web_token :

class JsonWebToken
  class << self
    def encode(payload, exp = 24.hours.from_now)
      payload[:exp] = exp.to_i
      JWT.encode(payload, RubyChallenge::Application.credentials.secret_key_base)
    end
  
    def decode(token)
      body = JWT.decode(token, RubyChallenge::Application.credentials.secret_key_base)[0]
      HashWithIndifferentAccess.new body
    rescue
      nil
    end
  end
end

I have set the environment variable RAILS_MASTER_KEY, to my heroku with the same value as the contents of config / master.key : my heroku config var

my Gemfile :

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.1'

gem 'rails', '~> 5.2.2'
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
gem 'puma', '~> 3.11'
gem 'bcrypt', '~> 3.1.7'
gem 'jwt'
gem 'simple_command'
gem 'figaro'
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Thanks in advance

EDIT:

here my config / application.rb :

require_relative 'boot'

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"


Bundler.require(*Rails.groups)

module RubyChallenge
  class Application < Rails::Application
    config.autoload_paths << Rails.root.join('lib')
    config.api_only = true
  end
end
btw thx Mr.Gabbar for your suggestion

Upvotes: 2

Views: 960

Answers (3)

Anoob K Bava
Anoob K Bava

Reputation: 605

I have followed the same blog, but when I place my code after the class header, it just works !!!

module TodoApiApp
  class Application < Rails::Application
    config.eager_load_paths << Rails.root.join('lib')
    config.load_defaults 5.2
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins '*'
        resource '*', headers: :any, methods: %i[get post delete put patch options]
      end
    end
    config.api_only = true
  end
end

Upvotes: 0

Abdul Mu&#39;min
Abdul Mu&#39;min

Reputation: 29

SOLUTION :

Change my config/application.rb from

module RubyChallenge
  class Application < Rails::Application
    ...
    config.autoload_paths << Rails.root.join('lib')
    ...
  end
end

to

module RubyChallenge
  class Application < Rails::Application
    ...
    config.eager_load_paths << Rails.root.join('lib')
    ...
  end
end

make it runing well, it because Rails 5 disables autoloading after booting the app in production, thx to Shailesh Kalamkar Article

Upvotes: 0

Anand
Anand

Reputation: 6531

Probably you are missing this: -

By default any file in app directory is auto loaded and you can use it without including it, However outside this you would have to load it when application is loaded.

To make sure everything will work, the contents of the lib directory have to be included when the Rails application loads.

config/application.rb

module ApiApp
  class Application < Rails::Application
    #.....
    config.autoload_paths << Rails.root.join('lib')
    #.....
  end
end

Upvotes: 1

Related Questions