Add an empty nested object to newly created user in Devise - ruby-on-rails

I have a User model in my Rails 5 app using Devise.
I also have a relationship: user belongs_to account.
Now, when I create a new user I want to automatically create a new empty account for it.
I already have my registrations controller and a method:
def create
super
end
I would like to do something like this:
def create
super
user.account.create()
end

Are you looking for something like that ?
In your model User.rb
after_create do
self.account = Account.create() if self.account.nil?
end

In devise controllers, user object is called as resource by default. So something like this will work:
def create
super
if resource.persisted? # user has been created with no errors
resource.account.create # assuming it will pass validations without arguments
end
end
Another way, as #julien-js mentioned, is to create account in after-create hook, but instead of after_create you can use after_commit(read more about the difference):
after_commit :create_account, on: :create
def create_account
self.account.create unless self.account.present?
end

Related

disable all logins except admin in ruby on rails

while doing admin work, i'd like to disable user logins --
is there some way to use devise for this -- I don't THINK this
is suitable for rolify -- because this is a temporary disablement --
thanks in advance for any help,
rick
Back-End
If you wanted to create a "maintenance" mode, you'll be best doing something like this:
#app/models/user.rb
class User < ActiveRecord::Base
end
#app/models/admin.rb
class Admin < User
def maintainance!
self.toggle! :maintainance
end
end
This will need a maintenance column in the users table, and you'll have to add a type column in the users table, too.
You could get away with keeping this in the User model, however, you'd need some conditions to determine whether the user is an admin. Since you didn't specify how you're differentiating, above is how we do it.
--
You'd be able to call it like this:
#app/controllers/users_controller.rb
class SettingsController < ApplicationController
before_action :authenticate_user!
def maintenance
current_user.maintenance! #-> toggles so you'll just be able to call this as you need.
end
end
#config/routes.rb
resources :settings, only: [] do
put :maintenance #-> url.com/settings/maintenance (considering current_user present)
end
This will allow you to set the "maintenance" mode through your user settings area. If you don't have one, you'll be able to use the above code to get it working.
Front-End
With the backend in place, you'll be able to then manage the front-end.
To do this, you'll need a helper to determine if any user has set the "maintenance" mode...
#app/helpers/application_helper.rb
class ApplicationHelper
def maintenance_mode?
Admin.exists? maintenance: true
end
end
This will allow you to use this helper to determine whether you should allow Devise to accept logins or not:
#app/views/devise/sessions/new.html.erb
<% unless maintenance_mode? %>
... devise form ...
<% end %>
The helper will execute a DB request, but keeping it in the devise areas only (IE it's not "site wide") should make it okay.
#app/controllers/devise/sessions_controller.rb
class SessionsController < Devise::SessionsController
before_action :check_maintenance
private
def check_maintenance
redirect_to root_path, notice: "Sorry, maintenance mode is in effect; no logins." if maintenance_mode?
end
end
This will prevent any controller-based actions from firing.
Finally, if you want to get rid of any logged-in users, you'll need to do something quirky, like resetting the sessions or something similar:
How can I reset all devise sessions so every user has to login again?
Devise force signout
Here's what I'd do:
1. Create a method for your User model. It could be something like active, or able_to_login.
2. Set this attribute to :boolean.
3. Use rails console. Use the console to set the active method to true or false, enabling or disabling your users to access your application:
user = User.all
user.each do |u|
u.active = false # or
u.able_to_login = false
u.save
end
I don't think this is the best method, but it should work without installing another gem or heavy code.
In your /models/user.rb add this method
def active_for_authentication?
super && is_admin?
end
def is_admin?
# returns true if user is admin
end
This is the "Devise way" of doing this :)

What's the best way to make a new model when overriding the Devise User Controller?

I'm trying to make it so that when a new User is created (through Devise), a new Household(essentially a group) model will be created if no previous Household model with that name exists.
pseudocode:
if Household.find(params[:household_name))
# allow current_user to join household
else
# create new Household model with User's household_name parameter
end
I've overwritten the base user controller from Devise::RegistrationsController with controllers/registerhousehold_controller.rb:
class RegisterhouseholdController < Devise::RegistrationsController
But I'm not sure how to implement the actual creation here. Any suggestions?
No changes in controller required as far as I see.
User.rb
after_create :create_or_join_to_household
def create_or_join_to_household
household = Household.find(params[:household_name])
if household.present?
self.join_to_household
else
Household.create(name: params[:household_name])
#or self.households.create(name: params[:household_name])
#if you have a household - user relation somehow
end
p.s.
join_to_household would be another method in your user model that will create a household_users relation.
Simple - use the before_create callback in the user model to build the object, then you'll be able to use it when you save:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :set_household, if: Proc.new {|user| user.household_id.present? }
private
def set_household
if house = Household.find(self.household_id)
#if it is set
else
#create a new houshold
end
end
end
I had to call custom method after successful sign up, on my previous task.
U also need something similar.
I'm not sure about overriding.
Try this in App. controller
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if Household.find(params[:household_name))
# allow current_user to join household
else
#create new Household model with User's household_name parameter
end
root_path
end
end
Check this

how to create a model after a devise registration

I have a user registration with an extra field called "company_name". After the user gets created, I want a Company instance to be created based on the extra field "company_name" and that user associated with the company. I've tried a few things like this:
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
company = Company.create(name: params[:company_name])
current_user.admin = true
current_user.company = company
current_user.save
end
def update
super
end
end
however, I don't have a current_user when trying to do the lines after I create the company. Is there a better way of doing this?
You can pass a block to the Devise controller's create that will give you the created user resource:
class RegistrationsController < Devise::RegistrationsController
CREATE_COMPANY_PARAMS = [:name]
def create
super do |created_user|
if created_user.id
company = Company.create! create_company_params
created_user.update! company_id: company.id
end
end
end
private
def create_company_params
params.require(:user).require(:company).permit(*CREATE_COMPANY_PARAMS)
end
end
There are some tough parts to handling this correctly though.
It seems that even if the user already exists, it will still call your block and pass you a user, but the user won't have an id assigned because the DB save failed. The if created_user.id check prevents a company from being created for an invalid user.
If the company already exists. The .create! will throw an exception which causes the controller to return an HTTP 422.
Utilizing the after_save callback in User model is probably suitable for this case:
# app/models/user.rb
class User < ActiveRecord::Base
...
# Execute this callback after an record is saved only on create
after_save :create_and_associate_company, on: :create
private:
def create_and_associate_company
company = self.companies.build
# Other necessary attributes assignments
company.save
end
end
Reference on other Active Record Callbacks.
You can access the newly created user using the resource variable
Here, I'm logging info only if the user was actually saved
class RegistrationsController < Devise::RegistrationsController
def create
super
if resource.persisted?
Rails.logger.info("Just created and saved #{resource}");
end
end
end

Rails devise before_destroy user

I wan't to notify the admin when a user deletes his account. My idea is to use the before_destroy validation to send an email from within the model.
This is probably better done in the controller but I don't want to subclass the devise controller because I think I could break something.
The problem is that the model can't access the current_user, which was my first thought about how to "get" the corresponding user account. But the model has to somehow know which user account is meant to be destroyed, right?, so there has to be some kind of variable being passed.
I've looked into the devise controller at github and it seems like everything is done using "resources".
https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb
I can't figure out the last part, how could I access the id / or user object?
Simple add a hook in your model and send the mail..
class User < ActiveRecord::Base
before_destroy :notify_admin
private
def notify_admin
YourMailer.user_destroyed(self).deliver
end
end
The "self" will be your current_user object..
Something like that could work:
class Users::RegistrationsController < Devise::RegistrationsController
def destroy
# You have access to current_user here
super
end
end

Accessing a variable declared in a controller from a model

I'm using the facebooker gem which creates a variable called facebook_session in the controller scope (meaning when I can call facebook_session.user.name from the userscontroller section its okay). However when I'm rewriting the full_name function (located in my model) i can't access the facebook_session variable.
You'll have to pass the value into your model at some point, then store it if you need to access it regularly.
Models aren't allowed to pull data from controllers -- it would break things in console view, unit testing and in a few other situations.
The simplest answer is something like this:
class User
attr_accessor :facebook_name
before_create :update_full_name
def calculated_full_name
facebook_name || "not sure"
end
def update_full_name
full_name ||= calculated_full_name
end
end
class UsersController
def create
#user = User.new params[:user]
#user.facebook_name = facebook_session.user.name
#user.save
end
end

Resources