I'm creating a project that allows users to sign in and refer other users once they are signed in. The users have_many referrals and the referrals belongs_to users. I would like to have a page that, once a current user signs in, they can input a referral with the params: name, and referral_email. But this is a collaborative project, and the person who set up the user authentication used devise. Currently, I'm getting the following error:
undefined method `resource' for #<ReferralsController:0x007fb75735aa00>
User Controller:
class UsersController < ApplicationController
def new
end
end
Referral Controller:
class ReferralsController < ApplicationController
def new
#referral = Referral.new
end
def create
#referral = current_user.referrals.new(referral_params)
if #referral.save
redirect_to #referral
else
render 'new'
end
end
def show
#referrals = current_user.find_by_user_id(params[:user_id]).referrals.all
end
def index
#referrals = current_user.referrals.all
end
def edit
#referral = Referral.find(params[:id])
end
def update
#referral = Referral.find(params[:id])
if #referral.update(referral_params)
redirect_to #referral
else
render 'edit'
end
end
def destroy
#referral = Referral.find(params[:id])
#referral.destroy
redirect_to referral_path
end
private
def referral_params
params.require(:referral).permit(:name, :email, :user_id)
end
end
User Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_many :referrals
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
include RoleModel
# attr_accessible :email, :password, :password_confirmation, :school_name, :sport, :roles, :roles_mask
roles_attribute :roles_mask
roles :admin, :coach, :guest
end
Referral Model
class Referral < ActiveRecord::Base
belongs_to :user
end
User view:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :school_name %><br />
<%= f.text_field :school_name %></div>
<div><%= f.label :sport %><br />
<%= f.text_field :sport %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
Referral view:
<h2>Refer a coach </h2>
<%= form_for ([current_user, #referral]), :html => { :mulitpart => true } do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :referral %><br />
<%= f.text_field :referral %></div>
<div><%= f.label :referral_email %><br />
<%= f.email_field :referral, autofocus: true %></div>
<div><%= f.submit "Refer This Coach" %></div>
<% end %>
Routes:
Blog::Application.routes.draw do
devise_for :users
resources :users do
resources :referrals
end
get 'sessions/new'
get 'users/new'
resources :articles do
resources :comments
end
root 'welcome#index'
end
The problem is coming from the devise_error_messages! in your referrals view - remove it. Only the user resource is set up to work with devise, nothing else.
As stated in the comments, you should reconsider having referrals be a nested resource under users - there isn't really any advantage to do so the way you are using it, and lots of room for error. Keep it simple!
Related
I'm having issues with Devise. I'm trying to set up an authentication scheme in Rails and I'm getting an "invalid email or password" response when I try to sign in. This is right after creating a user, logging in, logging out, and then trying to log in again. I'm totally sure that the email/password match, so that's not the issue. I'm using Devise 4.4.3 and Rails 5.2.0. Help a guy out, please!
Users controller:
class UsersController < ApplicationController
before_action :authenticate_user!
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You've successfully signed up!"
else
flash[:alert] = "There was a problem signing up."
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Sessions controller:
class SessionsController < ApplicationController
before_action :authenticate_user!
def create
#user = User.authenticate(params[:email], params[:password])
if #user
flash[:notice] = "You've signed in."
session[:user_id] = #user.id
redirect_to "/"
else
flash[:alert] = "There was a problem signing in. Please try again."
redirect_to signin_path
end
end
end
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 :posts
has_many :projects
attr_accessor :password
validates_confirmation_of :password
validates :email, :presence => true, :uniqueness => true
before_save :encrypt_password
def encrypt_password
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password,password_salt)
end
def self.authenticate(email, password)
user = User.find_by "email = ?", email
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
end
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Routes.rb
Rails.application.routes.draw do
devise_for :users
root :to => 'homes#index'
resources :posts
resources :projects
resources :users
end
New session view:
<h2>Log in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Happy to provide more if needed. I'm still somewhat new to development so pardon me if it's a very obvious answer. Thanks!
In the user model try this
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
I have the following models in my application:
User:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_destroy { roles.clear }
has_many :users_roles
has_many :roles, through: :users_roles
accepts_nested_attributes_for :roles
#accepts_nested_attributes_for :user_extras
#id :confirmable is activated
def confirmation_required?
false
end
end
Role:
class Role < ApplicationRecord
#before_destroy { users.clear }
has_many :users, through: :users_roles
end
and UserRoles:
class UsersRoles < ApplicationRecord
belongs_to :user
belongs_to :role
end
I also have a form that will accept nested attributes, which looks something like this:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<%= f.fields_for :roles do |field| %>
<%= field.label :name %></div>
<%= field.text_field :name %>
<% end %>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
Also I have a controller that renders this form:
class Admin::AccountController < ApplicationController
before_action :authenticate_user!
layout "admin/layouts/dashboard"
def index
#resource = User.new
#resource2 = Role.new
#resource.roles << #resource2
p "==================="
p #resource
p #resource2
p "==================="
#resource.roles.build
# respond_to do |format|
# format.html
# format.json { render json: #resource }
# end
end
private
def admin_account_params
params.require(:resource).permit(:email, :password, :password_confirmation, roles_attributes: [ :name ])
end
end
The thing is when I visit the page, I get the following error:
uninitialized constant User::UsersRole
Extracted source (around line #9):
7
8
9
10
11
12
#resource = User.new
#resource2 = Role.new
#resource.roles << #resource2
p "==================="
I am not sure what is wrong with this at the moment. It will be great if you can spot the problem. Many thanks in advance.
Model classes by convention should be singular so the model class name should be...
class UsersRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
This will automatically map to a database table named users_roles
If you insist on using a plural class name then you need to specify the class name explicitly
has_many :users_roles, class_name: 'UsersRoles'
And you'll need this in both the User model and the Role model
i am having two tables employee , and company. i want to register the company name of an employee while Employee is registering by using the devise sign_up action. how to write devise parameter sanitiser method to save the company name while an employee is registering?
The trick is using accepts_nested_attributes_for and overriding the sign_up_params method on the registrations controller.
1. Set up the User model to accept attributes for company
class User < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
2. Override the default Registrations controller
# config/routes.rb
Rails.application.routes.draw do
# ...
devise_for :users, controllers: { registrations: "registrations" }
end
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
# GET /users/sign_up
def new
#user = User.new(company: Company.new)
end
def sign_up_params
params.require(:user).permit(
:email, :password, :password_confirmation,
company_attributes: [:name]
)
end
end
By digging into the source of Devise::RegistrationsController we can se that it calls build_resource(sign_up_params) which is about equivalent to User.new(sign_up_params). So we can simply add our own params handling by declaring our own sign_up_params method.
Note that in sign_up_paramswe use the built Rails 4 strong parameter handling instead of the Devise sanitized params which is a home rolled solution that predates Rails 4. It might be possible to do it with Devise sanitized params but there is no real reason unless you have to have backwards compatibility with Rails 3.
3. Customize the Registration form
To get the correct params hash we want the company name input to have the following name attribute:
user[company_attributes][name]
Rails has a nice helper called fields_for which lets us do that:
<%# app/views/devise/registrations/new.html.erb %>
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<fieldset>
<legend>Company</legend>
<%= f.fields_for :company do |c| %>
<div class="field">
<%= c.label :name %><br />
<%= c.text_field :name%>
</div>
<% end %>
</fieldset>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
Notice that fields_for gives us a new form builder instance in the block (c) which we create our nested inputs from.
4. Beer.
for adding custom info in sign_up form Try this :
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit( :company_name, :email,:password, :password_confirmation ) }
end
end
and change in your views/devise/registrations/new.html.erb.
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :company_name %><br />
<%= f.text_field :company_name %>
</div>
<% end %>
My rails app has a few cab operators and they have a few cabs, and they are related as follows:
class Operator < ActiveRecord::Base
has_many :cabs
end
I wish to add authentication system so as to create admins for each operator. I am using Devise. Since I need to create path as: operator/:operator_id/admins/sign_up, I generated the Admin model, as:
rails generate devise Admin
Then I modified my routes so as to obtain the above mentioned path:
scope "operators/:operator_id" do
devise_for :admins
end
Running rake routes shows that I am getting the required urls. I also modified the models:
class Admin < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :operator
end
class Operator < ActiveRecord::Base
has_many :admins
end
I also modified the devise/sessions/new.html.irb to include a hidden field for operator_id:
h2>Log in
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<% f.hidden_field :operator_id, :value => params[:operator_id] %>
<div><%= f.submit "Log in" %></div>
<% end %>
<%= render "devise/shared/links" %>
Finally, in order to authenticate admins before accessing the cab details, I added the following to the cabs_controller:
before_filter :authenticate_admin!
The problem is I am unable to submit the admin form. The form doesn't respond when I submit the admin credentials. Where am I going wrong?
You have to add :operator_id to Devise's permitted parameters
Take a look here
Essentially, you want to go with following in application_controller.rb:
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:email, :password, :password_confirmation, :operator_id) #add :operator_id
end
end
So I took the steps described in Profile model for Devise users?
I'm using rails 4 and ruby 1.9.3p448
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_attributes
has_one :profile
accepts_nested_attributes_for :profile
protected
def profile
super || build_profile
end
end
#
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :uname, :manager
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 %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<%= f.fields_for :profile do |profile_form| %>
<h2><%= profile_form.label :uname %></h2>
<p><%= profile_form.text_field :uname %></p>
<h2><%= profile_form.label :manager %></h2>
<p><%= profile_form.text_field :manager %></p>
<% end %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
#
Still can't save profile for user and this is the output:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kG7S9lF4+5hm+ggmKA4LZyXrN4hsPf01jGQvKxgzGGI=", "user"=>{"email"=>"test1#test.test", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "profile_attributes"=>{"uname"=>"1", "manager"=>"1"}}, "commit"=>"Sign up"} **Unpermitted parameters: profile_attributes**
Shoud I do anything in Profiles controller?
Thanks in advance
I found the solution!
In my users/registrations_controller.rb, I had to add the following
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {|u|
u.permit(:email, :password, :password_confirmation, :remember_me,
profile_attributes: [:uname, :manager])}
end
and it works just fine!
Rails 4 has stopped using attr_accessible and has started using strong parameters -http://api.rubyonrails.org/classes/ActionController/StrongParameters.html
You'll need to add your nested attributes for profile into the permitted attributes in the user controller.