How to Represent a Person Who is not a User in Rails - ruby-on-rails

I'm trying to build a rails app similar to a CRM, where I have users, and each use has many "clients".
Initially, I created a model for a user and one for client, but while writing unit tests I realized these two are very much the same.
So my question is: was my original design decision to model them separately correct? Or is there a better way of reusing the code, even though I don't foresee Clients ever being able to actually log into the system?
I've looked at similar questions but they all apply to different user types and roles. In this case, Clients only exist as a model and will never actually BE users.

In any CRM application, User and Client there are similarities and differences between them. Let's put some details
The Similarities
Both of Users and Clients share various attributes, especially those related to personal information (name, contact info, ...etc.)
Both (mostly) represent one and only one person who has a relation to the system. One obvious exception are system users.
The Differences
A user accesses the system. This implies security needs such as authentication, identification and authorization, which in turn implies some validation (passwords, certificates, ...etc)
In CRM, a Client usually has various relationships such as with Account, Company, Team, Account Manager, and others, a User doesn't. Even though this is not handled by the model, but you might choose to do some model-level validations on some fields
Therefore, the choice of separating Users and Clients in two different models, combining them, or subclassing from a parent is a choice based on your actual need. There are some systems (such as OpenERP, if I remember correctly) treat Client and User in the same manner, while defining an is_system_user property. Personally I'd choose to separate them for the differences mentioned and for security reasons. If you're not sure about your need, it's safer (while not DRY'er) to separate them.

Maybe you should give a type to users (by creating a Type model and making a has_many/belongs_to ActiveRecord association for example): so a user can be a client or anything else you want.
Then, you can manage user permissions with cancan (a very great gem), depending of their type.

Related

What is the name of this concept I am implementing, and how to in Rails?

I am trying to implement a feature to my project (kind of like a social media site) that could be either basic or complex and I am not sure if I am going to take forever reinventing the wheel or just go on a crazy tangent that won't work. I just need to "check in" so to say.
I am going to use Facebook terminology as an example to simplify the concept but implement similar features with different names. In Facebook you have Pages and Groups, which are similar yet have slight differences (from now, I will call the collection of these DataSets). Both of these can have multiple admins or followers, which are all User roles, and each User can have roles for multiple Groups and multiple Pages (one role per Group or Page). Then for example, you can click a drop down to change your account to post as a Page you are an admin for.
Essentially, the concept I am describing is where a single User can have a role for multiple different types of DataSets. For example, a single User could follow 30 different Pages and 10 different Groups, and be an admin for one Group and two Pages. Does the concept I am describing belong to a particular concept or software design pattern? I am finding it really hard to describe this feature without using Facebook examples.
I have a strategy to implement this type of functionality in Rails, but I feel like using this strategy would be making the problem harder than it is and there is a fancy rails way of doing it, or a Gem, but I just don't know how to research it due to lack of terminology to describe my problem.
Current strategy is:
I have a Users table from Devise. Pages and Groups are each individual models and have their own tables. I have matching database tables to make the many-to-many relationships between Pages and Users, along with Groups and Users (e.g. 3 column design, column for the user_id, column for the page_id and the type of relationship such as admin or follower). Let's call these Group_User and Page_User. I am being flexible at the moment as I may add more DataSets similar to Page and Group.
Then for the Devise User table, I have an extra two columns to track the DataSet that the User is an admin for and currently posting as. One column is for the DataSet type and the other for the id for this instance (e.g. [Group,1] is stored in these two columns to represent Group with group_id:1 and [Page,3] is used to represent Page with page_id:3). These two columns can be checked when displaying options relevant for admins in that Group/Page and a simple drop down at the top of the site changes the values in these columns to any of the Pages/Groups the logged in User is an admin for. This way, one User login can take on many admin roles and change between these easily as needed.
Is there a better way to do this in Rails, such as a gem or specific design pattern? Or am I on track to implement these features myself? I think I understand the problem but my solution just seems simple/raw and possibly might have unintended consequences later down the track (e.g. it seems database intensive).
One way I was thinking of doing this was making a concern that includes methods to build the relationships and pass in the name of the DataSet as an argument, just so I am not rewriting the same methods for Pages, then Groups, then whatever comes next.
I looked at other solutions such as polymorphic typing (which I think is good for if each user only had one role or only managed relationships for one group or one page) and Single Table Inheritance (but I think my Pages and Groups might be too different for this to work). I thought about using inheritance as well (e.g. a parent for both Group and Page) but I am not sure this helps much.
I am just a guy that studied too much computer science and not enough software engineering. Any tips on how to simplify this problem or just a simple "yeah that will work" would be really helpful!
I think you are going great in the database design. Once participated in a social media application like yours which had similar type of design. Your design seems much better than the one I worked with. In my opinion this type of applications are supposed to be database extensive.
There are several design patterns used in RoR. One I heavily use is Service Object Pattern to maintain thin controller and models. Also it helps me to write reusable class.
Another one I like is the Presenter Pattern to simplify views.
You can have a details look at this blog post for more design pattern ideas.

How to set up Rails app that has different types of users?

If I want to build a Rails app that has two different types of users, let's say one type is called players and the other one is owners, what is the best and most efficient approach to modeling the app?
Things to take into account:
There should only be one Login, but different Registration forms that Owners/Players can use.
Owners can have access to a control panel but Players cannot.
Owners cannot share any of Players capabilities, but both need to be able to perform Login/Registration.
I am not using Devise, so please do not suggest it.
Different Approaches I've considered:
Using cancancan gem, but it does not really seem to meet my needs in the sense that I am not looking to create a user/admin hierarchical approach but rather a if you're a Player, then you can see these pages and perform these actions but Owners cannot and vice versa. Almost like splitting the app in two. cancancan seems that it would treat Owners as "Players with extra privileges", not different privileges entirely.
Creating separate models with separate login and registration forms, which seems like a disaster waiting to happen. One small mixup between a Players table and the Owners table, especially with the primary keys, and that will be a world of trouble where people could end up logging in to the wrong accounts.
Creating a polymorphic or has_one relation toward an Account model, which so far, seems like the best way to probably go about it. If I created a polymorphic Account model, I can store different types of Players/Owners, but how could I compare login credentials against all types?
I had been trying to find something on this matter regarding how to map this out and was surprised to not find an information on how to do this without using Devise. If anyone has any good links they can point me to that also address this matter (without Devise), please leave them in your answer! Thanks.
I'd suggest one User class with a type attribute that determines whether the user is a Player or an Owner (single table inheritance). This way you keep the registration logic in one place but can customize the forms depending on the user's class.
There must be alternatives to cancancan that help with what you want to do, or you can implement helpers yourself:
def can_access_control_panel?
current_user.is_a?(Owner)
end
You have to have a way to separate one user from another. One way is to add an attribute to the User table so you can call current_user.role and it will return "owner" or return "player".
I have used Pundit gem in the past. It lets you define which controller actions the current user is allowed to access. So as you create resources for your application, you can add a policy that specifies who is allowed to that given resource. This is the repo to the application.
This answer might help you.

What are the advantages/disadvantages of separate user & account models

I frequently see programmers separate these, e.g. User has_one Account or Profile or whatever. What gives? Is there some security advantage to keeping authentication/authorization separate from other attributes that might much more easily be put into the User model?
I guess more generally, my question is, what is the purpose of a one-to-one has_one-belongs_to relationship?
I could see the purpose if there were different, interchangeable account types (although you could just have user types, I think), or if a User has_many Accounts.
Am I missing something? Why not use one model?
Here are some advantages I can think about:
Separating user & profile make models cleaner, every model will be responsible for only one thing: the user for logging/auth - things that matters for the user himself only; and the profile for information that are viewed on the user's page and matter for everybody.
As a result, this will make less overhead on the DB. You'll need to access/update users table only at logging, and without accessing/updating the (possibly big) data of the user's information that only matter for viewing.
If you have a developers team responsible for authentication and another responsible for presentation. It's a good idea to separate the resources they're dealing with as much as possible. A thing that deserves mention here is that such kind of separation not only helps in models, but also in controllers and maybe views.
Another good reason is when you need to give a user more than one profile. Yes that's common if your app is somehow "social". LinkedIn allows users to have many profiles, each in a specific language.
Well one big reason is that it is better to separate classes to make your app more manageable.Maybe your user will fill out some profile information after they sign up for your service so having a separate class that handles that is much easier to manage and things get less messy down the line.
Your classes should have one single responsibility and it should be clear what that class does and be able to fully execute it.This principle is the S in SOLID design principles.You can read more about it here: SOLID
Hope this answers some of your questions!
It can actually make a significant performance difference in a heavily trafficed website.
Consider a website where people are almost always logged in but rarely access their profile/account. Every time you fetch a user for authentication, you're moving all the profile data in/out/around of memory (you can get around this by telling the fetch to only include certain columns, but that's less maintainable and more prone to accidental screwups than segmenting the model).

Authenticating different kinds of users in Rails

I'm making an app with 3 kinds of users: Teachers, Students and Admins.
Teachers can post Classes, with class info and assignments and stuff, and Students can enroll in those classes and do the assignments, get the materials and stuff. The Admins are just the people involved in running the site and will not be taking any of those actions, but just moderating and keeping an eye on things.
My question is, how do I built the separate authentication for those three cases? The users won't have anything in common and I'm gonna need 3 different logins/sign ups from the get-go, so it's not just a matter of assigning different roles.
How should I go about this?
All of those users have one important thing in common - they are all users. I don't see a reason for separating them. If you need users to sign up as a Teacher or Student, you can simply set up different views for those options and the default role that will be assigned to that user.
I don't see a reason why you think that it should be something different than assigning roles. If you really see many differences in data that will be stored for those different types of users, you can for example have have a common Users table and subclass them and store type-specific data in separate tables with reference to the User. However, for now I don't see anything in your description that can force something more than a simple role assigning.

not sure whether to create new models

I'm making an app with Rails that, among other things, allows certain types of professionals to create an online profile. So there is an 'association' between the User and Profile model. I created the Profile model so that I can basically restrict the User model to name and registration information, and keep profile information elsewhere (i.e. 'normalize' the db as I think it's called)
However, these profiles (as is often the case, on LinkedIn, for example) contain a lot of information. For example, there will be a section for Work Experience that sometimes looks like this, where a user can add many different work experiences.
and then something similar for education
And many other different types profile information. Note also, that the ability to add to and edit these discrete categories takes place in separate forms. I can add to my work experience without touching education.
My question is, should I try to store all of this information in my Profile model? Or, would you, for example, create a WorkExperience model and do something like Profile :has_many work_experiences? and something similar with EducationExperience, or is there even another way to do it?
One of my concerns is queries on the DB. For example, in terms of performance, is there going to be much of a difference if, in the show action of the Profile controller, I do queries for WorkExperience.all, Education.all rather than storing all the information in the Profile model?. Another concern is just general code organization for profiles that can become quite large.
You'll have the cleanest code (and decent performance) by splitting out something like Experience (which will belong_to Profile), along with a type column that determines whether it's "work" experience, "education" experience, etc. That's the approach I would take, unless the different types of experience have vastly different columns.

Resources