cancan role strategies strategies comparision / tradeoffs - ruby-on-rails

To create an application that supports authorization in Rails using cancan, it is up to the developer to decide what will be the role method. Now I need to figure out the best for my case - It is the traditional one-role-per-user model.
The methods I found seems to be quite similar from me. I wonder if there is really a technical difference while selecting the role models, or is just a question of preference? If there are techincal differences, what are the trade-offs in selecting one model or another, in special about scaling?
Some possible models I could found:
Using the gem rolify
Using single table inheritance
Using a row in the users table
Separated tables for each user type, according to its role
Multiple table inheritance
Table with roles and using relationships
Points for articles that compare these approaches on the comments are appreciated, since I couldn't find much material comparing them.

If you only need one role per use, just go with the simplest approach.
Add a single string field to user; called "role".
Add an array to User to hold the values: ROLES = %w{System\ Admin Admin Customer Etc}
Add some helper methods to user so you can easily check for a role (i.e. user.admin?)
Role helpers:
ROLES.each do |role_name|
define_method "#{role_name.downcase.tr(" ", "_")}?" do
role == role_name
end
end
You can easily leverage the more complex options in the future if needed.

Related

has_many :through model names, controller and attributes best practices?

Disclaimer: I really spent time thinking about names of models and variables. If you also do, this question is for you.
I have a Rails project which contains two models: User and Project.
They are connected by the model ProjectsUser, which is a connection model in a many-to-many relationship. This model also holds the role of a user in the given project, along with other attributes such as tier and departments. So this is a has_many :through relationship.
Given this scenario, here is everything that always bothered me on all my rails projects since I started developing on it:
Should I use a ProjectsUserController or better add the relevant actions on UserController and ProjectController? At some point, I want to assign users to a project, or even changing the role of a user in a given project. Is it a better practice to leave those actions on the connection controller, or use the model controllers?
Should I write a method to get the role of a user for a given project? This is basically if I should have a method User#role_for(project) or not. Since this method basically is getting the information from the projects_user object it could make more sense to always let this explicity on the code, since most of the times I'll have the project and the user, but not the projects_user. Is this line of thinking correct, or maybe the problem is that I'm should have more project_user on my code than I really do? Are there good caveats for this?
Should I try to rename my table to a non-standard name if it is not obvious? Ok, I got that if I have the models User and NewsSite I should use has_many :subscriptions, but the thing is that naming those models in real life cases are usually harder, by my experience. When the name ends up not being that obvious (for exemple, in my case, maybe project_participation as #wonderingtomato suggested) is for the best, or in those cases it is better to fall back to the ProjectsUser approach?
One extra cookie for pointing beautiful open source Rails code, or by book indications that might help with my kind of questions.
I would use a specific controller. Even if now the interaction sounds simple, you can't know if in the future you'll need to add more advanced features.
I've been handling these kind of relationships in several projects, and using a controller for the join model has always paid off.
You can structure it this way, for example:
index should expect a params[:project_id], so that you can display only the index of users for a specific project.
create is where you add new users, that is where you create new join models.
update is to modify a value on an existing join model, for example when you want to update the role of a user in a project.
destroy is where you remove users from the project, that is where you delete the corresponding join models.
You might not need a show and edit actions, if you decide to manage everything in the index view.
Also, I'd suggest to choose a different name. Rails relies heavily on naming conventions, and projects_users is the default name for the join_table you would use with a has_and_belongs_to_many association. In theory you can use it for an independent model (and a has_many through:), but it's not immediately clear and you might break something. In addiction, it will confuse the hell out of any new programmer that could join the project in the future (personal experience).
What about calling the model something like project_participation?
If you haven't built a lot of functionality yet, and don't have yet that table in production, changing it now will save you a lot of headaches in the future.
update
1) I stand by what I said earlier: your join model is a full fledged record, it holds state, can be fetched, modified (by the user) and destroyed.
A dedicated controller is the way to go. Also, this controller should handle all the operations that modify the join model, that is that alter its properties.
2) You can define User#role_for(project), just remember that it should properly handle the situation where the user is not participating to the project.
You can also make it explicit with something like:
#user.project_participations.where(project_id: #project.id).first.try(:role)
# or...
ProjectParticipation.find_by(project_id: #project.id, user_id: #user.id).try(:role)
But I'd say that encapsulating this logic in a method (on one of the two models) would be better.
3) You are already using a non standard name for your table. What I mean is that it's the default name for a different kind of association (has_and_belongs_to_many), not the one you are using (has_many through:).
Ask yourself this: is the table backing an actual model? If yes, that model represents something in the real world, and thus should have an appropriate name. If, on the other hand, the table is not backing a model (e.g. it's a join table), then you should combine the names of the tables (models) it's joining.
In my mind, REST doesn't always have to map directly to DB records. A conceptual resource here is the association of Projects to Users. Implementation would be different depending on your persistence layer, but a RESTful API would be standard.
Convention over Configuration in Rails is a great helper, but it isn't necessarily applicable to every case 100% of the way through the stack. There doesn't need to be a 1-to-1 mapping between controllers, models, and their respective names. At the app-level, particularly, I want my routes/controllers to represent the public view of the API, not the internal implementation details of the persistence and domain layers.
You might have a UserProjectsController which you can perform CRUD on to add/remove project associations to users, and it will do the appropriate record manipulation without being overly bound to the DB implementation. Note the naming, where the route might be /user/:id/projects, so it's clear you are manipulating not Users or Projects, but their associations.
I think thinking about this sort of thing (both before and after the fact) is what leads to better designs.
I too start with the model and think about the application structurally. The next step in my oppinion is to build the user interface to make sense based on what makes it easy and useful for the user (spending more effort on things that matter more). So if it makes sense for the user to separately edit the ProjectsUser objects then the ProjectsUsersController is the way to go. More likely editing the join model objects as part of the Project (or User depending on the structure of you app) will be a better fit for the user. In that case using a nested form and editing via the controller (and model) that's the main model referenced by the form is better. The controller really lives to serve the UI, so decisions about it should be dependent on the UI.
Yes, if it makes your code simpler or more readable. If you use role more than once I suspect it will.
I would actually name that model something like Member, or ProjectMember (or Membership). It defines a relationship between a user and a project, so its name should reflect what relationship that is. In the occasions where such a name is too unwieldly or too hard to define then falling back to something like ProjectUser is reasonable (but not ProjectsUser). But I definitely like finding a more meaningful name when possible.

Rails architecture, better create a a new model or add a boolean value to existing model?

I have an article model article.rb (with related images):
attr_accessible :name, :content, :image_ids, :altname1
I now want to create another kind of articles, that are just for cities. They will appear in a completely different section of the website and are not related to all other articles. But the model is exactly the same.
Is it better to create a new model cityarticle.rb or is it better to add a new boolean column (cityarticle with true and false as options) into the existing article model. I'd then add a new action to the controller:
def cityarticles
#cityarticles = Article.where("cityarticle = ?", true)
end
For me it's easier to keep just one model, but there might be good reasons for a new model? How about performance?
Some questions to ask yourself: In what other ways will these types of articles be different? Will different people have access to create/edit/delete them? How do I create a city article and not let it accidentally be saved as a non-city article, or vice versa? Am I using attr_accessible or strong_parameters to prevent users from overriding the article type with params passed in? Will the different types have different validation rules (maybe requiring a city name for city articles)?
How hard will it be to get just the articles I want? How likely am I to forget to specify what kind of articles to show and show all of them?
Based on answers to questions like those, you should be able to decide what data structure will work best for you. If you use Rails' STI pattern, you could wind up with one table but two different models, as one solution. Performance is probably not the main consideration, but if the two types of articles are never going to be queried together, it might be slightly better in terms of performance to split them into separate tables.
New model represents a new "entity" on the System.
Say CityArticles extends Article
It should be a new Model for code clarity and extensibility to increment functionality over the "CityArticles".
You can make a new table scaffold or migration:
rails g scaffold CityArticles references:article
Or making Article class polimorfic association. Read http://teachmetocode.com/articles/ruby-on-rails-what-are-polymorphic-associations/

Create new classes or overloading with type in Rails

I have a Meeting model, with multiple Participants in different roles (say, Tutor and Student). Right now, I have a single class Participant, which has attribute :type with two possible values (Tutor/Student). These two types share some exactly the same methods. Each also has its own version of other methods. (say, a Tutor when schedule a meeting must get approval from Director).
I handle the differences in methods by overloading with type:
def make_appointment
do stuff
if type = "Tutor"
do something extra
end
end;
I am undecided whether to go this way, or to have two classes, Tutor and Student that inherit Participant class.
What are the issues/pitfalls should I consider in deciding which way to implement this?
Thank you.
For methods that differ only slightly, there are options--build in extension points, pass blocks around to enhance behavior, etc.
Almost every time there's type-dependent behavior implemented via type comparisons it's not a good idea.
You should use some authorization gem like cancan which is the most poular.
There is a short tutorial about role based authorization. => https://github.com/ryanb/cancan/wiki/Role-Based-Authorization
Its absolutly okay to store the data in one Model. If there are many different attributes you may use polymorpic associations to expand the user Model but in most cases thats not needed!

How to handle multiple user types in Rails?

I'm finding a good way to modeling User different types in the system. For registration, he/she can select to be a student, a mentor, or both. Being a student or a mentor has different set of properties. Student and mentor will have different profile template layout as well.
How would you design your controllers and models for this kind of problem?
I would create a User which can hold a Mentor class and/or a Student class. This way your different properties are seperated from each other while the same properties still remain in the User class.
In the Controller you can render a template (or partial), depending on the instance the User holds. One for students, one for mentors and one for both.
You could also use Inheritance (User as parent with Mentor, Student and Both as childs). The key word you want to look into here is Single Table Inheritance.
Imho the problem is the both option. That's why I would prefer the 1st solution.

Help with database strategy for giving roles to users in specific model instances

I have a ruby app running with declarative authorization and I have made the roles:
admin ( app admin )
org_admin ( organization administratr )
org_colab ( organization colaborator )
org_visitor ( organization visitor )
a User can has_many Organizations and he can be an admin or a colaborator.
I link them using a Affiliation table
What is the best strategy to solve this many to many roles?
To put a extra attribute on the Affiliation table ?
like : Affiliation(:user_id:integer, :organization_id:integer, :affiliation_type:integer)
and the affiliation type can be 0 for org_admin and 1 for org_colab and 2 for org_visitor?
I imagine there must be a better way to assign roles to a particular organization...
It's the classic "User-Group-Role" model. It's a ternary relationship. User-Group is many-to-many; so is Group-Role. You'll need five tables to capture all of it.
You'll start with a User table. It'll have a primary key (of course). Same with Group and Role.
You'll have a UserGroup table sitting in-between the User and Group tables in your E/R diagram. UserGroup will have two columns: user_id and group_id. The primary key will be the combination (user_id, group_id). The user_id column will have a foreign key relationship with the primary key of the User table. Ditto for the group_id column and the Group table.
There will be a similar arrangement with Group and Role. Each will have a primary key. The GroupRole table will sit between the two in your E/R diagram. Repeat the verbiage above and you'll have it.
I'd create an E/R diagram to show you, but I'm busy doing some other things. I hope this is sufficient for you. If not, perhaps I'll add it later.
So Users don't have Roles; Groups do. You add a User to a Group, and that User gets all the authorizations that the Group has. That way you can add a particular Role (e.g., admin permissions for the Admin group) one for a particular Group. Then everyone who is added to that Group will get those permissions.
i actually implement something similar to my project. To clarify if i understand you correct - your user might have a role in the whole application-context, as well as a specific role in the organizational-context which may not depend on each other.
A pragmatic solution might be to implement two different rolesets. Let's think of the following construct: You have a user-model (having application-wide informations), a organization-model (which defines an organization and might possibly be owned by a user) and a collaborative-model which defines a relationship between a user and a organization.
In this case it would be the best to store roles in the user-model (which do the application-wide stuff) and to store organization-specific roles in the collaborative-model.
I used a string based roles-store in the models in combination with ryan bates cancan-gem - its easy & fast to use and puts roles-logic in a defined place.
This might not work for thousands of different roles and groups and stuff, in that case you should implement the role-storage in an extra model (with a polymorphic relation to users and collaboratives).
Best wishes - Florian
edit: To make that implementation searchable by ActiveRecord you might want to use the 'serialize'-feature for the :roles-field, as described in ActiveRecord::Base.

Resources