I'm attempting to write out some unit tests for my Rails application using RSpec. I'm using Rails 4.0.0 and Rspec-Rails 2.14.6. My User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => Settings.authentication.providers.map { |i| i.to_sym }
validates :password, presence: true, allow_nil: true,
allow_blank: true, if: :password_required?
validates :email, presence: true
def password_required?
self.providers.empty?
end
has_many :providers
has_one :profile
end
And my (magical) spec:
require 'spec_helper'
describe User do
describe '.new' do
it 'can create a valid user with no providers' do
params = ActionController::Parameters.new(FactoryGirl.attributes_for :user)
u = User.create params.permit!
expect(u).to be_new_record
end
end
describe '.build_with_provider' do
it 'can create a valid user with a provider' do
puts ap(User)
provider = FactoryGirl.create :provider
oauth_data = FactoryGirl.attributes_for :oauth_data
u = User.build_from_provider_and_oauth_data provider, oauth_data
expect(u).to_not be_nil
expect(u).to be_new_record
expect(u).to be_valid
u.save
expect(u).to be_persisted
end
end
end
If I'm missing something, please let me know. As far as I know, using ActionController::Parameters is the new way of (white|black)listing parameters in Rails 4.
Related
I want to create profile automatically when user is created by devise signup Or by Omniauth-facebook. I have User Model (user.rb)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
has_many :listings, dependent: :destroy
has_many :bookings, dependent: :destroy
has_one :profile, dependent: :destroy
before_create :build_profile
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]
#Profile.where(:user_id => user.id).update_all(name: auth.info.name)
user.build_profile(name: auth.info.name)
end
end
end
But if I use before_create :build_profile and sign_in with Facebook then two profiles are created
and if I use user.build_profile(name: auth.info.name) in self.from_omniauth(auth) then if I sign_up with Devise ..no profile is created.
so where in this code I Build profile ?
My omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Faceboo") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
Following a tutorial for adding favorites to my existing project, whereby users can favorite property listings but can't get past the error below:
Routing error,uninitialized constant FavoriteRoomsController
favoriterooms_controller.rb
class FavoriteRoomsController < ApplicationController
before_action :set_room
def create
if Favorite.create(favorited: #room, user: current_user)
redirect_to #room, notice: 'Room has been favorited'
else
redirect_to #room, alert: 'Something went wrong...*sad panda*'
end
end
def destroy
Favorite.where(favorited_id: #room.id, user_id: current_user.id).first.destroy
redirect_to #room, notice: 'Room is no longer in favorites'
end
private
def set_room
#room = Room.find(params[:room_id] || params[:id])
end
end
room.rb
class Room < ActiveRecord::Base
belongs_to :user
has_many :photos
has_many :favorites
geocoded_by :address
after_validation :geocode, if: :address_changed?
validates :home_type, presence: true
validates :room_type, presence: true
validates :accommodate, presence: true
validates :bed_room, presence: true
validates :bath_room, presence: true
validates :listing_name, presence: true, length: {maximum: 50}
validates :summary, presence: true, length: {maximum: 500}
validates :address, presence: true
validates :lister_type, presence: true
validates :gender_type, presence: true
validates :occupation_type, presence: true
validates :move_in, presence: true
validates :term, presence: true
validates :term, presence: true
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable
validates :fullname, presence: true, length: {maximum: 50}
has_many :rooms
has_many :favorites
has_many :favorite_rooms, through: :favorites, source: :favorited, source_type: 'Room'
def self.from_omniauth(auth)
user = User.where(email: auth.info.email).first
if user
return user
else
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.fullname = auth.info.name
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.image = auth.info.image
user.password = Devise.friendly_token[0,20]
end
end
end
end
favorite.rb
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :favorited, polymorphic: true
end
routes.rb
Rails.application.routes.draw do
root 'pages#home'
devise_for :users,
:path => '',
:path_names => {:sign_in => 'login', :sign_out => 'logout', :edit => 'profile'},
:controllers => {:omniauth_callbacks => 'omniauth_callbacks',
:registrations => 'registrations'
}
resources :users, only: [:show]
resources :rooms
resources :photos
resources :conversations, only: [:index, :create] do
resources :messages, only: [:index, :create]
end
resources :favorite_rooms, only: [:create, :destroy]
end
roomshow.html.erb
favorites button link code added in my roomsshow.html.erb
<%- unless current_user.favorite_rooms.exists?(id: #room.id) -%>
<%= link_to 'Add to favorites', favorite_rooms_path(room_id: #room), method: :post %>
<%- else -%>
<%= link_to 'Remove from favorites', favorite_room_path(#room), method: :delete %>
<%- end -%>
favorite_rooms_path POST /favorite_rooms(.:format) favorite_rooms#create
favorite_room_path DELETE /favorite_rooms/:id(.:format) favorite_rooms
Screenshot of error message with full trace
I've watched various tututorials and followed numerous suggestions but dont seem to be able to solve the issue by myself.
Routing error,uninitialized constant FavoriteRoomsController
Rails follow naming conventions very strictly and for a good reason. It expects the file names to be in a snake case. So you should change the file name favoriterooms_controller.rb to favorite_rooms_controller.rb
I have a problem with devise_invitable 1.4.0 and strong parameters when I add additional custom parameters and I really hope somebody can guide me in the right direction. I am able to send invitations, but when an invited user accepts an invitation and enters a desired username, maiden name, password and confirmed password, the following error is shown:
Processing by Users::InvitationsController#update as HTML
Unpermitted parameters: username, name
The user is created as expected, but the 'username' and 'name' columns in the database are empty.
I have tried all the suggestions I could find for related issues, but none of the worked. I have noticed that if I change the app/controllers/users/invitations_controller.rb file in any way (eg inserting a blank space on an empty line) without restarting the webserver (Thin) the problem disappears - but the problem reappears when the webserver is restarted.
The various relevant files look like this:
routes.rb:
Rails.application.routes.draw do
root to: 'visitors#index'
#Tell rails to use the Devise controllers that were generated with this command:
# > rails generate devise:controllers users
#Using these generated controllers allows us to overwrite anything in the deault controllers.
devise_for :users, :path_names => {:sign_in => 'login', :sign_out => 'logout'}, controllers: {confirmations: "users/confirmations", passwords: "users/passwords", registrations: "users/registrations", sessions: "users/sessions", unlocks: "users/unlocks", :invitations => 'users/invitations'}
resources :users
end
config/initializers/devise.rb
Devise.setup do |config|
...
...
config.scoped_views = true
config.authentication_keys = [ :username ]
...
...
end
app/controllers/users/invitations_controller.rb:
class Users::InvitationsController < Devise::InvitationsController
private
# this is called when creating invitation
# should return an instance of resource class
def invite_resource
## skip sending emails on invite
resource_class.invite!(invite_params, current_inviter) do |u|
u.tenant = current_inviter.tenant
u.role = :user
end
end
def after_invite_path_for(resource)
users_path
end
def resource_params
params.permit(user: [:name, :email,:invitation_token, :username])[:user]
end
end
app/controllers/application_controller.rb
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
around_filter :scope_current_tenant
before_filter :configure_permitted_parameters, if: :devise_controller?
if Rails.env.development?
# https://github.com/RailsApps/rails-devise-pundit/issues/10
include Pundit
# https://github.com/elabs/pundit#ensuring-policies-are-used
# after_action :verify_authorized, except: :index
# after_action :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
end
#############################################################################
private
#############################################################################
if Rails.env.development?
def user_not_authorized
flash[:alert] = "Access denied." # TODO: make sure this isn't hard coded English.
redirect_to (request.referrer || root_path) # Send them back to them page they came from, or to the root page.
end
end
def current_tenant
#current_tenant ||= current_user.tenant unless current_user.nil?
end
helper_method :current_tenant
def scope_current_tenant(&block)
if current_tenant.nil?
scope_visitor_schema
yield
else
current_tenant.scope_schema("public", &block)
end
end
def scope_visitor_schema()
original_search_path = ActiveRecord::Base.connection.schema_search_path
ActiveRecord::Base.connection.schema_search_path = 'public'
ensure
ActiveRecord::Base.connection.schema_search_path = original_search_path
end
#############################################################################
protected
#############################################################################
def configure_permitted_parameters
# Only add some parameters
devise_parameter_sanitizer.for(:account_update).concat [:name, :email]
# Override accepted parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:name, :username, :password, :password_confirmation,
:invitation_token)
end
end
end
app/models/user.rb:
class User < ActiveRecord::Base
enum role: [:user, :admin]
after_initialize :create_tenant, :if => :new_record?
belongs_to :tenant
# has_many :invitations, :class_name => self.to_s, :as => :invited_by
scope :unconfirmed, -> { where(confirmed_at: nil) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
# validate :username, presence: true, uniqueness: true, format: { with: /[a-zA-Z0-9]{4,20}/ }
def displayed_username
username.nil? ? "N/A" : username
end
def displayed_name
name.nil? ? "N/A" : name.titleize
end
def create_tenant
#The create_tenant method will also be called when looking up a user,
#so the following ensures a tenant is only created if it does not already
#exist - and the user has not been invited and assigned to an existing tenant:
if self.tenant.nil?
#Set role to 'admin' if a tenant is about to be created:
self.role = :admin #if self.tenant.nil?
self.tenant = Tenant.new
end
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
end
I finally found a fix, which was to place the parameter sanitizer directly in users/invitations_controller.rb instead of the application_controller.rb.
class Users::InvitationsController < Devise::InvitationsController
before_filter :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:username, :name, :email, :password, :password_confirmation, :invitation_token)
end
end
end
I have multiple possible user names in my user model i.e. a user can login with either one.
This is based on the article here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address
The actual login is working fine with either code, but following the example I cannot get the reset password page to fire properly.
This is a multi-tenant scenario so there is also an account_id to take into consideration.
I have found many other people with the same issue - but I don't know what the right way to resolve this is, if I permit! in the relevant method (below) then reset password functions but standard login no longer works.
I am getting a ForbiddenAttributesError as follows:
ActiveModel::ForbiddenAttributesError - ActiveModel::ForbiddenAttributesError:
activemodel (4.1.6) lib/active_model/forbidden_attributes_protection.rb:21:in `sanitize_for_mass_assignment'
activerecord (4.1.6) lib/active_record/relation/query_methods.rb:568:in `where!'
activerecord (4.1.6) lib/active_record/relation/query_methods.rb:559:in `where'
activerecord (4.1.6) lib/active_record/querying.rb:10:in `where'
app/models/user.rb:356:in `find_first_by_auth_conditions'
devise (3.4.1) lib/devise/models/authenticatable.rb:266:in `find_or_initialize_with_errors'
devise (3.4.1) lib/devise/models/recoverable.rb:115:in `send_reset_password_instructions'
devise (3.4.1) app/controllers/devise/passwords_controller.rb:13:in `create'
User.rb
devise :database_authenticatable, :recoverable, :rememberable, :trackable, authentication_keys: [:login, :account_id], reset_password_keys: [:login, :account_id]
....
def self.find_first_by_auth_conditions(warden_conditions)
# IF i switch these 2 lines then reset password works BUT normal login doesn't
#conditions = warden_conditions.dup.permit!
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(remote_id) = :value OR lower(other_id) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller?
...
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:remote_id, :email, :password, :password_confirmation, :remember_me) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :remote_id, :email, :password, :remember_me) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:remote_id, :email, :password, :password_confirmation, :current_password) }
end
Using devise 3.4.1, rails 4.1.6
I want to when a user click the confirmation link after that user should be logged in and redirect to some specific path. I don't have any idea.How can do this?
Now I'm click my account confimation and redirect to sign in page but i want to logged in and redirect to root page
This is my confirmation_instructions.html.erb
<p>Welcome <%= #email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></p>
This is User.rb
class User < ActiveRecord::Base
has_paper_trail
acts_as_messageable
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_one :day_care
has_many :actvities, dependent: :destroy
has_one :director, dependent: :destroy
has_one :assistant_director, dependent: :destroy
has_one :teacher, dependent: :destroy
accepts_nested_attributes_for :day_care
accepts_nested_attributes_for :director
accepts_nested_attributes_for :assistant_director
accepts_nested_attributes_for :teacher
def confirm!
super
if confirmed_at_changed? and confirmed_at_was.nil?
UserMailer.welcome_email(self).deliver if self.confirmed_at_changed?
end
end
#def send_welcome_email
# UserMailer.welcome_email(self).deliver if self.confirmed_at_changed?
#end
def mailboxer_email
"#{self.email}"
end
def name
name = ""
if self.has_role? "director"
name = self.director.name
elsif self.has_role? "assistant_director"
name = self.assistant_director.name
elsif self.has_role? "teacher"
name = self.teacher.name
end
name
end
end
Thanks ur help!
In your controller, whichever action you want to make this happen
#user = User.find(params[:id])
sign_in(:user, #user)
redirect_to some_path