Despite all the questions and solutions I've read so far, nothing has worked for me yet here showing a signed in user profile page with devise. I've tried a number of things in my routes as well. My error is currently shows - "Couldn't find User without an ID". I'm a newbie with devise so while I like it, it has been a bit frustrating. Any help would be appreciated. Still very much in the learning stages. Thanks.
class UsersController < ApplicationController
before_filter :authenticate_user!, :except => [:show]
after_action :verify_authorized
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
Routes
Rails.application.routes.draw do
devise_for :users
devise_scope :user do
get 'users/sign_in' => 'devise/sessions#new'
get 'users/sign_out' => 'devise/sessions#destroy'
end
resources :user
match 'users/show', to: 'users#show', via: 'get'
root to: 'pages#home'
end
match 'users/show', to: 'users#show', via: 'get'
This line suggests that it will route the url users/show to your controller#action users#show. And inside your controller you are looking for the user with the id of params[:id]. Except, you are never passing in an id parameter. Typically, the id is passed in the url (ie users/show/:id). That is the cause of your error.
The solution depends on what you are trying to accomplish. You could either pass the id through the url, or if you want the users/show to show only the logged in user profile, I would add a controller action (called profile for example) and then set/get a session variable with the user id and redirect to user's view.
Related
Problem: I am using the after_sign_up_path_for(resource) method provided by Devise but I cannot seem to get the User redirected to the specified page after they sign up. Currently I receive a Template Missing error with the url being www.localhost:3000/users. Ideally, I would like to have the User redirected to www.localhost:3000/subscribers/new
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
#user = User.new(sign_up_params)
#domain = #user.email.split('#').last
#company = Company.find_by_domain(#domain)
#user.company_id = #company.id
if #user.save
after_sign_up_path_for(#user)
else
render 'new'
end
end
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :participation_code, :company_id)
end
protected
def after_sign_up_path_for(resource)
"/subscribers/new"
end
end
routes.rb
...
devise_for :users, controllers: { registrations: "registrations" }
...
Error Log I Get:
Template is Missing
Missing template registrations/create, devise/registrations/create,
devise/create, application/create with {:locale=>[:en], :formats=
[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby,
:coffee, :arb, :haml, :jbuilder]}.
UPDATE: I have tried moving the after_sign_up_path_for(resource) to the ApplicationController but still got the same result.
Very interesting. I simply changed
def create
...
end
to...
def update
...
end
Now everything works perfectly and my after_sign_up_path_for(resource) method is recognized.
Add below method in your application controller
Devise: Where to redirect users once they have logged in
def after_sign_up_path_for(resource)
"http://www.google.com" # <- Path you want to redirect the user to.
end
Maybe it worth to put redirect_method in after_action : create callback?
you dont need to create controller you can redirect user to specific path by defining
def after_sign_in_path_for(resource)
your_path
end
remember use after_sign_in_path_for not after_sign_up_path_for
I am using cancancan authentication mechanism in my rails application. I want only those users who are owner of their own posts and comments t be edited and deleted , and admin to manage all the things. my admin ability is working fine but others are not working. here is my ability.rb file
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.admin?
can :manage, :all
else
can :read, :all
can :create, Topic
can :update, Topic do |topic|
topic.try(:user) == user
end
can :delete, Topic do |topic|
topic.try(:user) == user
end
can :delete, Comment do |comment|
comment.try(:user) == user
end
# can :manage, Comment, :task => { :user_id => user.id }
can :update, Comment do |comment|
comment.try(:user) == user
end
end
end
end
what should i do in order to work it properly. its working properly for topics but not for comments
this is the line in my topics contrller
load_and_authorize_resource :topic
If it is working for topics there must be something wrong with your comments model. Does the comments table have a user_id column that stores the author of the comment or any other way to check its ownership? It might be that comment.try(:user) returns nil and it then fails to give the users the right permissions.
So I have a Post model. My posts can be either published or unpublished. I am using Rolify, CanCanCan and Devise.
What I want to happen is my :admin users, should be able to view the Post#Show action of all Posts, but my :member or guest users (i.e. non-logged in) should only ever be able to see Post.published posts.
My ability.rb looks like this:
if user.has_role? :admin
can :manage, :all
#Member
elsif user.has_role? :member
can :read, :all
can :create, Post
can :status, Post
can :update, Post do |post|
post.try(:user) == user
end
#Guest
else
can :read, :all
can :create, Post
can :status, Post
end
I tried doing this, for both :member and Guest, but it gave me an endless redirect loop on my Post#Index page - which is my root_path:
can :read, Post.published
Where Post.published returns an array of all the posts with publication_status = "published".
This is how I declared that on my Post.rb:
enum publication_status: [ :unpublished, :published ]
How do I achieve this?
I figured this out.
In my Post#Show, I simply did this:
def show
if current_user and current_user.has_role? :admin or :editor
#post = Post.find(params[:id])
else
#post = Post.published.find(params[:id])
end
end
That works like a charm.
If someone has a more elegant way, say inside the ability.rb I would love to know.
I tried many permutations using blocks and all this jazz in the ability.rb but nothing worked.
I also had to remove the default added set_post method in my controller, because cancancan already loads_and_authorize the resource.
Try the following
#Member
elsif user.has_role? :member
can :read, Post, publication_status: :published
can :create, Post
can :status, Post
can :update, Post, user_id: user.id
# same goes for the Guest user
this will work given that you have a column named publication_status in the Post model
I'm using omniauth-twitter to create and authenticate users in my rails app and I'm successfully getting everything I need from Twitter, avatar, username, description, etc. But I'd like to let users add a custom string to display on their account page.
I added a column to the User table and ran the migration. The column is there.
I can't seem to get the update form to work, however. I'm not seeing errors. I just get a page refresh. Since I didn't have an existing form or controller methods to begin with I added them manually.
Here's my Users controller (I'm using friendly-id, hope that doesn't throw you.)
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.friendly.find(params[:id])if params[:id]
end
def edit
#user = User.friendly.find(params[:id])if params[:id]
end
def update
#user = User.friendly.find(params[:id])if params[:id]
end
private
def user_params
params.require(:user).permit(:custom_text)
end
end
I'm not sure if I need both edit and update methods here, but I thought I'd try.
Here's my form (SLIM Template) which I include on the user's show page:
= form_for #user do |f|
= f.text_field :custom_text
= f.submit
The submit button works, as it were, but nothing is updated.
I'm pretty sure I'm just overlooking something painfully obvious.
I can't seem to get the update form to work, however. I'm not seeing errors.
Nothing is getting updated because you are not updating anything in the first place. As per the current code in the update action, its just selecting the record to be updated from the database but doing nothing with it.
def update
#user = User.friendly.find(params[:id])if params[:id] ## Simply selecting
end
In order to update the fetched record, you should call either update or update_attributes method on the instance of User model passing the changed attributes values to the method.
SOLUTION:
Use the following updated code in your UsersController. I have also DRYed up the code little bit by adding a before_action callback named set_user. The set_user method will be called every time before performing the actions such as show, edit and update and will take care of setting the instance variable #user.
class UsersController < ApplicationController
## Adding a before action callback
before_action :set_user, only: [:show, :edit, :update]
def index
#users = User.all
end
def show
end
def edit
end
def update
if #user.update(user_params)
## Redirect to user show page on successful update
redirect_to #user, notice: 'User was successfully updated.'
else
## Render user edit page again upon failure to update
render action: 'edit'
end
end
private
def set_user
#user = User.friendly.find(params[:id]) if params[:id]
end
def user_params
params.require(:user).permit(:custom_text)
end
end
Have you tried changing your form code to set the multipart:true to allow file to be sent
Try this and then upload the photo
= form_for #user,html: {multipart: true} do |f|
= f.file_field :custom_photo
= f.submit
I might also be missing something painfully obvious or new in Rails 4, but it seems like you should actually be telling the DB to update your record:
def update
#user = User.friendly.find(params[:id]) if params[:id]
#user.update_attributes(user_params) if #user
end
private
def user_params
params.require(:user).permit(:custom_text)
end
Without this second line in the update action, it's just finding the record and not doing anything with it...
I created a starter app from RailsApps with the rails-devise-pundit example app. I am trying to write a user controller test because I plan to change some functionality and I want to make sure things still work. The pundit UserPolicy is not returning the correct value which is based on a role enum in the User class. The UserPolicy.index? method seen below is returning false when called from the first test in UsersControllerTest. Sorry there is a lot of code and detail here. I hope everyone can follow it.
Here's the failing test in UsersControllersTest. The response is a :redirect instead of :success.
require "test_helper"
class UsersControllerTest < ActionController::TestCase
def setup
#admin = users(:admin)
#admin.role = :admin
end
test "should get index page when authenticated as an admin" do
sign_in #admin
get :index
assert_response :success
end
...
end
Here's my user controller class just showing the index method where my problem is. authorize #users should call the UserPolicy.index? method.
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized, except: [:show]
def index
#users = User.all
authorize #users
end
...
end
My pundit user policy class. When I change the index? method so it returns true, the response in my UsersControllerTest is :success. So for some reason #user.admin? is not returning the correct value.
class UserPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
def index?
#user.admin?
end
...
end
What is even stranger is that I created a UserPolicyTest class and when I test calling index? from there, I get the correct response. This test works correctly:
require 'test_helper'
class UserPolicyTest < ActiveSupport::TestCase
def setup
#admin = users(:admin)
#admin.role = :admin
end
def test_index
policy = UserPolicy.new #admin, nil
assert policy.index?
end
end
Here is my User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
validates :name, presence: true
def set_default_role
self.role ||= :user
end
end
Here's my test fixture for an admin user:
admin:
email: admin#example.com
name: Mr Admin
role: admin
encrypted_password: $2a$10$PoBe1MvkoGJsjMVTEjKqgeBUp.xdfzWoiDjBzQhtLAj16NqIa2fOy
remember_created_at: nil
sign_in_count: 3
current_sign_in_at: 2014-01-02 08:31:23
last_sign_in_at: 2014-01-02 08:31:23
current_sign_in_ip: 127.0.0.1
last_sign_in_ip: 127.0.0.1
confirmation_token: nil
confirmed_at: 2014-01-02 08:31:23
confirmation_sent_at: 2014-01-02 08:30:59
created_at: 2014-01-02 08:30:59
updated_at: 2014-01-02 08:31:23
I found that setting the role in the fixture doesn't work. I'm guessing that's because of the after_initialize :set_default_role, :if => :new_record? line in my User model. If there's another reason or a better way to handle this, please let me know.
UPDATE: Maybe this is being caused by strong parameters. When I tried debugging my code with pry, I found that in the UsersControllerTest, after signing in, the admin user had a role of 2 which is correct. But when it got to User.Policy.index?, the role was 0. I may need to add the role field to the devise strong parameters. I saw something about how to do that a while back. It didn't look easy. If someone knows the answer before I get to it, please let me know.
After I changed the value of #admin.role in setup, I didn't save the user. After adding #admin.save to the setup method, the test passed.