Manually instantiating devise user models - ruby-on-rails

This question is related to one that I asked a couple years ago:
Instantiating Devise user models manually using contents of params hash
I am not sure if this is a rails 4 issue, but I am finding that I cannot manually instantiate devise user in my controller code. This used to work in rails 3.
class RegistrationsController < Devise::RegistrationsController
...
def schema_test
#user = User.new(:email => 'jhw#ausd.k12.edu', :password => 'asdf123', :password_confirmation => 'asdf123')
#user.save
end
...
end
This is the devise-specific part of my routes.rb:
devise_for :users, :controllers => {:registrations => "registrations"}
devise_scope :user do
get '/schema_test', to: 'registrations#schema_test'
end
When I call schema_test, I am finding that the user object is not getting saved to the database. Does anyone have any suggestions?

Best practice is to check the behavior in the rails console before you integrate it into your rails application.
The simplest way to figure out your issue is to read the error messages that devise returns. This could be caused by not meeting the password requirements and/or that the email already exists in your database.
From the root of your application, run rails c.
#user = User.new(:email => 'jhw#ausd.k12.edu', :password => 'asdf123', :password_confirmation => 'asdf123')
# Check if the user object is valid
#user.valid?
# If it comes back false, read the error messages
#user.errors.messages
=> {:password=>["is too short (minimum is 8 characters)"]}
In your example, the user is not being saved because the password is too short.

Related

Rails + devise sign_in error

Devise throws:
"NoMethodError (undefined method `login' for #<ActionDispatch::Request:0x00000004e42d80>):
"
every time I try to log in.
In this application "login" field is used as authentication key:
/config/initializers/devise.rb:
config.authentication_keys = [ :login ]
In session_controller.rb I used before_filter:
def configure_permitted_parameters
devise_parameter_sanitizer.for(:user) { |u| u.permit(:login, :password) }
end
And my routes.rb:
devise_for :users, :controllers => { :sessions => 'sessions', :registrations => 'registrations', :invitations => 'users/invitations'}
This problem appeared after upgrade from Rails 3 to Rails 4.
Can someone explain to me, what I'm doing wrong?
UPDATE
My bad. Found wrong parameter in devise initializer, set by my co-worker.
Anyway i have error message:
NameError (undefined local variable or method `invitation_token' for #<User:0x0000000286c750>):
app/controllers/sessions_controller.rb:6:in `create'
sessions#create:
def create
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
render :json => { :user_id => resource.id }, :status => :created
end
UPDATE
Crap. My co-worker also changed database.yml to another DB. So this DB was not migrated to last state =. After rake db:migrate all works fine. Thanks to all.
The underlying issue here is generally that devise's invitable code is generated by an second step in your devise work flow, a generator that makes a second migration:
$ rails g devise_invitable:install
$ rails g devise_invitable User (where User is my Model)
$ rake db:migrate
What you need to check for is if both migrations are in sync (in my case I reran the user migration but NOT the invitable migration and thus my user table was incorrect).
According to this link, you should create a login virtual attribute in the User model.
#Virtual attribute for authenticating by either username or email
#This is in addition to a real persisted field like 'username'
attr_accessor :login
Also add login to attr_accessible for rails 3
attr_accessible :login
You may also need to overwrite Devise's find_for_database_authentication method in User model
(assuming it is activerecord)
# app/models/user.rb
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
You may need to modify config/initializers/devise.rb to have
config.reset_password_keys = [ :login ]
config.confirmation_keys = [ :login ]

Rails 3.2, Mass Assignment, Dynamic Roles?

I have a Rails app with a user model that contains an admin attribute. It's locked down using attr_accessible. My model looks like this:
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation, :admin, :as => :admin
And here's what my update method in my users controller looks like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => current_user_role.to_sym)
flash[:notice] = "Profile updated"
redirect_to edit_user_url(#user)
else
render 'edit'
end
end
I have a helper method in my application controller that passes back the role as a string:
def current_user_role
#current_user_role ||= current_user.admin? ? "admin" : "default"
end
helper_method :current_user_role
I've also set config.active_record.whitelist_attributes = true in config/application.rb.
I've verified that the current_user_role method is returning the proper value based on the current user's admin status. Rails isn't throwing a mass-assignment error. But when I try to update a user's admin status while logged in as an admin, Rails performs the update and silently ignores the admin attribute. Pulling up the user's record in the Rails console shows that the record hasn't been modified.
I have a feeling there's a Ruby- or Rails-specific issue at play that I'm not aware of. I can't locate any info on making the role dynamic. The best I could find was this.
There was an errant attr_accessor :admin in my model that was left in from a prior attempt at getting this to work. I overlooked it. Removing it fixed it.
So, the upshot is that this is a pretty simple way to get dynamic roles working in Rails 3.2.
Looks like it could be a bug in Rails 3.2
https://github.com/stffn/declarative_authorization/issues/127

NoMethodError when I push a object into a Doc with mongoid

I'm having this problem, I tried a lot of differents aproachs but
everytime it falls in that error.
Enviroment:
Rails 3.0.5
Mongoid 2.0.1
class User
include Mongoid::Document
field :name
has_and_belongs_to_many :companies
end
class Company
include Mongoid::Document
field :name
has_and_belongs_to_many :users
end
In my UserController method Create a I do something like this:
#user = User.where(:email => params[:user][:email])
if #user.count > 0
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
else
#user = User.create(:name => params[:user][:name],
:email => params[:user][:email],
:password => "123456")
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
end
When the user dont exist works great.
But if the user is already in the DB, fall a error.
NoMethodError in UserController#create
undefined method `companies' for #<Array:0x10679f638>
But after all it pushes the object into the document.
I don't know if I'm missing something.
If someone know how to solve this ... will be great.
Thanks in advance.
Try this:
#user = User.where(:email => params[:user][:email]).first
On a side note, you may also want to push some of this code into one of your models, either the User or Company model, so that in your controller you would only have one call such as:
#company.add_user(#user)
The implementation details of adding a user would then be encapsulated in your model.
You may also want to consider embedding the two calls to ActiveRecord::Base#save into a single transaction to avoid ending up with inconsistent data in your database.

How to set up routes to show additional information in the URL using namespaces?

I am running Ruby on Rails 3 and I would like to set up my routes to show additional information in the URL using namespaces.
In the routes.rb file I have:
namespace "users" do
resources :account
end
So, the URL to show an account page is:
http://<site_name>/users/accounts/1
I would like to rewrite/redirect that URL as/to
http://<site_name>/user/1/Test_Username
where "Test_username" is the username of the user. Also, I would like to redirect all URLs like
# "Not_real_Test_username" is a bad entered username of the user.
http://<site_name>/users/accounts/1/Not_real_Test_username
to the above.
At this time I solved part of my issuelike this:
scope :module => "users" do
match 'user/:id' => "accounts#show"
end
My apologies for not answering your question (#zetetic has done that well enough), but the best practice here is to stay within the RESTful-style Rails URL scheme except for rare exceptions. The way most people make pretty URLs in this way is to use a hyphen, e.g.:
/accounts/1-username
This does not require any routing changes. Simply implement:
class Account < ActiveRecord::Base
def to_param
"#{self.id}-#{self.username}"
end
end
And handle the extra string data in your finds by calling to_i.
class AccountController < ApplicationController
def show
#account = Account.find(params[:id].to_i)
end
end
When you do link_to 'Your Account', account_path(#account), Rails will automatically produce the pretty URL.
It's probably best to do this in the controller, since you need to retrieve the account to get the username:
#account = Account.find(params[:id])
if #account && #account.username
redirect_to("/user/#{#account.id}/#{#account.username}")
return
end
As to the second issue, you can capture the remaining parameter by defining it in the route:
get "/users/accounts/:id(/:other)" => "users/accounts#show"
This maps like so:
/users/accounts/1/something # => {:id => "1", :other => "something"}
/users/accounts/1 # => {:id => "1"}
And you can simply ignore the :other key in the controller.

How to manually create a new user and user session in Devise?

I have a form where I collect a lot of information in Rails. Part of this form is fields for a new user to register. Since Devise has controllers/actions specifically to create a new user, I don't know how to programmatically create a user in an entirely different action that also creates another record. I really can't have the user registration form separate. I can't figure out how to create a user, and then log the user in, like I could easily do in Authlogic.
I have used both Authlogic and Devise, and think each has their strengths and weaknesses. With Devise, I love how quick it is to "get going" with a new project, but customizing it seems to be a pain. Authlogic had so many problems with Rails 3 a while back, that I switched to Devise. I'm now working on a new project and get to start from scratch.
So I think there are 2 potential answers to this question: (a) how to do this in Devise, or (b) why I should just switch to Authlogic with Rails 3 instead.
You can create a new Devise user simply by creating a new user model (see https://github.com/plataformatec/devise/wiki/How-To:-Manage-users-through-a-CRUD-interface)
#user = User.new(:email => 'test#example.com', :password => 'password', :password_confirmation => 'password')
#user.save
To sign in your newly created user, use sign_in #user
Saving the newly created user before sign_in will fail because devise has not yet filled the required fields for User object yet. So David's code will work for the current page, but the next page won't get the signed in user, since the user is only saved after the session is set. This happens when I use Mongoid, and I don't know if it is a problem specific to Mongodb.
To address this problem, I have a very imperfect solution to call sign_in twice. The first sign_in will save the user. It just works but people can improve it certainly.
#user = User.new(:email => 'test#example.com',
:password => 'password',
:password_confirmation => 'password')
# This will save the user in db with fields for devise
sign_in #user
# :bypass is set to ignore devise related callbacks and only save the
# user into session.
sign_in #user, :bypass => true
For new people seeing this question...
A simple way to do this is in your
config/routes.rb
you should have a line like the following :
devise_for :users
so, you just have to add a path prefix that devise will use:
devise_for :users, :path_prefix =>'auth'
Hope it helps!

Resources