ROR 5 API - Internal Server Error when doing a POST action - ruby-on-rails

Im trying to resolve this many days ago. I really don't know how to fix this issue. Im just a beginner with rails and im creating an api for personal use. here's my code:
users_controller.rb:
class UsersController < ApplicationController
def index
users = orchestrate_query(User.all)
render serialize(users)
end
def show
render serialize(user)
end
def create
user = User.new(user_params)
if user.save
UserMailer.confirmation_email(user).deliver_now
render serialize(user).merge(status: :created, location: user)
else
unprocessable_entity!(user)
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render serialize(user).merge(status: :ok)
else
unprocessable_entity!(user)
end
end
def destroy
user.destroy
render status: :no_content
end
private
def user
#user ||= params[:id] ? User.find_by!(id: params[:id]) : User.new(user_params)
end
alias_method :resource, :user
def user_params
params.require(:data).permit(:email, :password, :given_name, :family_name, :role, :confirmation_redirect_url)
end
end
users_confirmation_controller.rb:
class UserConfirmationsController < ActionController::API
before_action :confirmation_token_not_found
def show
user.confirm
if user.confirmation_redirect_url
redirect_to(user.confirmation_redirect_url)
else
render plain: 'You are now confirmed!'
end
end
private
def confirmation_token_not_found
render(status: 404, plain: 'Token not found') unless user
end
def confirmation_token
#confirmation_token ||= params[:confirmation_token]
end
def user
#user ||= User.where(confirmation_token: confirmation_token).first
end
end
user.rb -> model
class User < ApplicationRecord
has_secure_password
before_validation :generate_confirmation_token, on: :create
before_validation :downcase_email
enum role: [:user, :admin]
validates :email, presence: true,
uniqueness: true,
length: { maximum: 255 },
format: { with: /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i }
validates :password, presence: true, length: { minimum: 8 }, if: :new_record?
validates :given_name, length: { maximum: 100 }
validates :family_name, length: { maximum: 100 }
validates :confirmation_token, presence: true, uniqueness: { case_sensitive: true }
def confirm
update_columns({
confirmation_token: nil,
confirmed_at: Time.now
})
end
private
def generate_confirmation_token
self.confirmation_token = SecureRandom.hex
end
def downcase_email
email.downcase! if email
end
end
user_presenter.rb
class UserPresenter < BasePresenter
FIELDS = [:id, :email, :given_name, :family_name, :role, :last_logged_in_at,
:confirmed_at, :confirmation_sent_at, :reset_password_sent_at,
:created_at, :updated_at]
sort_by *FIELDS
filter_by *FIELDS
build_with *[FIELDS.push([:confirmation_token, :reset_password_token,
:confirmation_redirect_url,
:reset_password_redirect_url])].flatten
end
routes.rb
Rails.application.routes.draw do
scope :api do
resources :resorts, except: :put
resources :contact_info, except: :put
resources :users, except: :put
resources :user_confirmations, only: :show, param: :confirmation_token
get '/search/:text', to: 'search#index'
end
root to: 'resorts#index'
end
this is my request to my REST Client;
Method: POST, URL: http://localhost:3000/api/users;
Headers: application/json; and body is:
{"data":{"email":"john#gmail.com",
"password": "password",
"confirmation_redirect_url":"http://google.com"}}
Here is the error:
"error": "Internal Server Error",
app/controllers/users_controller.rb:12:in `create'
please help me. im stuck with it for the past 3 days now.
app/controllers/users_controller.rb:12:in `create'

You need to add an attribute to your users table password_digest:string when using has_secure_password. I am not positive on why your errors aren't showing correctly but this should fix the problem.
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html
has_secure_password
short definition
Adds methods to set and authenticate against a BCrypt password. This mechanism requires you to have a password_digest attribute.

Your user params require you to call the User model to access and save the attributes YOU defined in the User table
def user_params
params.require(:user).permit(:email, :password, :given_name, :family_name, :role, :confirmation_redirect_url)
end

Related

Rails: password can't be blank, BCrypt

I have a user model
class User < ApplicationRecord
include ApplicationConstants
enum role: { admin: 0, waiter: 1, chef:2 }
has_secure_password
validates :name, :role, presence: true, on: :create
validates :email, uniqueness: true, format: EMAIL_REGEX
validates :password, length: { minimum: 8 }, allow_blank: true
end
The controller definition is:
before_action :set_user, only: [:show, :update, :destroy]
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
def set_user
#user = User.find(params[:id])
end
def user_params
params.permit(:name, :role, :email, :password, :password_confirmation)
end
The problem is the following test case fails
test "should update user" do
put user_url(#user), params: { name: #user.name }
assert_response 200
end
The error is: {"password":["can't be blank"]}
I tried other answers like the one listed here, but it didn't work
As Matt pointed out, it was because of the digest attribute being nil. I added it to the fixtures and now it works.

Partial validations in multistep forms (Wizard)

I have a multistep form, which I created with wizard. Basically the first tep of the form is user/sign_up - which in my understanding not a step yet. After hitting the sign-up button, user moves to the "real" first step, which is :address.
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
def show
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
render_wizard
end
def update
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
def redirect_to_finish_wizard(options = nil, params = nil)
redirect_to new_user_profile_path(current_user)
end
end
This is basically the end of the form already. All gets saved to the user. Now I am stuck with validations.
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: %i[facebook]
has_one :profile, dependent: :destroy
after_create :create_profile
accepts_nested_attributes_for :profile
validates :first_name, presence: true
validates :last_name, presence: true
validates :street, presence: true
validates :house_number, presence: true
validates :city, presence: true
validates :zip_code, presence: true
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
name = auth.info.name
user.first_name = name.split(" ")[0]
user.last_name = name.split(" ")[1]
end
end
end
I would love to work with the the conditional validations in my model and only validate presence if on a certain step. This should be easy, as I theoretically only have one step, which is address. All I find on the internet, is way too complicated. Question is, do I have to somehow change user/sign_up to a first step in the form and address would be the second step? Or is it fine like this? And if so, can I just add the "if" statements to the address attributes in my validations, somehow defining what is the address step? Would it work like this?
def on_address_step?
wizard.steps = wizard.steps.first
end
Or how do I define it? The validations would look like this then:
validates :first_name, presence: true
validates :last_name, presence: true
validates :street, presence: true, if: :on_address_step?
validates :house_number, presence: true, if: :on_address_step?
validates :city, presence: true, if: :on_address_step?
validates :zip_code, presence: true, if: :on_address_step?
This is surely not that easy. For now this also doesn't work. How do I need to change it? Thanks.
P.S: here is also my Users Controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
end
If filling in the address is a completely separate process I would just branch the address out into its own model and controller.
class User < ApplicationRecord
# ...
has_one :address
end
class Address < ApplicationRecord
# ...
belongs_to :user
validates :first_name, :last_name, :street,
:house_number, :city, :zip_code, presence: true
end
This avoids turning your user model into even more of a god object and removes the need for the conditional validation that makes your model much more aware of the UX steps than it should be.
# routes.rb
resources :addresses, only: [:new, :create]
class UsersController < ApplicationController
# ...
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to new_address_path
else
render :new
end
end
end
class AddressesController < ApplicationController
# You should have some sort of method that checks if the user
# is signed in and redirect otherwise
before_action :authenticate_user!
# GET /addresses/new
def new
# I'm assuming you have some sort of method to fetch the signed in user
#address = current_user.build_address
end
# POST /addresses
def create
#address = current_user.build_address(address_params)
if #address.save
redirect_to '/somepath'
else
render :new
end
end
def address_params
params.require(:address).permit(
:first_name, :last_name, :street,
:house_number, :city, :zip_code
)
end
end
<%= form_with(model: #address) %>
# ... inputs
<% end %>
I doubt you really want the complexity involved with using Wicked which is ok if you really need a long multiple step form but in this case there is a far simpler and better design choice.

"password_digest can't be blank" and "password can't be blank" errors

I'm trying to create a user via Postman as shown in the screenshot, but getting errors:
This is a rails app created with an --api option.
user.rb
class User < ApplicationRecord
validates :email, uniqueness: true
validates_format_of :email, with: /#/
validates :password_digest, presence: true
has_secure_password
end
users_controller.rb
class Api::V1::UsersController < ApplicationController
# GET /users/1
def show
render json: User.find(params[:id])
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:email, :password)
end
end
routes.rb
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :users, only: [:show, :create]
end
end
end
Your user_params method expects the attributes nested in a user hash. Change the response to:
{
"user": {
"email": "...",
"password": "..."
}
}
Btw the validates :password_digest, presence: true line is not needed because has_secure_password has validations build in and ensures internally that a password is present.

Create users without a password in Rails

I have three user roles defined using enums and a single users table with STI. My three user roles are staff, clinician, and listed. The aim is that staff will upload lists of names and emails which will create users of role listed. Once these listed users create their own passwords, their role will change to clinician.
I am working on adjusting my model and controller to allow to create these listed users but running into difficulty as most of the answers I've found deal with a Devise implementation. I am using an authentication system mostly built with Michael Hartl's Rails Tutorial. Do you have any tips on how I can get this to work? Currently when I try to sign up as a listed user, I get the following error:
unknown attribute 'password' for Listed.
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
Any help at all would be greatly appreciated. Thanks!
Here are my user models:
class User < ApplicationRecord
self.inheritance_column = :role
enum role: { Staff: 0, Clinician: 1, Listed: 2 }
attr_accessor :remember_token, :activation_token, :reset_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 }
validates :role, presence: 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
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def feed
ReferralRequest.where("user_id = ?", id)
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
class Staff < User
validates :university_id, presence: true
belongs_to :university
has_many :referral_requests
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
class Clinician < User
has_many :lists
has_many :universities, through: :lists
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
class Listed < User
has_many :lists
has_many :universities, through: :lists
end
Here is my users controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:show, :index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#referral_requests = #user.referral_requests.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name, :email, :role, :university_id, :password, :password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
I'm not sure if STI is the best approach to manage roles, anyway, if your User class has the attribute password and you are correctly making the inheritance, Listed should have that attribute too.
Your Listed model should look like this:
Class Listed < User
end
Same for all your models that inherit from User

updating resource undefined method `valid?' for false:FalseClass

I have some validations for my Lesson model, and I'm able to highlight validation problems on the controller under the create action with the valid? method. However, if I try to valid? in an analogous manner, I get undefined methodvalid?' for false:FalseClass` How can I go about validating my edit form upon submission, such that it renders the edit form again if the validation doesn't pass?
Lesson model:
class Lesson < ActiveRecord::Base
belongs_to :user
has_many :words, dependent: :destroy
validates :title, presence: true, length: { maximum: 55 }
validates :description, presence: true, length: { maximum: 500 }
validates :subject, presence: true, length: { maximum: 55 }
validates :difficulty, presence: true, numericality: { less_than_or_equal_to: 5 }
end
Controller:
class Teacher::LessonsController < ApplicationController
before_action :authenticate_user!
before_action :require_authorized_for_current_lesson, only: [:show, :edit, :update]
def show
#lesson = Lesson.find(params[:id])
end
def new
#lesson = Lesson.new
end
def edit
#lesson = Lesson.find(params[:id])
end
def create
#lesson = current_user.lessons.create(lesson_params)
if #lesson.valid?
redirect_to teacher_lesson_path(#lesson)
else
render :new, status: :unprocessable_entity
end
end
def update
#lesson = current_lesson.update_attributes(lesson_params)
if #lesson.valid?
redirect_to teacher_lesson_path(current_lesson)
else
render :edit, status: :unprocessable_entity
end
end
private
def require_authorized_for_current_lesson
if current_lesson.user != current_user
render text: "Unauthorized", status: :unauthorized
end
end
def current_lesson
#current_lesson ||= Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:title, :description, :subject, :difficulty)
end
end
If you see an error that looks like undefined method 'valid?' for 'false:FalseClass
That means that wherever you call the method :valid?, the object on which you are calling it is not the object you expect, it is instead just false
So you have two instances in your code where you are calling #lesson.valid?, which means one or both of the assignments of #lesson is sometimes returning false.
In the docs of create, it says: The resulting object is returned whether the object was saved successfully to the database or not.
In the docs of update_attributes, it says: If the object is invalid, the saving will fail and false will be returned.
So it looks like your problem is with update_attributes, which apparently just returns false if your update was unsuccessful.

Resources