Create new classes or overloading with type in Rails - ruby-on-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!

Related

Managing polymorphic data in Rails

I have an application where a User can create many Links, and each Link can store different type of data, depending on what type of Link it is. For example, a TelephoneLinkData stores a telephone number, an EmailLinkData stores an email address, a subject and a body. Each Link also has some fields in common, such as a reference to the user and a name.
I've tried to map this into ActiveRecord as cleanly as I can. Currently, I have a polymorphic relationship from Link to its data-type:
class Link < ApplicationRecord
belongs_to :user
belongs_to :link_data, polymorphic: true
...
class EmailLinkData < ApplicationRecord
has_one :link, as: :link_data
accepts_nested_attributes_for :links
...
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes, what I'm trying to model is a class having multiple possible different child classes. This works fine, and I'm able to create Links through the various *LinkData controllers, but what I'd really want to do is have the Link act as the primary source of interaction for the user, so that the user manages their links through the /links path. For example, I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
I've looked around for other ways to model this relationship, and the most common other suggestion seems to be Single-Table Inheritance, but the majority of my columns will differ between LinkData classes, so that feels like the wrong abstraction.
Is there a more idiomatic way to model this data structure?
As is always the case, the best choice depends on the business or application needs, so it's difficult to provide a recommendation without knowing more about what you're trying to do.
It sounds like you prefer the MTI approach, essentially using actual foreign keys and an XOR constraint to the Link table instead of a type column. That's a totally reasonable (although not as common) alternative to a polymorphic association.
However, I think there was a bit of a misunderstanding in your question.
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes...
A polymorphic association in Ruby/Rails doesn't have anything to do with class inheritance (e.g. parents and children). You might be thinking of Single table inheritance. A polymorphic association allows one class (e.g. a Link) to be associated a record in any other table (e.g. the various classes of LinkData) via two fields, a association_id and association_type. These associated classes need not be related to each other. For example, a common use case might be the acts_as_commentable gem, that allows you to add a comment to any other object, and the comment would have a polymorphic association with the other classes.
In the second part of your question you mention that you'd like the User to interact with Link's via a single controller.
I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
There's nothing stopping you from implementing this using the initially proposed data model. ActiveRecord may not handle this completely for you out of the box, but you can imagine implementing a link_data= method on the Link class that would create the appropriate associated object.
I'd say the pros/cons of using a polymorphic association would be...
Pros:
easy to setup and use
easy to make required (validate presence of / not null)
easy to associate with a new class
Cons:
no referential / database integrity
have to migrate data if you change a class name
And using the MTI approach is basically the opposite. A bit harder to setup and use, harder to add a new association/table, harder to ensure exactly one association exists... but the long term data quality benefits are significant.
I was able to get things to work the way I wanted to using multiple table inheritance, based largely on this chapter: https://danchak99.wordpress.com/enterprise-rails/chapter-10-multiple-table-inheritance/

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.

STI + namespaces

I need to be able to schedule reminders for users. So I thought I could just create a base model Reminder and use STI to make descendant classes which override the function fire(), called when reminder fires. But different user roles have similar types of reminders. So they need to be namespaced, e.g. Adult::BrushTeethReminder sends an email to user, Kid::BrushTeethReminder posts on kid's FB wall.
Is it possible with STI and how if yes?
Other way I see is to just prefix model names like KidBrushTeethReminder. Or go even deeper - write a factory method which creates objects according to type. Or is there a cleaner way?
I see two types of reminders, one for adult and one for kid. Personally, I would use an STI called Reminder and have one model called BrushTeeth inherit from Reminder. In the BrushTeeth model, I would have two Boolean columns named for_adult and for_kid.
In your Namespace for Adult, you can check BrushTeeth by querying whether the for_adult column is set to TRUE and base your logic / implementation from there. And for the Kid Namespace, you check the for_kid column.
Having Reminder as the base, gives you the option to have other reminders easily (i.e. Shower, Bath, Nails, etc). The Reminder model also comes with a Type column, since this is an STI model.
Hope that helps.
I found that you do can use STI for models in different namespaces. You just need to place them in appropriate subfolders. For example, Kid::BrushTeethReminder should be placed under app/models/kid in file brush_teeth_reminder.rb

When to create a class vs setting a boolean flag?

I have an interesting question to pose; when should one create a model class/object as opposed to setting a boolean flag for data stored in a database?
For example, say I have a Person class that has boolean flags for President, Guard, and PartTime. This class/model is treated differently depending on the value of the flags. So the President gets different privileges in the system from the Guard and from the PartTime(r).
When would one use Single Table Inheritance to represent this information and when would one just continue to use the boolean flag?
My instinct is to convert these to different Objects using STI since this seems more OO to me. Checking booleans seems wrong in some way, but I can also see a place for it.
Update for clarification
Let me use another example because the one above has too many cases involved with it.
I am working on a CMS application that contains Pages, a Page can be Public, Private, Shared, Hidden, or Default (meaning it is what you get when you don't specify a page in the url). Right now, we have a Page model and everything is a boolean flag - Public, Default, Shared.
I am not convinced this is the best method of handling this. Especially since we have rules governing what page can be what, i.e., the Default page or a Shared page must be a Public page whereas a Private page is just Private.
I agree with the comment below that Roles for the Person example makes a lot of sense. I am not sure that for the Page example it does.
And to make things more complicated, there can only be one Default page and one Shared page. STI may allow me to validate this, but I am not sure since there can be many default and shared pages in the table (just not associated with a particular site).
Note: The context for the question is a Ruby on Rails application, but is applicable for any object-oriented language.
First of all, let's establish what single-table inheritance typically is used for. It is a way to combine the storage and behaviour of multiple things that resemble each other. Sticking to a CMS, an example would be a table with posts, which could be either a Comment or an Article. They share similar data and behavior, but are ultimately different things. Whether or not something is a comment is not the state of the object, it's an identity.
In your example, however, whether or not a page is public or private, shared or not, or hidden, appears to be a part of the state of the page. Although single-table inheritance might technically work (provided all subclasses are mutually exclusive), it's not a good fit.
State should be implemented in one or more columns. An attribute that represents a certain dual state can be specified as a boolean; yes or no. If a page always is either private or public, you can model this as a single boolean column, private. If it's not private it's public (or the other way around).
In some cases you may want to store three or more different states that are mutually exclusive. For example, a page could be either private, or public, or shared (I don't know if this is the case -- let's pretend that it is). In this case a boolean will not help. You could use multiple boolean flags, but as you correctly observe that is very confusing. The easiest way is to model this as an enumeration. Or when you lack this (as is the case with Rails), simply use string values with a special meaning and add a validation that ensures the only values you use are one of private, public or shared.
Sometimes certain combinations of different state variables are invalid. For example, a page might be a draft or approved (reflected by a boolean column approved); and it is also either public or private (also reflected by a boolean column). We could decide that a page should must be approved before it is made public. In this case we declare one of the states invalid. This should be reflected by the validation of your model. It is important to realise that a draft, public page is not fundamentally impossible, it's only impossible because you decide it should not happen.
When creating your model, make a careful distinction between the attributes that reflect actual properties and states of the subjects in the real world, and the business rules that determine what should be possible and what shouldn't be. The first should be modelled as columns, the second as validations.
Original answer:
One obvious difference is that boolean flags allow a Person to be marked as president and guard at the same time. If your model should allow these situations, single-table inheritance will not work for you.
On the other hand, maybe a Person that is a president behaves differently from a regular person; and a single person can only be president or guard. In this case inheritance may be a better fit. I don't think you should model "part time" as a subclass, though. That is an attribute in any case.
There is also an important third option, one where you completely separate the job or role of a person from the model. One person has one (or many?) jobs, which are or are not part-time. The advantage of this model is that you separate attributes of a person from the attributes of their job. After all, people change jobs, but that does not make them literally a different person. Ultimately this seems to me the most realistic way to model your situation.
I prefer not to use a flag for this, but also not to subclass Person for this. Rather, attach a Role (or if you have someone who's both a President and a Guard, a set of Roles) with subclasses of Role governing the prvileges.
Personally, I am neither a President nor a Guard, but I am both a Programmer and a Musician, and have a few other roles at times (in fact, I was a Guard for a while simultaneous with being a Student many years ago.).
A Person has-a Role.
I have found that whenever I think "Hm, I have these 3 types of behavior and they do look like subclasses, but need to change at runtime", look at a strategy or state pattern. It usually fits very well and usually also beats a simple boolean flag with respect to keeping responsiblities apart.
In your case, this heuristic would say that you have a Person with an attribute of type AccessRights, which decides if a certain action can be performed or not. Person either gives access to this object or delegates appropiate methods. After that, you have PresidentialRights, GuardRights and PartTimeRights implemetning this AccessRights interface and you are good to go.
Given this, you never need to change the person class whenever a new type of access right appears, you might need to change the person class if a new type of action appears (depends on if you delegate and how you delegate) and in order to add new types of AccessRights, you just add new implementations of AccessRights.
the answer is that it is basically a design decision. There is not an a priori right way of designing an architecture. When you define classes and relationships among them you define an architecture and, at the same time, a language representing the domain of your application.
As any languages it consists of a vocabulary (i.e. Person, President, Guard, etc.); a Syntax (i.e. the relationships you can specify for the instances of your vocabulary) and Semantics (i.e. the meaning of the terms you specify in vocabulary and relationships).
Now you can obviously obtain the same behaviour in possibly infinite way. And anyone would come up with a different architecture for the same system since anyone might have a different way of thinking at the problem.
Despite this there are some criteria you should take into account when designing.
When you define a Class you are defining a "first order" construct of your language, when you define attributes for a Class you are describing the characteristics of your first order constructs.
The best way to decide if you need a class or an attribute might be this.
Do Presidents and Guards have different characteristics apart of those they share since they are both person? If that is the case, and they have a number of different characteristics you should create two classes (one for the President and one for the Guard)both inheriting from Person. Otherwise you have to collapse all the characteristics (those belonging to person, those belonging to President and those belonging to Guard) in the Person class and condition their validity to another flag (type). This would be a very bad design
The characteristic of a Page of being public or not is instead something which actually describes the status of a page. It is therefore quite reasonable to model it as a Property of the Page Class

Rails Single Table Inheritance - What is the best way to explicitly set type?

I am using single table inheritance in my rails application, and want to explicitly set the type of an instance.
I have the following;
class Event < ActiveRecord::Base
class SpecialEvent < Event
which is implemented through single table inheritance.
SpecialEvent.new works as expected, but I want to be able to do things like
Event.new(:type => 'SpecialEvent')
So I can create different sub_types easily in the application.
However this doesn't work and seems to set :type to nil, not the value I set it to; I suspect this is because by calling Event.new it is overwriting the :type argument.
Has anyone got a good way of doing this?
If you're trying to dynamically instantiate a subtype, and you have the type as a string, you can do this:
'SpecialEvent'.constantize.new()
from "Pragmatic - Agile Web Development with rails 3rd edition", page 380
There’s also a less obvious constraint (with STI). The attribute type
is also the name of a built-in Ruby method, so accessing it directly
to set or change the type of a row may result in strange Ruby
messages. Instead, access it implicitly by creating objects of the
appropriate class, or access it via the model object’s indexing
interface, using something such as this:
person[:type] = 'Manager'
man, this book really rocks
No, I want to create instances of
sub-types, where I want to
programmatically determine which
sub_type they are
– HermanD
You could use a factory pattern, although I have heard recently that people frown on the overuse of this pattern. Basically, use the factory to create the actual types you want to get
class EventFactory
def EventFactory.create_event(event_type)
event_type.constantize.new()
end
end
To me it sounds like you'll need some mojo in the event#create action:
type = params[:event].delete(:type)
# check that it is an expected value!!!
die unless ['Event', 'SpecialEvent'].include(type)
type.constantize.new(params[:event])
Apparently, Rails does not allow you to set Type directly. Here's what I do...
klass_name = 'Foo'
...
klass = Class.const_get(klass_name)
klass.new # Foo.new
I believe .constantize is a Rails inflector shortcut. const_get is a Ruby method on Class and Module.
Up front I'll agree that STI is often NOT the best way to deal with things. Polymorphism, yes, but it's often better to use a polymorphic association than STI.
That said, I had a system in which STI was important. It was a judicial system and things like court cases were remarkably similar across their types and generally shared all their essential attributes. However, a civil case and a criminal case differed in the elements they managed. This happened at several levels in the system so abstracted my solution.
https://github.com/arvanasse/sti_factory
Long story short, it uses a factory method to leverage the common approach described above. As a result, the controller can remain neutral/ignorant of the particular type of STI class that you're creating.
You can use the Rails safe_constantize method, which will ensure the object/class actually exists.
For example:
def typeify(string)
string.classify.safe_constantize
end
new_special_event = typeify('special_event').new

Resources