rails validating the number of users on the account model - ruby-on-rails

I have an app where a user has many accounts and an account has one user. I'm trying to create a setup where an account manager can add a user from the database by entering their e-mail address or if someone is not already a user they are added to the database and then added to the account. I want to limit the number of users that can be added to each account.
class Account < ActiveRecord::Base
has_many users
end
class User < ActiveRecord::Base
belongs_to account
validates_each :account do |user, attr, value|
user.errors.add attr, "too many users for account" if user.account.users.size >= 6
end
end
I found a great validation resource here: Best practice for limiting the number of associations within a has_many relationship? This works great for limiting the number of users but when I try to create a new user (either entering an unknown email or from the normal sign up path) it throws the error
undefined method `users' for nil:NilClass
I also have an add_user controller to control how users are added to the account
class AddUsersController < ApplicationController
before_action :get_user, only: [:edit, :update]
def new
end
def edit
end
def create
#user = User.find_by(email: params[:user][:email])
if #user
#user.add_user_to_account(params[:account_id])
flash[:info] = "Added User to Account"
redirect_to accounts_url
else
email = params[:user][:email]
generated_password = rand_password=('0'..'z').to_a.shuffle.first(8).join
#user = User.new(:name => "New User", :email => email, :password => generated_password, :password_confirmation => generated_password )
#user.save
#user.create_reset_digest
#user.add_user_to_account(params[:account_id])
#user.send_lab_invited_email
flash[:info] = "Email sent to user"
redirect_to accounts_url
end
end
def update
end
private
def user_params
params.require(:user).permit(:account_id, :user_id)
end
def get_user
#user = User.find_by(email: params[:email])
end
end
I think it has something to do with the fact that the user doesn't have an account so it can't find the account.users but most users won't have accounts until they are assigned one by the account manager. Can anyone shed light on how I can fix this issue? Can I write an if statement in the model like if current user or maybe instead define the validation in the AddUser Controller?
UPDATE:
This is my add_user_to_account method
def add_user_to_account(account_id)
update_attributes(account_id: account_id)
end

Try this:
validates_each :account do |user, attr, value|
user.errors.add attr, "too many users for account" if value.users.size >= 6
end

First of all, I think you don't need validates_each since you're validating only one field.
Secondly, you can add a if option to the validation.
So what you want is:
class User < ActiveRecord::Base
belongs_to account
validates :maximum_users_per_account if: :account
... Your code ...
private
def maximum_users_per_account
errors.add :account, "too many users for account" if user.account.users.count >= 6
end
end

Related

Create record for another model when account is created?

I have a multi-tenant application. When an account is created, the account belongs to an owner and also creates a user record for the owner. This owner can invite other users through a memberships join table. All users except for account owners have memberships to the account.
For users with memberships (not owners of accounts) the account/users/:id show page shows up. I would like the same for account owners, but am receiving the following error message:
ActiveRecord::RecordNotFound in Accounts::UsersController#show
Couldn't find User with 'id'=2 [WHERE "memberships"."account_id" = $1]
def show
#user = current_account.users.find(params[:id])
end
I can add a membership to the owner user in the admin panel and this error goes away, however I would like to add the membership to the owner/user when they create their account.
Any ideas?
Adding #account.memberships.build(user_id: current_user, account_id: current_account) before if #account.save in the accounts controller below does not seem to work.
controllers
user.rb
module Accounts
class UsersController < Accounts::BaseController
before_action :authorize_owner!, only: [:edit, :show, :update, :destroy]
def show
#user = current_account.users.find(params[:id])
end
def destroy
user = User.find(params[:id])
current_account.users.delete(user)
flash[:notice] = "#{user.email} has been removed from this account."
redirect_to users_path
end
end
end
accounts_controller.rb
class AccountsController < ApplicationController
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
if #account.save
sign_in(#account.owner)
flash[:notice] = "Your account has been created."
redirect_to root_url(subdomain: #account.subdomain)
else
flash.now[:alert] = "Sorry, your account could not be created."
render :new
end
end
private
def account_params
params.require(:account).permit(:name, :subdomain,
{ owner_attributes: [:email, :password, :password_confirmation
]}
)
end
end
Models
user.rb
class User < ApplicationRecord
has_many :memberships
has_many :accounts, through: :memberships
def owned_accounts
Account.where(owner: self)
end
def all_accounts
owned_accounts + accounts
end
end
account.rb
class Account < ApplicationRecord
belongs_to :owner, class_name: "User"
accepts_nested_attributes_for :owner
validates :subdomain, presence: true, uniqueness: true
has_many :memberships
has_many :users, through: :memberships
end
membership.rb
class Membership < ApplicationRecord
belongs_to :account
belongs_to :user
end
Have you tried callback after_create?
If it works, you will need to figure it on client and admin create an account, self assigning (admin) against on create assigning (client).
# models/account.rb
after_create do
self.memberships.create(user_id: self.owner, account_id: self.id)
end
Ended up answering this question by putting this line under if #account.save:
if #account.save
#account.memberships.create(user_id: #account.owner.id, account_id: #account.id)
Probably not ideal, but it works for now. I might end up making a service or something like that for it, though.

Rolify Gem: User has to have at least one role

How can I validate presence of at least one role for a User using rolify gem? I tried validating presence of roles in User.rb as per below, but it does not work.
Bonus: Is it possible not to permit admin user take off his own Admin role?
User.rb:
class User < ApplicationRecord
rolify
validates :roles, presence: true
end
Edit Form:
= form_for #user do |f|
- Role.all.each do |role|
= check_box_tag "user[role_ids][]", role.id, #user.role_ids.include?(role.id)
= role.name
= f.submit
Controller:
class UsersController < ApplicationController
before_action :set_user, only: [:edit, :update, :destroy]
def edit
authorize #user
end
def update
authorize #user
if #user.update(user_params)
redirect_to users_path
else
render :edit
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit({role_ids: []})
end
end
When the user has 1+ roles it works ok, but if I take away all the roles it gives an error:
You can create a custom validation to requires that the user has at least one role:
class User < ActiveRecord::Base
rolify
validate :must_have_a_role
private
def must_have_a_role
errors.add(:roles, "must have at least one") unless roles.any?
end
end
The presence validation is really only intended for attributes and not m2m associations.
Is it possible not to permit admin user take off his own Admin role?
Its possible but will be quite complex since Rolify uses a has_and_belongs_to_many assocation and not has_many through: which would let you use association callbacks.

Calling a Rails 5 action from one controller to another

I am creating an application that has users and accounts. My issue is that I created the users model and authentication function first but then realized I need to have users belong to accounts.
If I signup a user at route http://lvh.me:3000/signup it will create a new user, send an activation email and activate a user. Works great except it doesn't create the Account. But now, I need to add an account in the mix. If I sign up at my new route http://lvh.me:3000/accounts/new it will create the account and the user, but I need to send the activation email so I can actually activate the user. I can't seem to get my Account controller to trigger the #user.send_activation_email in the create action inside my UserController -- see code below. I know the way I have it below is not the right way, but I've hit a brick wall and not sure where to go from here.
user.rb
class User < ApplicationRecord
has_many :memberships
has_many :accounts, through: :memberships
accepts_nested_attributes_for :accounts
...
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
...
account.rb
class Account < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
accepts_nested_attributes_for :owner
has_many :memberships
has_many :users, through: :memberships
end
accounts_controller.rb
class AccountsController < ApplicationController
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
if #account.save
#user.send_activation_email
flash[:info] = 'Please check your email to activate your account.' # Use this for registered users
# flash[:info] = 'Please have user check their email to activate their account.' # Use this for admin created users
redirect_to root_url
else
flash.now[:alert] = 'Sorry, your account could not be created.'
render :new
end
end
private
def account_params
params.require(:account).permit(:organization, owner_attributes: [:name, :email, :password, :password_confirmation])
end
end
users_controller.rb
class UsersController < ApplicationController
...
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = 'Please check your email to activate your account.' # Use this for registered users
# flash[:info] = 'Please have user check their email to activate their account.' # Use this for admin created users
redirect_to root_url
else
render 'new'
end
end
...
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, accounts_attributes: [:organization])
end
...
If you need both models created as part of the signup flow, then have a single action in a single controller that triggers your signup flow and creates both records.
You can implement this in a number of ways, such as having the Users#signup action create the user and the account inside a transaction, or you can move that logic out of your controller and into your model layer and provide a User.signup method that creates the account explicitly or in an after_create callback.
Either way, the fix here is to simplify and unify your signup flow, not to split it up across several controllers. You only ever need to do that if you have some kind of multi-step signup that requires the user to perform some action between steps.

Assign User to a Company on sign up

Let's say I have a User model:
class User
has_secure_password
belongs_to :company, required: true
end
And a Company model:
class Company
has_many :users, dependent: :destroy
end
I wand to create a form that assigns the User to a Company, either a new one if my app doesn't have a record with that company name, or a pre-existing Company.
This is what I have so far, but I am sure there is dryer method...
class UsersController
def create
user = User.new(user_params)
user.company = Company.find_by_name(params['company']) || Comapny.create(name: params['company'])
if user.save
redirect_to root_path
else
redirect_to singup_path
end
end
end
Thanks!
You could use first_or_create:
user.company = Company.where(name: params['company']).first_or_create
...which basically does what it says on the tin.

Changing roles in rails 4

I created the app with the help of rails composer. Using devise for authentication and cancan for managing roles. So I have 3 roles by default: Admin, User and VIP. I deleted VIP, because I don't need it. Run rake db:seed to create a default admin. Then I'm coming to localhost and seeing the "First User" as admin. I logout and register(signup) a new user. Then, signing in again as admin. I see, that by deafault, this new user doesn't have any role. And also I see, that I can change it("Change role" - button). I push it and as admin can choose whether new user will be the second admin or just User. I choose, for example, User, push "change role" and have an "ArgumentError in UsersController#update wrong number of arguments (2 for 1)".
Sooo, I have two questions:
1. How to give my admin the ability to change roles without errors.
2. How to make new signing up users to have default role "User".
Thanks!
Ok, I managed to set the default role this way:
after_create :assign_reader_role
private
def assign_reader_role
self.add_role "user"
end
Here is my UserControlle:
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
if user.update_attributes(user_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
Here is models.
User:
class User < ActiveRecord::Base
after_create :assign_reader_role
rolify
devise :database_authenticatable, :registerable,#:confirmable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :name
private
def assign_reader_role
self.add_role "user"
end
end
Role:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
UserController I've already put! And where can I take params from the form?
I think you missed role_ids in permit
def user_params
params.require(:user).permit(:name, :email, :role_ids)
end

Resources