I'm going through Michael Hartl's priceless tutorial and I got stuck with some Rspec errors. I've double checked everything but it seems like I'm still missing something. Here's what the error messages look like.
The thing that's bothering me the most is when I was generating the Microposts model I accidentally made a typo in one of the options so I did rails destroy model Microposts to undo the generate command before generating the model again. I'm wondering if that has anything to do with the errors I'm seeing.
I really wish to finish this tutorial ASAP so I can get on with building my own web application. ANY help would be appreciated.
Here's what my code looks like.
micropost_pages_spec.rb
require 'spec_helper'
describe "MicropostPages" do
subject {page}
let(:user) {FactoryGirl.create(:user)}
before {sign_in user}
describe "micropost creation" do
before {visit root_path}
describe "with invalid information" do
it "should not create a micropost" do
expect {click_button "Post"}.not_to change(Micropost, :count)
end
describe "error messages" do
before {click_button "Post"}
it {should have_content('error')}
end
end
end
end
microposts_controller.rb
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def create
#micropost = current_user.micropost.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_path
else
render 'static_pages/home'
end
end
def destroy
end
end
static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
#micropost = current_user.microposts.build if signed_in?
end
def help
end
def about
end
def contact
end
end
user.rb (User model)
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :microposts, dependent: :destroy
before_save {self.email.downcase!}
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: {maximum: 50}
validates :email, presence: true, format: {with: VALID_EMAIL_REGEX},
uniqueness: {case_sensitive: false}
validates :password, presence: true, length: {minimum: 6}
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
The error is with this line:
#micropost = current_user.micropost.build(params[:micropost])
It should be:
#micropost = current_user.microposts.build(params[:micropost])
You're using micropost when you should be using microposts.
Related
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.
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
Was using Michael Harti's Ruby on Rails Tutorial and when following it I get an undefined method between two models. I have renamed the models and tried playing around a lot with it but all attempts end up with undefined methods or an uninitialized constant.
Shout Out controller
class ShoutOutController < ApplicationController
def show
#user = User.find(params[:id])
end
def create
#shout_out = current_user.shout_out.build(shout_out_params)
if #shout_out.save
flash[:success] = "Shout Out Created"
redirect_to root_url
else
render user
end
end
def destroy
end
private
def shout_out_params
params.require(:shout_out).permit(:content)
end
end
User Controller
class UsersController < ApplicationController
def index
#users = User.paginate(page: params[:page])
end
def new
#user = User.new
end
def show
#user = User.find(params[:id])
#shoutouts = #user.shout_outs.paginate(page: params[:page])
#shoutouts.user = User.find(params[:id]).name
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
#handles successful update of account info
flash[:success] = "Updated Profile Info"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
user model
class User < ActiveRecord::Base
attr_accessor :remember_token
# :activation_token
has_many :scrimmages
has_many :ShoutOuts, dependent: :destroy
has_many :friendships
has_many :direct_messages, :through => :friendships
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]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: {with: VALID_EMAIL_REGEX}, uniqueness: { case_sensitive: false}
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
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 for remember function
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 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 the user
def forget
update_attribute(:remember_digest, nil)
end
private
# converts emails to downcase
def downcase_email
self.email = email.downcase
end
#creates and assigns activation token and digest
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
micropost model
class ShoutOut < ApplicationRecord
belongs_to :user
default_scope -> { order(created_at: :desc)}
validates :user_id, presence: true
validates :content, presence: true, length: {maximum: 140}
end
view partial
<li id="shoutout-<%= ShoutOut.id %>">
<%= link_to gravatar_for(ShoutOut.user, size: 50), ShoutOut.user %>
<span class="user"><%= link_to ShoutOut.user.name, ShoutOut.user %></span>
<span class="content"><%= ShoutOut.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(shout_out.created_at) %> ago.
</span>
routes file
Rails.application.routes.draw do
# root to: 'users#new'
# these routes are for showing users a login form, logging them in, and logging them out.
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
get '/logout' => 'sessions#destroy'
get '/signup' => 'users#new'
post '/users' => 'users#create'
post '/users/id/edit' => 'users#edit'
resources :users
resources :account_activations, only: [:edit]
root to: 'landing#index'
end
Error Message Screenshot-
There are a couple of errors in the above code which is confusing rails.
In the micropost model, you have defined a class called ShoutOut. You may have heard the expression 'convention over configuration'. Rails is looking for the shoutout file. Your ShoutOut class should be in a file called shout_out.rb in the models folder.
In the user class, you have put has_many ShoutOuts, it should be has_many :shout_outs
I have a feeling the ShoutOutController should be ShoutOutsController, but you will need to post your routes file in order to confirm.
In the view partial, why are you calling ShoutOut.id? I suspect you want to use shout_out.id, but you need to post the view that is calling that partial, along with the controller method that is calling the view. In this view you currently only have access to #user variable which is set in the show method in the Users controller.
It looks like you are not clear on why some things should be capitalised and why they shouldn't. Read up on instance vs method variables and on class methods vs instance methods. Try the following in your view
<% #user.shoutouts.each do |shout_out| %>
<li id="shoutout-<%= shout_out.id %>">
<%= link_to gravatar_for(#user, size: 50), #user %>
<span class="user"><%= link_to #user.name, #user %></span>
<span class="content"><%= shout_out.content %></span>
<span class="timestamp"> Posted <%= time_ago_in_words(shout_out.created_at) %> ago. </span>
</li>
<% end %>
As Joe C commented, you need to post the actual error message along with the stack trace so we can help you debug the error. If you look carefully at the error message, it will give you a file name and line number of where the error is occurring.
Ty again for helping, but what I've been using is the guide from michael harti linked above and, after a lot of changes this was when i decided to post on Stack. I tried using User.shout_out and User.shoutout and User.shout_outs with the results being the same error. Is the guide wrong?
My App works without any problem locally, but as soon as I push it to Heroku the logs show a NoMethodError. Looking through my code, I do not really see what I am missing right away. Especially, why would it work locally but on Heroku it would throw a NoMethod?
Error
Started POST "/users" for <IP>
Processing by UsersController#create as HTML
Completed 500 Internal Server Error in 102ms
app/models/user.rb:38:in `create_remember_token'
app/controllers/users_controller.rb:28:in `create'
NoMethodError (undefined method `remember_token=' for #<User:0x000000052e8fb0>):
user.rb
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :image
has_secure_password
has_many :microposts, dependent: :destroy
mount_uploader :image, AvatarUploader
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true,
length: { maximum: 50}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: {minimum: 6}
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
user_controller.rb
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to El Beano"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = 'Profile updated'
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
It looks as it you've uploaded your code to Heroku but the database migration that created that column was not run or did not run correctly.
Can you verify that the column is there?
Also, have you tried logging to a console session on your heroku instance:
heroku run bash --app my_app
bundle exec rails c
Then testing that you can access that property in that environment?
Good day! I'm practising materials from "Ruby on Rails Tutorial" by Michael Hartle.
Below is the failure message I received, even though the "expected" and "got" seems to match. Would you please give me some suggestion to see how I should approach this issue?
Thank you so much!
Below is the implementation code:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :emp_id, :dept_id, :password, :password_confirmation
validates :emp_id, :presence => true
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(emp_id, submitted_password)
user = find_by_emp_id(emp_id)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
Below is the SPEC code:
require 'spec_helper'
describe User do
before(:each) do
#attr = {:name=>"Example", :dept_id=>01, :emp_id=>10, :password=>"pwdabcd", :password_confirmation => "pwdabcd" }
end
.
.
.
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
.
.
.
describe "authenticate method" do
it "should return the user on emp_id password match" do
matching_user = User.authenticate(#attr[:emp_id], #attr[:password])
matching_user.should == #user
end
end
end
end
Thank you so much for your kind assistance.
Have a nice day!
Kevin - when you see a failure message like that, the representation of the object (#<User ...>) is up to the object, so it's possible that it doesn't show you everything that is being compared by ==. My guess is it has something to do with :password_confirmation, but I'm not certain. It doesn't look like the implementation is using it yet, so try removing password_confirmation from #attr in the spec, and from the attr_accessible declaration, and see if it passes.