I edited devise's RegistrationsController::create method to modify slightly the behaviour. What I'm trying to do is that if there are no admin users in the database, the one that first signs up is going to be assigned the admin role, else it will be a regular user.
However, the role, though assigned correctly to the object (tested), it's not being persisted to the database.
Model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :role
Roles = [ :admin, :default ]
def is? requested_role
self.role == requested_role.to_s
end
def self.admin_role
return Roles[0]
end
def self.default_role
return Roles[1]
end
end
Modified devise method:
def create
build_resource(sign_up_params)
admin_user = User.find_by_role(User.admin_role)
if admin_user.nil?
resource.role = User.admin_role
else
resource.role = User.default_role
end
# here puts resource.role shows what's expected is indeed being assigned to the object
if resource.save
...
end
end
Why isn't the role being stored in the database? Why is it NULL?
You don't need the attr_accessor for :role if you have this defined as a column on your table. ActiveRecord gives you the database backed accessors just by having the relevant column defined in the relevant table.
Your attr_accessor will be overriding these and preventing them from persisting your changes to the database.
Related
My rails app has a User model and a Role model. Each User belongs to one role and each role has many users. There three methods defined in the user model to check the role of that user def admin?, def user?, and def expert?.
The User class:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
validates_presence_of :name
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
before_save :assign_role
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role
has_many :items
belongs_to :organization
has_many :expertkeywordmodels
has_many :keywords, through: :expertkeywordmodels
def assign_role
self.role = Role.find_by name: "Admin" if self.role.nil?
end
def self.with_role(role)
my_role = Role.find_by_name(role)
where(:role => my_role)
end
def admin?
self.role.name == "Admin"
end
def user?
self.role.name == "User"
end
def expert?
self.role.name == "Expert"
end
end
The Role class:
class Role < ActiveRecord::Base
has_many :users
end
I am trying to create a collection_select only with users that have expert role. Something like:
<%= collection_select(:keyword, :user_ids, User.where('expert?'), :id, :name, {prompt: true}, {:multiple => true}) %>
But it does not recognize expert? as a method. I was wondering if anyone knows how can I perform this query.
I am sorry if this is a naive question as I am new to rails.
Thanks,
Amir
User.where('expert?') doesn't really makes sense for the database, because it would translate to SQL like:
SELECT * FROM users WHERE expert?;
And obviously expert? isn't a valid SQL expression. expert? is only available in the context of your code.
Instead you need to write that logic in a way that translates to valid SQL and makes sense in the context of your database schema. My guess is that the following might work:
User.joins(:role).where(roles: { name: 'Expert'})
You might want to define a scope in your User model, like this:
scope :experts, -> { joins(:role).where(roles: { name: 'Expert'}) }
Than User.experts would return all users that have the expert role.
Not for nothing, but you have three methods in your user model that all set the same field, just differently.
def role(role_type)
self.role.name = role_type
end
To get your desired require to work properly - you can either write a scope or a method.
def get_roles(role_type)
User.role.name = role_type
end
Rail Guides are always extremely helpful. http://guides.rubyonrails.org/active_record_querying.html#passing-in-arguments
I am facing an error while trying to link the :username in my User table and my Room table. I made a custom auth with devise and added :username.
I would like the username to be the link between the User table from devise and my Room table.
I am trying to build this app to recreate a kind of airbnb but mainly as an exercise as I started programming in ruby few months ago.
I get the error:
ActiveRecord::RecordNotFound in RoomsController#new
Couldn't find User without an ID
line #19 #room.username = User.find(params[:username])
Thank you very much for your help. I am stuck in here for hours now :-(
rooms_controller
def new
#room = Room.new
#room.username = User.find(params[:username]) #error seems to come from here
end
routes.rb
Rails.application.routes.draw do
devise_for :users
get "home/info"
root :to => "home#info"
resources :rooms
resources :users do
resources :rooms
end
room.rb
class Room < ActiveRecord::Base
mount_uploader :photo, PictureUploader
belongs_to :user
validates_presence_of :username, :location, :description, :capacity, :price_day, :photo
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
validates_presence_of :username
validates_uniqueness_of :username
has_many :rooms
end
It should be something like this
def new
#room = Room.new
#room.username = User.find_by_username(params[:username])
end
If you just use .find() it expects the id of the user. Also see http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
There is a logic error in that you are saving #room.username to the User Object. You should be setting #room.user = User.find_by(...) OR #room.user_id = User.find_by(...).id
Active record will automagically make a method for you that will be #room.user.username if you want to get the username.
Now, here are the ways to find a user.
#room = Room.new #Then either of the following
#room.user = User.find_by(username: params[:username]) #Returns only one value
#room.user = User.find_by_username(params[:username]) #Returns only one value
#room.user = User.where(username: params[:username]) #Returns all users which meet condition.
As already mentioned in the answers, User.find() takes an ID. One thing to know is that all methods that start with .find for active record return a single record even if many meet the condition.
If you are having any problems still, then show us your Database Schema, and we can help further.
I found a solution. The Room is created with the the right :username and nothing is seen by the user.
In my Rooms controller, I kept
def new
#room = Room.new end
And I added this line in the "def create" part :
def create
#room = Room.new(room_params)
#room.username = current_user.username
Thank you for your help, this help me to understand better the relations in rails.
Have a nice day !
I'd like to add a new column to my users table, and populate it with a random token. I've got that working, but I'm curious as to why the first method I tried didn't work.
Here's the working version of my migration:
class AddTokenToUser < ActiveRecord::Migration
def up
add_column :users, :secure_token, :string
User.reset_column_information
User.all.each do |user|
user.secure_token = SecureRandom.urlsafe_base64(16)
user.save!
end
end
def down
remove_column :users, :secure_token
end
end
However, because I'm also going to want the code to generate this token on the User model, as I'd like to create a new token along with every new user, I thought I might be able to add the code as a method on the User object:
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 :snaps
def generate_new_secure_token
#secure_token = SecureRandom.urlsafe_base64(16)
end
end
...and then call it from the migration, to avoid repeating myself:
class AddTokenToUser < ActiveRecord::Migration
def up
add_column :users, :secure_token, :string
User.reset_column_information
User.all.each do |user|
# user.secure_token = SecureRandom.urlsafe_base64(16)
user.generate_new_secure_token
user.save!
end
end
def down
remove_column :users, :secure_token
end
end
However, with this method, I get no errors, but my secure_token column values all end up as NULL in the database, rather than having the a token in them.
I'm new to both Rails and Ruby, so I figure I'm missing something obvious, but I can't see what it is. Why isn't my method working, and is there a good way to move the token generation routine to the User class, so I don't need to have it in two different places?
Change your method to this
def generate_new_secure_token
self.secure_token = SecureRandom.urlsafe_base64(16)
end
#secure_token is an instance variable. Setting that doesn't change the attribute secure_token on the user object
I am very new to Ruby on Rails and have setup Devise for authentication. I have an existing model that I created prior to adding Devise. That model is called Article. I believe I have done everything I need to do in order to use the association=(associate) method that "assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object’s foreign key to the same value" which is exactly what I need to do.
Here is Devise's User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_one :article
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my Article model:
class Article < ActiveRecord::Base
belongs_to :user
validates :name, presence: true, length: { minimum: 5 }
end
Here is my migration:
class AddUserRefToArticles < ActiveRecord::Migration
def change
add_reference :articles, :user, index: true
end
end
Here is my create method from my articles_controller.rb:
def create
#article.user = current_user
#article = Article.new(post_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
And here is what happens when my controller runs:
NoMethodError in ArticlesController#create
undefined method `user=' for nil:NilClass
The highlighted code is #article.user = current_user. I was at least glad to know that I wrote that line of code similar to the popular answer in the Devise how to associate current user to post? question that I saw on here before posting this one.
I know I'm making a rookie mistake. What is it?
A new User instance needs to be assigned to #article before you can access any of the instance's attributes/associations. Try the following:
#article = Article.new(post_params) # Assign first
#article.user = current_user # Then access attributes/associations
The code posted in the question yields a nil:NilClassexception because the user association is being invoked on #article, which is nil because nothing has yet been assigned to it.
I am using devise omniauth in my rails application, here is the User class
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :encrypted_password, :fb_id
def set_facebook_info(info)
#facebook_info = info
end
def get_facebook_info
#facebook_info
end
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.find_by_email(data.email)
if not user.fb_id
user.fb_id = access_token.uid
user.save
end
user.set_facebook_info "whatever" <-- I tried here
user
else # Create a user with a stub password.
user = User.create(:email => data.email, :password => Devise.friendly_token[0,20], :fb_id => access_token.uid)
user
end
end
def self.new_with_session(params, session)
super.tap do |user|
user.set_facebook_info "whatever" # <-- I Tried here too
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["user_hash"]
user.email = data["email"]
end
end
end
end
I want to use set and get methods for keeping some facebook user information. but when I use current_user in view, it gives me no value of what I've set in my User class.
like in application.html.erb :
<span><%= current_user.get_facebook_info %></span>
returns an empty value
Does anybody has an idea about it ? It should be a common case. in general , how can we assign some non connected to DB attribute to current_user via devise ?
Thanks
Your #facebook_info attribute is only stored in memory, not persisted to the database. So on the next page load, Devise is going to load your model from the database again, and that is why the information is missing.
If you want to keep the Facebook info, you need to persist it to the database. Rails has some nice ways of storing hashes directly in a text column:
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
Source: http://api.rubyonrails.org/classes/ActiveRecord/Base.html