Is this a legitimate use of Rails' Single Table Inheritance? - ruby-on-rails

I've just been reading Chad Fowler's blog post about 20 Rails Development No-Nos. On Single Table Inheritance he comments:
The storage of a column called “type” which holds a class name is a pretty good indicator that something fishy is going on. It’s fishy but not always bad. I think, though, that any time you use it you should ask yourself more than once if it’s the right solution. Databases don’t do what they do best as well when you have lots of STI and polymorphic associations.
I'm writing a blog application and I'm considering using STI for the comments that can be made on a post and for the contact messages that visitors can post if they want to get in touch with me. My Message model will inherit from my Comment model. They both share common attributes, except that Message will have an extra subject field. Another commonality is that both will be submitted to Akismet for spam checking.
Rather than just ask myself more than once if it's the right solution as Chad suggests, I thought I'd get some opinions from the Stack Overflow experts as well! Does what I'm proposing sound like a good fit for STI?

I've used STI a number of times. Consider a CMS which might have Page, NewsItem, BlogItem etc.
They could each descend from a common class which in turn inherits from ActiveRecord. The table for each would be the same (title, body, tags, published_at) but each model might have different associations, or different statuses, or a different workflow so each has custom code in their own class. Yet they all share a common table and parent class. It also allow me to use the parent class to do a cross class search and have the resulting Array of records automatically type cast.
There are other ways to tackle this and maybe not the best example but there are certainly times when STI is handy for situations where object behavior may differ but persisted state is the same. Of course you have to be sure this is also true in the future.
In your case comments and contact messages are different. It sounds like there is there no benefit by having them in the same table. Maybe put shared code in a parent class or better still in a module in /lib.

Related

DesignPattern for Attachments in a Chat with carrierwave

User should be able to send some pictures to his chat partner (whatsapp/line style).
There might be a possibility to also add other attachments and videos. We having an argue about the model structure. We are using carrierwave to manage the files. right now, the user can only send Pictures, in future there might be some other datatypes to be added.
Solution 1
pretty standard imo, having 3 classes (each has own DB table) and each class is getting his own carrierwave-uploader.
Chat::Picture
Chat::Video
Chat::PDF
Solution 2
this might be a little bit more trickier. Instead of 3 Models we are only having a Chat::Attachment and by that we use STI to define the type.
Chat::Attachment::Picture < Chat::Attachment
Chat::Attachment::Video < Chat::Attachment
Chat::Attachment::Pdf < Chat::Attachment
here also every class is getting his own uploader.
So the Question:: is this the right spot to use STI as a design pattern or should we stick to regular rails-models?
The answer depends on how much change you expect in future. If you expect more changes in future then you should go for rails usual way. I personally dont like STI much. I think you will be better off not using STI here, rails will provide you better separation of concern here. STI should be considered when dealing with model classes that share much of the same functionality and data fields, but you as the developer may want more granular control over extending or adding to each class individually. Rather than duplicate the code over and over for multiple tables (and not being DRY) or forego the flexibility of adding idiosyncratic functionality or methods, STI permits you to use keep your data in a single table while writing specialized functionality.

Rails 4 multiple similar models... STI?

I have 3-4 different types of clients for a photo site. Musician, wedding, portrait, and general with the possibility of adding more types. The idea is each type will have a shared set of attributes such as name email etc. but each will have their own too.
Musicians will have band name, members, genre, while wedding will have venue, coordinator details and so on.
I think this is the right way to go about it, correct me if there's an easier way to track multiple shared and unique attributes.
I was reading up on single table inheritance and one place said to use it only if the models have the same attributes but different behavior. How can I structure my models to meet this? From a more generic OOP standpoint this makes a lot of sense to me, but I'm having doubt's from that clause of STI.
Your use case sounds like a good one for STI. The key thing is that the children share some attributes with their siblings, not all attributes. I found this overview helpful:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-1/

Models in Rails are implicit, is this an annoying thing?

Considering models in Rails:
class Organization < ActiveRecord::Base
belongs_to :OrgType
end
Does it bother you that models in Rails don't include the fields that made the entity?
I know this is done for DRY's sake but you have to check the data base tables schema every time you want to check model's fields.
If you prefer a declarative style of ORM model, you might want to check out DataMapper or Sequel, both of which are easy to plug in to Rails 3.
Not annoying for me... ever since I started using the annotate-models gem to automatically add comments to the top of my model files, listing the fields associated with that model.
I was listening to a podcast a few months ago where the guy hosting the cast and the guest were advocating that Rails should take this path, and do away with migrations. The guy kept repeating "migrations must die", suggesting there be a way to specify your schema on the model instead.
I cannot disagree more, and I hope Rails never takes this path. Not only is it not DRY, but I like the fact that Rails encourages you to be aware of your own databases schema and structure.
In addition, there would be no way to keep a history of your schema if models were what controlled it, without having them be extremely cluttered. Migrations are essentially version control for your database that evolves with your application...and I wouldn't want to not have that.
Let's assume that OrgType has a field called position. This is common when you want to present a select list to the users who will be choosing a type. It's highly unlikely that your Organization will ever care about this field. Now extrapolate this to other related models with fields that other models don't care about, and add in the fact that if you ever wanted to change one of these fields you'd then have to hunt down each declaration of them, not just each place where they are used.
It's a mess.
Open up your db tool and look at your fields when you want to know what they are. Keep your models clean and readable. If you see code like this:
organization.org_type.name
It's pretty obvious that OrgType has a name field, without having to look it up, and without having to wade through configuration clutter in each model.

Ruby on Rails - Create Entity with Relationship

I'm new to rails, so be nice.
I'm building a "rolodex" type application, and this question is about the best way to handle creating an entity along with several relationship entities at the same time.
For (a contrived) example:
My application will have a Person model, which has_one Contact_Info model. On the create.html.erb page for Person it makes sense for the user of my appliction to create the person, and the contact_info at the same time.
It doesn't seem right to include details for creating a contact directly in the create view/controller for person. What's the rails way to handle this?
Using nested attributes is the most common way to do this.
The actual documentation is here.
You want to use "Nested Forms". There is a great example of them in this blog post.
I'm also noob, but I had a similar issue with an app. I was using a tutor at the time and he basically said it was a good example of rails being opinionated. It sounds like you want to take the create action for two different models at the same time, which may be possible but probably very hard. Id suggest considering whether your data model could be modified, or find a way to make an acceptable user flow while collecting the data in different forms.
Update: while writing this the technical answer came in. Keep in mind, its perfectly okay to take the easy route if doing so helps you get the app out the door, and especially while you're still new.

RoR table inheritance?

Scenario: I have a users table in my application. I also have two subclasses of users, lets say contributors and viewers. Each user group must have an entirely different set of attributes but they both share certain user properties (login, password, name).
What is the best way to implement this using Ruby on Rails?
I think single table inheritance would leave me with too many null fields.
I think linking three tables (users, viewers, contributors) would work fine, but then when wanting to edit any information i have to do: #user.viewer, while i would love to be able to just do #viewer.
Any ideas of the best solution?
I would probably go with the three tables approach. Data integrity is king over code cleanliness.
If you want to make it look neater, put virtual attributes on the Viewer and Contributor models that make it look like the User attributes are local. You can make it a module and include it in both Viewer and Contributor models.
You can also set up an :include => :user on the default finders so that you don't get an extra query when using those fields.
I'm extremely caffeinated right now, so comment back if that doesn't make sense :)
don't compromise the database schema, make it fit best. I like the three table method. If you do the database bad, the application will have very hard to fix issues later, run slow, etc.

Resources