how to create a model after a devise registration - ruby-on-rails

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

Related

Add an empty nested object to newly created user in Devise

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

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 get Devise's current_user in ActiveRecord callback in Rails?

I'm using Devise and Rails 3.2.16. I want to automatically insert who created a record and who updated a record. So I have something like this in models:
before_create :insert_created_by
before_update :insert_updated_by
private
def insert_created_by
self.created_by_id = current_user.id
end
def insert_updated_by
self.updated_by_id = current_user.id
end
Problem is that I get the error undefined local variable or method 'current_user' because current_user is not visible in a callback. How can I automatically insert who created and updated this record?
If there's an easy way to do it in Rails 4.x I'll make the migration.
Editing #HarsHarl's answer would probably have made more sense since this answer is very much similar.
With the Thread.current[:current_user] approach, you would have to make this call to set the User for every request. You've said that you don't like the idea of setting a variable for every single request that is only used so seldom; you could chose to use skip_before_filter to skip setting the User or instead of placing the before_filter in the ApplicationController set it in the controllers where you need the current_user.
A modular approach would be to move the setting of created_by_id and updated_by_id to a concern and include it in models you need to use.
Auditable module:
# app/models/concerns/auditable.rb
module Auditable
extend ActiveSupport::Concern
included do
# Assigns created_by_id and updated_by_id upon included Class initialization
after_initialize :add_created_by_and_updated_by
# Updates updated_by_id for the current instance
after_save :update_updated_by
end
private
def add_created_by_and_updated_by
self.created_by_id ||= User.current.id if User.current
self.updated_by_id ||= User.current.id if User.current
end
# Updates current instance's updated_by_id if current_user is not nil and is not destroyed.
def update_updated_by
self.updated_by_id = User.current.id if User.current and not destroyed?
end
end
User Model:
#app/models/user.rb
class User < ActiveRecord::Base
...
def self.current=(user)
Thread.current[:current_user] = user
end
def self.current
Thread.current[:current_user]
end
...
end
Application Controller:
#app/controllers/application_controller
class ApplicationController < ActionController::Base
...
before_filter :authenticate_user!, :set_current_user
private
def set_current_user
User.current = current_user
end
end
Example Usage: Include auditable module in one of the models:
# app/models/foo.rb
class Foo < ActiveRecord::Base
include Auditable
...
end
Including Auditable concern in Foo model will assign created_by_id and updated_by_id to Foo's instance upon initialization so you have these attributes to use right after initialization, and they are persisted into the foos table on an after_save callback.
another approach is this
class User
class << self
def current_user=(user)
Thread.current[:current_user] = user
end
def current_user
Thread.current[:current_user]
end
end
end
class ApplicationController
before_filter :set_current_user
def set_current_user
User.current_user = current_user
end
end
current_user is not accessible from within model files in Rails, only controllers, views and helpers. Although , through class variable you can achieve that but this is not good approach so for that you can create two methods inside his model. When create action call from controller then send current user and field name to that model ex:
Contoller code
def create
your code goes here and after save then write
#model_instance.insert_created_by(current_user)
end
and in model write this method
def self.insert_created_by(user)
update_attributes(created_by_id: user.id)
end
same for other methods
just create an attribute accessor in the model and initialize it when your record is being saved in controller as below
# app/models/foo.rb
class Foo < ActiveRecord::Base
attr_accessor :current_user
before_create :insert_created_by
before_update :insert_updated_by
private
def insert_created_by
self.created_by_id = current_user.id
end
def insert_updated_by
self.updated_by_id = current_user.id
end
end
# app/controllers/foos_controller.rb
class FoosController < ApplicationController
def create
#foo = Foo.new(....)
#foo.current_user = current_user
#foo.save
end
end

after_save callback to set the updated_by column to the current_user

I would like to use an after_save callback to set the updated_by column to the current_user. But the current_user isn't available in the model. How should I do this?
You need to handle it in the controller. First execute the save on the model, then if successful update the record field.
Example
class MyController < ActionController::Base
def index
if record.save
record.update_attribute :updated_by, current_user.id
end
end
end
Another alternative (I prefer this one) is to create a custom method in your model that wraps the logic. For example
class Record < ActiveRecord::Base
def save_by(user)
self.updated_by = user.id
self.save
end
end
class MyController < ActionController::Base
def index
...
record.save_by(current_user)
end
end
I have implemented this monkeypatch based on Simone Carletti's advice, as far as I could tell touch only does timestamps, not the users id. Is there anything wrong with this? This is designed to work with a devise current_user.
class ActiveRecord::Base
def save_with_user(user)
self.updated_by_user = user unless user.blank?
save
end
def update_attributes_with_user(attributes, user)
self.updated_by_user = user unless user.blank?
update_attributes(attributes)
end
end
And then the create and update methods call these like so:
#foo.save_with_user(current_user)
#foo.update_attributes_with_user(params[:foo], current_user)

How to get the current_user in a model observer?

Given the following models:
Room (id, title)
RoomMembers (id, room_id)
RoomFeed, also an observer
When a Room title is updated, I want to create a RoomFeed item, showing who the user is who made the update.
#room.update_attributes(:title => "This is my new title")
Problem is in my observer for RoomFeed:
def after_update(record)
# record is the Room object
end
The is no way for me to get the user.id of the person who just made the update. How do I go about doing that? is there a better way to do the update so I get the current_user?
I think what you are looking for is, room.updated_by inside your observer. If you don't want to persist the updated_by, just declare it as an attr_accessor. Before you push the update, make sure you assign the current_user to updated_by, may be from you controller.
This is a typical "separation of concern" issue.
The current_user lives in the controller and the Room model should know nothing about it. Maybe a RoomManager model could take care of who's changing the name on the doors...
Meanwhile a quick & dirty solution would be to throw a (non persistant) attribute at Room.rb to handle the current_user....
# room.rb
class Room
attr_accessor :room_tagger_id
end
and pass your current_user in the params when updating #room.
That way you've got the culprit! :
def after_update(record)
# record is the Room object
current_user = record.room_tagger_id
end
Create the following
class ApplicationController
before_filter :set_current_user
private
def set_current_user
User.current_user = #however you get the current user in your controllers
end
end
class User
...
def self.current_user
##current_user
end
def self.current_user= c
##current_user = c
end
...
end
Then use...
User.current_user wherever you need to know who is logged in.
Remember that the value isn't guaranteed to be set when your class is called from non-web requests, like rake tasks, so you should check for .nil?
I guess this is a better approach
http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
Update user.rb
class User < ActiveRecord::Base
cattr_accessor :current
end
Update application_controller.rb
class ApplicationController
before_filter :set_current_user
private
def set_current_user
User.current = current_user
end
end
Then you can get logged user by User.current anywhere. I'm using this approach to access user exactly in observers.

Resources