Related
I have a Devise User model and in my application I have different roles which I am specifying through an enum in my User model. When I am running the tests for the admin role, I am receiving the following error when running RSpec tests with Devise. I have tried some of the other answers to similar issues but nothing seems to be working. I hope you can point me in the right direction. Thanks!
RuntimeError:
Could not find a valid mapping for {:email=>"collin_cain#torpdoyle.info", :password=>"12345678", :password_confirmation=>"12345678", :role=>2}
Here is the User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :comments
enum role: [:member, :moderator, :admin]
before_save :set_default_role
def set_default_role
self.role ||= 0
end
end
The user factory:
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
password "12345678"
password_confirmation "12345678"
role 0
end
end
The categories controller spec
require 'rails_helper'
RSpec.describe Admin::CategoriesController, type: :controller do
it 'should redirect to sign in path for non signed users' do
get :index
expect(response).to redirect_to(new_user_session_path)
end
it 'should redirect to root path for non admin users' do
user = create(:user)
sign_in user
get :index
expect(response).to redirect_to(root_path)
end
describe 'GET #index' do
context 'when admin signed in' do
it 'renders the index template' do
admin = attributes_for(:user, role: 2)
sign_in admin
get :index
expect(response).to render_template(:index)
end
it 'assigns a list of categories' do
admin = attributes_for(:user, role: 2)
sign_in admin
category = create(:category)
expect(assigns(:categories)).to eq([category])
end
end
end
end
and the routes file
Rails.application.routes.draw do
devise_for :users
namespace :admin do
get '', to: 'dashboard#index', as: '/'
resources :categories
end
resources :topics do
resources :comments, only: :create
end
resources :categories do
resources :topics
end
root 'categories#index'
end
I am also adding the User schema
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "role"
t.string "image"
end
UPDATE:
I have updated the admin categories controller spec, specifically Devise's sign_in method from sign_in user to sign_in(:admin, user) as shown below.
describe 'GET #index' do
context 'when admin signed in' do
it 'renders the index template' do
user = create(:user)
user.role = 2
sign_in(:admin, user)
get :index
expect(response).to render_template(:index)
end
...
Now I am getting the following error
1) Admin::CategoriesController GET #index when admin signed in renders the index template
Failure/Error: expect(response).to render_template(:index)
expecting <"index"> but was a redirect to <http://test.host/users/sign_in>
For some reason the admin is not being signed in, I have included Devise Test Helpers in rails_helper.rb file, unfortunately the error continues. Any help will be greatly appreciated.
Have you declared the role in the migration like
t.integer :role
As this would need to be in there to be included in the migration created structure.
If not
Add the line, in your database drop the table and run your rake again
I was able to troubleshoot my own question and decided to post the answer in hope that it will help someone in the future.
Instead of setting the user role to admin in the the admin_categories_controller_spec file, instead I added a nested Factory inside the Users Factory.
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
password "12345678"
password_confirmation "12345678"
role 0
factory :admin do
role 2
end
end
end
and the test ends up like this:
describe 'GET #index' do
context 'when admin signed in' do
it 'renders the index template' do
admin = create(:admin)
sign_in admin
get :index
expect(response).to render_template(:index)
end
it 'assigns a list of categories' do
admin = create(:admin)
sign_in admin
category = create(:category)
get :index
expect(assigns(:categories)).to eq([category])
end
end
end
I am getting unknown attribute 'post' for Favorite. This error normally occurs when I am missing a column in a table. The error is stating this portion is where the issue lies: favorite = current_user.favorites.build(post: post). But there shouldn't be another column called post. Do I have another nested set of params I am missing perhaps that is layered under something? Or is my referential integrity incorrect?
Screenshot of error
Favorites_controller.rb file
class FavoritesController < ApplicationController
def create
post = Post.find(params[:post_id])
favorite = current_user.favorites.build(post: post)
if favorite.save
flash[:notice] = "This post is now favorited."
redirect_to [post.topic, post]
else
flash[:error] = "There was an error favoriting you post. Please try again."
redirect_to [post.topic, post]
end
end
end
Favorites schema
create_table "favorites", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "post_id"
end
Post Controller
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :votes
has_one :summary
belongs_to :user #means the post table has the user table's primary k ey in it
belongs_to :topic
has_many :favorites
#has_one :summary
mount_uploader :avatar, AvatarUploader
#default_scope {order('created_at DESC')}
default_scope {order('rank DESC')}
validates :title, length: {minimum: 5}, presence: true
validates :body, length: {minimum: 20}, presence: true
validates :topic, presence: true
validates :user, presence: true
def create_vote
# self == post
user.votes.create(value: 1, post: self)
end
def markdown_title
(render_as_markdown).render(self.title).html_safe
end
def markdown_body
(render_as_markdown).render(self.body).html_safe
end
def up_votes
votes.where(value: 1).count
end
def down_votes
votes.where(value: -1).count
end
def points
votes.pluck(:value).sum
end
def update_rank
age_in_days = (created_at - Time.new(1970,1,1)) / (60 * 60 * 24) #1 day in seconds
new_rank = points + age_in_days
update_attribute(:rank, new_rank)
end
private
def render_as_markdown
renderer = Redcarpet::Render::HTML.new
extensions = {fenced_code_blocks: true}
redcarpet = Redcarpet::Markdown.new(renderer, extensions)
return redcarpet
end
end
Post table schema
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "topic_id"
t.string "avatar"
t.float "rank"
end
Favorites route
rake routes | grep -i favorites
post_favorites POST /posts/:post_id/favorites(.:format) favorites#create
post_favorite DELETE /posts/:post_id/favorites/:id(.:format) favorites#destroy
This line
favorite = current_user.favorites.build(post: post)
should be
favorite = current_user.favorites.build(post_id: post.id)
Because you have post_id in your favourites table not post.
I am trying to model my first rails 4 project to make the leap from tutorial to the real world.
The app is a horse show signup.
The user (devise) adds an 'entry'.
The entry can consists one or more 'rides'
Each ride can have a single 'horse' and a single 'rider' (nested forms would allow user to select existing horse and rider or create new one via form during new entry create.
I started to do some nested web forms with Cocoon and Formtastic:
https://github.com/nathanvda/cocoon
buy I have problems with saving related data to tables.
When I save my entry the rides table contains correct values for:
test, entry_id, but not horse_id
The entry table has no ride_id value. It only has user_id (I set in controller manually)
My questions are:
I am sure I am missing a few conceptual concepts but do I have my models associations set up correctly?
How do I edit my current forms to save data correctly?
When I debug in the controller I get what looks to be an odd param value:
{"utf8"=>"√", "authenticity_token"=>"xxxxxxxxxxxxxx=", "entry"=>{"show_date"=>"10/26/2014", "rides_attributes"=>{"1407930343404"=>{"test"=>"Intro B", "horses"=>{"name"=>"Plain Horse", "_destroy"=>""}}}}, "commit"=>"Create Entry", "action"=>"create", "controller"=>"entries"}
I thought I would see 'horses_attributes' within the 'rides_attributes' hash but instead I see just 'horses'.
Any clarification including link to examples would be very appeciated.
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :entries
has_many :rides, :through => :entries
has_many :horses, :through => :entries
has_many :riders, :through => :entries
end
class Entry < ActiveRecord::Base
belongs_to :user
has_many :rides, :dependent => :destroy
has_many :horses, :through => :rides
has_many :riders, :through => :rides
accepts_nested_attributes_for :rides, :allow_destroy => true
accepts_nested_attributes_for :horses
accepts_nested_attributes_for :riders
end
class Ride < ActiveRecord::Base
belongs_to :entry
belongs_to :user
has_one :horse #, :dependent => :destroy
has_one :rider
accepts_nested_attributes_for :horse
accepts_nested_attributes_for :rider
end
class Horse < ActiveRecord::Base
belongs_to :ride # :dependent => :destroy
belongs_to :user, :dependent => :destroy
end
class Rider < ActiveRecord::Base
#belongs_to :ride #, :dependent => :destroy
belongs_to :user, :dependent => :destroy
has_many :rides
end
Here is my partial schema:
create_table "entries", force: true do |t|
t.date "show_date"
t.integer "user_id"
t.integer "ride_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "entries", ["ride_id"], name: "index_entries_on_ride_id", using: :btree
add_index "entries", ["user_id"], name: "index_entries_on_user_id", using: :btree
create_table "horses", force: true do |t|
t.string "name"
t.string "horse_ncdcta"
t.string "owner_fname"
t.string "owner_lname"
t.date "coggins_date"
t.integer "ride_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
add_index "horses", ["ride_id"], name: "index_horses_on_ride_id", using: :btree
create_table "riders", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "rider_ncdcta"
t.boolean "senior"
t.boolean "liability_signed"
t.integer "ride_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "riders", ["ride_id"], name: "index_riders_on_ride_id", using: :btree
create_table "rides", force: true do |t|
t.string "test"
t.integer "entry_id"
t.integer "rider_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "horse_id"
end
add_index "rides", ["entry_id"], name: "index_rides_on_entry_id", using: :btree
add_index "rides", ["rider_id"], name: "index_rides_on_rider_id", using: :btree
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name"
t.string "last_name"
t.string "phone"
end
partial entry controller:
def new
#user=current_user
#entry=Entry.new
end
def create
#entry = Entry.new(entry_params)
#entry.user_id=current_user[:id]
respond_to do |format|
if #entry.save
# example of params value
# {"utf8"=>"√", "authenticity_token"=>"xxxxxxxxxxxxxx=", "entry"=>{"show_date"=>"10/26/2014", "rides_attributes"=>{"1407930343404"=>{"test"=>"Intro B", "horses"=>{"name"=>"Plain Horse", "_destroy"=>""}}}}, "commit"=>"Create Entry", "action"=>"create", "controller"=>"entries"}
debugger
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
def entry_params
# added ids based on comments below
params.require(:entry).permit(:show_date, rides_attributes: [:id, :test, :_destroy, horses_attributes: [:name, :id]] )
end
views:
_form.html.haml
= semantic_form_for #entry do |f|
= f.inputs do
= f.label :show_date, "Show Date"
= f.input :show_date, :as => :select, :collection => [['08/03/2014', '08/03/2014'], ['09/14/2014', '09/14/2014'], ['10/26/2014', '10/26/2014'], ['11/15/2014', '11/15/2014']]
%h3 Rides
#rides
= f.semantic_fields_for :rides do |ride|
= render 'ride_fields', :f => ride
.links
= link_to_add_association 'add ride', f, :rides
= f.actions do
= f.action :submit
_ride_fields.html.haml
.nested-fields
= f.inputs do
= f.label :test, "Test"
= f.input :test, :as => :select, :collection => [['Intro A', 'Intro A'], ['Intro B', 'Intro B'], ['Intro C', 'Intro C']]
= f.semantic_fields_for :horses do |horse|
= render 'horse_fields', :f => horse
= link_to_add_association 'add horse', f, :horse
_horse_fields.html.haml
.nested-fields
=f.inputs do
= f.input :name
.links
= link_to_add_association 'add horse', f, :horse
Here's what I think is happening, you're nesting horse within Rides instead of Entry. Ride accepts nested attributes for a has_one horse relationship so you're seeing 'horses'. If you submitted horses against the Entry (which has a has_many relationship) you'd see horses_attributes.
In this section of _form.html.haml you assigned ride to f
= render 'ride_fields', :f => ride
And then in your _ride_fields.html.haml you use that f to create a nested form which is being created off the ride variable (Ride has_one horse).
= f.semantic_fields_for :horses do |horse|
= render 'horse_fields', :f => horse
Because your Ride model has accepts_nested_attributes_for :horse you're seeing horses within the Ride. To see horses_attributes you'll want to create horses against the Entry (which accepts the nested attributes for "horses").
I believe you can fix it by changing the render like this (pass in the Entry model form):
= render 'ride_fields', :f => ride, :e => f
And then the horse nested form like this (from f to e):
= e.semantic_fields_for :horses do |horse|
= render 'horse_fields', :f => horse
When using nested attributes the symbol you pass to form_for has to match the association name. You are doing
f.semantic_fields_for :horses
But rides doesn't have a horses assocation, only a horse association. Because of this rails doesn't realise that you're trying to use nested attributes at all, which is why the data is nested under the key "horses" rather than "horses_attributes". You will of course need to change the permitted params to match.
In addition you don't need all the stuff to manage adding extra horse fields for a ride if a ride can in fact only have one horse. If this was a mistake, then leave your forms alone but change the model so that a ride has_many :horses and accepts_nested_attributes_for :horses
I am now saving relationships correctly with below code (with a one caveat). Thanks to previous posters for getting me headed in the right direction!
Also Formtastic doc link: http://rdoc.info/github/justinfrench/formtastic/Formtastic/Inputs/SelectInput
The caveat: I cannot figure out how to select an existing horse with form select if I do not want to add a new horse (_ride_fields.html.haml). Adding new horse works fine.
When I attempt with current commented code I get error:
undefined method ride_id for Ride
I am not sure if this a formtastic problem, model related, or controller. I will post this as a separate question if I do not figure it out.
I realized in my original posting that I should not have foreign keys (eg ride_id) saved to 'entries' table because they are saved to the related tables (rides) in entry_id field.
entries controller (partial):
class EntriesController < ApplicationController
before_filter :set_horses, :except => [:destroy, :index]
def new
#c_user=current_user[:id]
#user=current_user
#entry = current_user.entries.new
#horses = current_user.horses
end
def create
#entry = current_user.entries.new(entry_params)
respond_to do |format|
if #entry.save
format.html { redirect_to #entry, notice: 'Entry was successfully created.' }
format.json { render :show, status: :created, location: #entry }
else
format.html { render :new }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_horses
if current_user.admin?
#horses=Horse.all
else
#horses = current_user.horses
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:show_date, rides_attributes: [:id, :test, :_destroy, horse_attributes: [:name, :id]] )
end
end
entry.rb:
class Entry < ActiveRecord::Base
belongs_to :user
has_many :rides, :dependent => :destroy
has_many :horses, :through => :rides, :dependent => :destroy
has_many :riders, :through => :rides, :dependent => :destroy
accepts_nested_attributes_for :rides, :reject_if => lambda { |a| a[:test].blank? }, :allow_destroy => true
accepts_nested_attributes_for :horses, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
accepts_nested_attributes_for :riders, :allow_destroy => true
validates_presence_of :show_date
end
ride.rb:
class Ride < ActiveRecord::Base
belongs_to :entry, inverse_of: :entry
belongs_to :user, inverse_of: :user
has_one :horse
has_one :rider
accepts_nested_attributes_for :horse
end
horse.rb:
class Horse < ActiveRecord::Base
belongs_to :ride, :dependent => :destroy
belongs_to :user, :dependent => :destroy
end
_form.html.haml
= semantic_form_for #entry do |f|
= f.inputs do
= f.input :show_date, :as => :select, :collection => ['2014/08/03', '2014/09/14', '2014/10/26', '2014/11/15']
%h3 Rides
#rides
= f.semantic_fields_for :rides do |ride|
= render 'ride_fields', :f => ride
.links
= link_to_add_association 'add ride', f, :rides
= f.actions do
= f.action :submit
_ride_fields.html.haml
.nested-fields
= f.inputs do
= f.label :test, "Test"
= f.input :test, :as => :select, :collection => [['Intro A', 'Intro A'], ['Intro B', 'Intro B'], ['Intro C', 'Intro C']]
= if !#horses.empty?
-# both of next 2 lines give error: undefined method ride_id for <Ride:0x00000008cc3370>
-#= f.input :horse, :as => :select, :collection => #horses
-#= f.input :horse, :as => :select, :member_label => :name
= f.semantic_fields_for :horse do |horse|
= render 'horse_fields', :f => horse
.links
= link_to_add_association 'add new horse', f, :horse
_horse_fields.html.haml
.nested-fields
= f.inputs do
= f.input :name
I've been at this for days, and cannot figure out the problem. When I attempt to Sign Up, it appears to work, but when I check the rails console, it shows no username. I'm able to add one from there and it works fine, but using my Sign Up view it never works. Only the fields that were created when I installed Devise work. I've also added First and Last Name, but I only attempted to get Username working, so the code for names is incomplete. If I've left anything important out, let me know. I'd REALLY appreciate the help.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
protected
def configure_devise_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:email, :username, :password, :password_confirmation)
end
end
.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :username, :uniqueness => {:case_sensitive => false}
has_many :posts
has_many :offers
has_many :requests
has_one :profile
def name
"#{first_name} #{last_name}"
end
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value", { :value => login.downcase }]).first
end
end
.
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %>
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :username %>
<%= f.text_field :username%><div>
<div><%= f.label :first_name %>
<%= f.text_field :first_name %><div>
...
.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def show_user
#user = current_user
render 'show'
end
def index
#user = User.all
end
def new
#user = User.new
render profiles_path
end
def create
#user = User.new(user_params)
if #user.save
redirect_to posts_path, notice: 'User successfully added.'
else
render profiles_path
end
end
def edit
end
def update
if #user.update(user_params)
redirect_to posts_path, notice: 'Updated user information successfully.'
else
render action: 'edit'
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :first_name, :last_name, :email, :username)
end
end
.
class RegistrationsController < Devise::RegistrationsController
def update
new_params = params.require(:user).permit(:email,
:username, :current_password, :password,
:password_confirmation)
change_password = true
if params[:user][:password].blank?
params[:user].delete("password")
params[:user].delete("password_confirmation")
new_params = params.require(:user).permit(:email,
:username)
change_password = false
end
#user = User.find(current_user.id)
is_valid = false
if change_password
is_valid = #user.update_with_password(new_params)
else
#user.update_without_password(new_params)
end
if is_valid
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end end
end
Schema:
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name"
t.string "last_name"
t.string "username"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
Add this to your ApplicationController
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end
Source: https://github.com/plataformatec/devise#strong-parameters
In a Rails 4 app -- I am getting this error when trying to create a simple User in my console.
RuntimeError:
Password digest missing on new record
My model, controller, and schema looks like:
class User < ActiveRecord::Base # User.rb
has_secure_password
end
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to root_path, notice: "Thank you for signing up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:email, :name, :password, :password_confirmation)
end
end
create_table "users", force: true do |t| #db/schema.rb (edited for brevity)
t.string "email"
t.string "name"
t.text "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
end
I am using postgresql, and am not sure if this is a bug, or if I have missed something simple.
Thanks,
In Rails 4 all of the attributes are mass assigned by default so you don't need to use attr_accesible. Now you have to state which ones you want to protect. you would write it like this
attr_protected :admin
The error that you are getting gets raised when password_digest is blank. It probably has something to do with your attr_accesor:. Can I see your view?
I had exact the same problem but that part here solved my problem. The password_confirmation could not be stored because it was not a permit parameter
def user_params
params.require(:user).permit(:name, :password_digest, :password, :password_confirmation)
end
I did it in rails 4:
for use "has_secure_password" you have to use the parameter password_digest
Schema Information = name :string, password_digest :string
class User < ActiveRecord::Base
has_secure_password
validates :name, presence: true, uniqueness: true
validates_presence_of :password, :on => :create
end
and the controller
def user_params
params.require(:user).permit(:name, :password_digest,
:password, :password_confirmation)
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_url, notice: "User #{#user.name} was successfully created.'"}
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end