Creating an additional related model with Devise - ruby-on-rails

I've started to implement a new project using Devise, which is pretty fantastic for handling users. However, when a user signs up, they're not just creating a User model, but also need to create a related Account model that represents the company. Additional users will also belongs_to this Account model.
I can't seem to find a hook for this in Devise, though it seems like a pretty common pattern. What's the best practice for this?
I should also mention that there are a couple of fields for the Account that need to be provided on the sign_up form, so just something like this in the User model:
after_create :make_sure_account_exists
def make_sure_account_exists
if self.account.nil?
#account = self.create_account({ :company_name => '???' })
end
.. as I'm not sure how to get the company name.

Related

Rails two registration forms with two user types

I have a rails project where I have one user model with two different roles one is for a client and the other for business, currently am playing around with both enum roles and rolify.
I am using devise as my authentication system and I have two registration forms for each one but what I want to achieve is to assign the role to the user after the create action so to give him the ability afterwards to create a client profile or a business profile and more ..., I don't want to give the user the ability to choose from a select what type he wants.
So I am bit confused I used this method before :
after_initialize :set_client, :if => :new_record?
def set_client
self.kind ||= :client
end
I know this will give every record a client role, so I wanted to do it on the controllers and I hear its bad practice.
So am thinking to pass a hidden field in the form with the value of the role.
Any suggestions ?

Rails Design Pattern for Multiple Devise Log-Ins Managing One Set of Data

EDIT: What's the design pattern for an app that does the following: A business sets up an account with the app. The business then creates "employees" in the app that can log in separately and CRUD the business's data, except for what their employer marks as off limits?
I've run into a problem with the hostel app. Here we go:
My app, a hostel management SaaS, has many users. These are hostels. The owner of the hostel signs up for my app via Devise. current_user is always the hostel itself.
Hostels have_many guests and each guest belongs_to a user(the hostel). These are people who call/email and want to spend a night in the hostel. Thanks a ton to everyone for helping me with the availability calendar.
All is fine and dandy now. Normally, the hostel owner or manager logs into my app, books rooms, emails guests, sends invoices, upload financials, you name it. However, many have been requesting the ability to add employees that can log in separately and create reservations, send emails, etc, but NOT view financial info. Enter CanCan.
Here's where I'm stuck. It's easy enough to delegate abilities and authorizations. Devise also gives me the ability to set up multiple devise models. However, I'm stuck with how I can give the employee, once they log in, access to their employer's data. The current_user.id is going to be different than their employer's ID(the business that signed up), so how can I tell Devise to use the ID of their user?
class User
has_many :employees
end
#should I do something like this?
class Employee
belongs_to :user
has_many :guests, :through => users
has_many :reservations, :through => users
has_many :rooms, :through => users
end
I thought about doing something like this below:
current_user.id = current_employee.user.id
The only problem is, it smells. There must be a better way. Once the employee logs in, everything is going to look the exact same as when their boss logs in(show all reservations, all guests, all emails, etc), the employee will just be restricted from certain areas.
The same current_user on multiple models in devise is mostly workarounds and hacks. https://leanpub.com/multi-tenancy-rails seemed kind of in the right direction, but it's a bit too much for what I need. There must be a specific design pattern for this situation, but I can't seem to find it or even get Googling in the right direction.
Basically, how do Rails apps give their users the ability to create sub-users, and let those sub-users view the user's data with restrictions?
Any help getting my thoughts straightened out is much appreciated.
Is there anything wrong with a simple override of the current_user method?
class ApplicationController
def current_user
super || current_employee&.user # safe operator for ruby 2.3+, otherwise `current_employee.try(:user)`
end
end

Rails 4 - coding through models

I have a user model, with a separate profile model. Each user has a profile. I then have 8 models for things within a profile (for example, each profile has a dashboard, feedback and publications). The profile belongs_to user and the dashboard etc belongs_to profile.
I am creating a profile view and would like to know how I write the line of code that will collate relevant information from the other models to display in the profile.
For example,the profile will be displayed with the name of its owner (which is stored in the user model). It will also have feedback stored in the feedback model. Is there a way to write that the profile view should display the user.first_name user.last_name, and user.feedback?
You can chain calls through Profile, like so:
#profile.user.first_name
But this violates a principle known as the “Law of Demeter”. There's a complex definition, but suffice to say that when you are accessing one object (User) through another (Profile), you begin to violate this law. It's not a huge deal when you're accessing user properties through the profile, necessarily, but things get messy quickly:
#dashboard.profile.feedback.order(:rating).where(user: #dashboard.profile.user)
Gross. And brittle, too. When you need to compose multiple models into a single view, there's a better pattern known as a Decorator. The job of a decorator is to give you a single object that appropriately collects data from the models for presentation, without tying your view code directly to your models. For example:
class DashboardDecorator
def initialize(dashboard, profile, user)
#dashboard = dashboard
#profile = profile
#user = user
end
def full_name
"#{#user.first_name} #{#user.last_name}"
end
def feedback_count
#profile.feedback.count
end
def days_since_last_post
Date.today - #dashboard.last_login
end
end
# /app/controllers/dashboard_controller.rb
def show
# ...
#dashboard = DashboardDecorator.new(dashboard, profile, current_user)
end
Then your view can access the data through the decorator:
<%= #dashboard.full_name %>
While you can write your own decorators like above, things get pretty tedious pretty quick. If you like to automate some of these parts, you should check out Draper, a handy gem that makes creating decorators a little easier, especially when your decorator methods map 1:1 with model methods.

Whats the best approach for using sorcery(auth) with multiple user classes?

I`m looking to create two models: trainer & client.
When signing up those two types of models share the basic auth info, such as email & password.
Thus I would like to use Sorcery to do the authentication for me, the gem creates a User model by default.
Searching through StackOverflow I understand I could use Single Table Inheritance, which most people find problematic.
Is there a better/simpler solution for those two types of users to share the basic auth info but be separate models which would contain their role specific data?
I`m sorry if I mixed things up.
What kind of "role specific data" do your two users have?
I was in a very similar situation as you are in an app that I'm still developing. I chose to use a role based approach using CanCan.
class User < ActiveRecord::Base
has_one :client_profile # or whatever a client has here
has_one :trainer_profile # or whatever a trainer has here
end
Then, you would define your abilities
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # anonymous user
if user.is? :trainer
can :create, TrainerProfile
# some other trainer specific roles here, like editing his/her profile
elseif user.is? :client
can :create, ClientProfile
# some other client specific roles here, like editing his/her profile
end
end
end
Of course, the above code assumes an is? method on the User class to check the user role.
More info on CanCan can be found on the CanCan wiki, and also the Railscast on CanCan.

Rails: How to treat some fields of model info independently? Eg. Account vs. Profile information

I have a User model with the usual information (login, email, name, location, etc). However, when users decide to edit their information, I'd like to separate the fields to be edited according to the appropriate concerns.
For example, I'd like to have Name, Bio and Location to be edited on a Profile page or tab, and login, email and password to be edited on an Account page or tab.
What are the best practices, and the safest way, to accomplish that? Should I have two separate model/resources: User and UserProfile? Or can I just create something like a profile method in the UserController, with a custom form with only the specific profile fields, and link to it in the user page? I'm really confused on how to go about this.
Thanks in advance for any ideas you might have.
I think it depends on the rest of your Application. It sounds like in your case it's good to be modular. If you want users to be able to see the profile of other users, it's an advantage to have a separate model for the Profile and like it with a has_one relationship. I'd just call the class Profile so it can be accessed through user.profile in your controllers and views.
Models:
class User < ActiveRecord::Base
has_one :profile, :dependent => :destroy
end
class Profile < ActiveRecord::Base
belongs_to :user
end
If all your users have both profile and account fields then I wouldn't put it in seperate models. It will only add unnecesary complexiety to your forms and add may add some sql queries.
I don't see here any security problems. In case of editing, both actions (edit account and profile) should be protected the same way - so only owner user should be able to edit both of them. If he want to "hack" it and edit also his login when he edits his first name then it is his problem. It won't cause any problem since he is allowed to edit both fields.
According to views that are viewable for other people: just don't display there any fields that belongs to account part.
How to seperate it? For me the cleanest way is to add this kind of routes:
map.resources :accounts
map.resources :profiles
And use paths like /accounts/34/edit to edit account part and /profiles/34/edit to edit profile part.
In this case you will need a seperate controller for both routes: accounts_controller.rb and profiles_controller.rb. If they share a lot of similar methods and behavior, you can add it in users_controller.rb and in profiles and accounts controllers inherit from it.
You can also do it with one controller:
map.resources :accounts, :controller => 'users'
map.resources :profiles, :controller => 'users'
But I don't know how to pass some additional value with routes created with resources. When you use connect you can pass it with :defaults => {:foo => 'bar'} and then it will be availble in controller as params[:foo] but it doesn't seem to work with resources. So how can you distinguish between accounts and profiles? You can read it from current url (here is example). And then in controller you can render different views according to requested resource.
I would be inclined to cut along the grain here. It's going to be easier to map a model to a form. So if you have information that is accessed within different forms in your UI, I would create a model for each.
That said, at a certain point in my systems I tend to pull the profile information out of the base User class, so the User becomes solely for authentication.

Resources