Ruby on Rails - Alternatives to STI? - ruby-on-rails

I have many different models (close to 20) that share some common attributes but also differ to some degree in others. STI seems attractive at first, but I have no idea how the various models will evolve over time with rapid product development.
A good parallel to our application that comes to mind is Yelp. How would Yelp manage something in Rails? All of the postings have some common attributes like "address". Yet, they differ quite a lot on others. For example, you have a reservation option for restaurants and maybe not for others. Restaurants also have a ton of other attributes like "Alcohol allowed" that don't apply to others. Doing this with STI will get out of hand pretty quickly.
So whats the next best option? HStore with Postgres? I am not comfortable using HStore for anything but small things. HStore solves some problems while introduces others like lack of data types, lack of referential integrity checks etc. I'd like a solid relational database as the foundation to build upon. So in the Yelp case, probably, a restaurant model is where I am going. I've taken a look at suggestions like here - http://mediumexposure.com/multiple-table-inheritance-active-record/, but I am not happy to do so much monkey patching to get something so common going.
So I am wondering what other alternatives exist (if any) or should I just bite the bullet, grind my teeth and copy those common attributes into the 20 models? I am thinking my problems would come from the migration files rather than the code itself. For example, if I setup my migrations to loop through tables and set those attributes on the tables, then would I have mitigated the extent of the problem with having different models?
Am I overlooking something critical that might cause a ton of problems down the road with a separate models?

I see a few options here:
Bite the bullet and create your 20 different models with a lot of the same attributes. It's possible that these models will drift over time - adding new fields to one specific type - and you'll create a 200 column table with STI. Maybe you don't - the future is hard to see, especially with exploratory/agile software.
Store non referential fields in a NoSQL (document) database. Use your relational database for parts of the record that are relational (a user has many reviews and a review has one business), but keep the type specific stuff in a NoSQL database. Keep an external_document_id in your Rails models and external_record_id / external_record_type in your NoSQL document schema so you can still query all bars that allow smoking using whatever NoSQL ORM you end up using.
Create an Attributes model. An attribute belongs_to :parent_object, polymorphic: true with a key and value field. With this approach you might have a base Business model and each business can has_many :attributes. Certain (non-relational?) attributes of the business (allows_smoking) are one Attribute record. An Attribute's key could be a string or could be a numeral you have Ruby constants for. You're essentially using the Attribute entities to create a SQL version of option #2. It might be a good option, and I've used this myself for User or Profile models. (Although there are some performance hits to be aware of with this approach).
I'd really worry about having that many (independent) models for something that sounds subclass-ey. It's possible you might be able to DRY up common behavior/methods by using Concerns (syntactic sugar over the mixin concept, see an awesome SO answer on concerns in Rails 4). You still have your (initial) migration problem, of course.

Adding another option here: Serialized LOB (272). ActiveRecord allows you to do this to an object using serialize:
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(preferences: { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
(Example code from ActiveRecord::Base docs.)
The important consequence to understand is that attributes stored in a Serialized LOB will not be indexable and certainly not searchable in any performant manner. If you later discover that a column needs to be available as an index you'll have to write [most likely] a Ruby program to perform the transformation (though by default serialization is in Yaml so any Yaml parser will suffice).
The advantage is that you don't have to make any technology changes to your stack in order to apply this pattern. Its easy to moderate - based on the amount of data you have collected - to migrate away from this pattern.

Related

where do I store static information in rails application?

This is a question about structure of a model and where to put static information. I've got a model Membership and the membership model has 7 entries describing unique features among various Memberships. The information is populated by seeds.rb. However, some attributes like click_value are the same across all memberships.
Would I be better off seeding this information and storing it and duplicating it across 7 entries in the database? Or is it better to write a method within the model like the following?
class Membership < ActiveRecord::Base
def click_value
return 0.001
end
end
Is it a matter of personal preference? Is one way better than the other? Just looking for some guidance on structure.
This is mostly a personal preference in my opinion, but I see a benefit in storing it in the database together with other data for the sake of consistency. What if the click_value will eventually change across membership types? In general, an extra float column in the database wouldn't cause significant overhead, would make sure that your data model is consistent, and be future-proof. Writing a method that returns a constant is not a crime, but I wouldn't prefer it over storing the value in the database.

A model with a handful of id's

I have a model which has many of another model but this model only needs to have 10 or less id's in it.
Let's say it has, Bathroom, Kitchen, LivingRoom for arguments sake and the new records will probably never need to change.
What is the best way of making a model like this that doesn't use a database table?
This may not be best practices, but to solve the same problem I just specified a collection in my model, like this:
ROOM_TYPES = [ "Bathroom", "Living Room", "Kitchen" ]
Then in the view:
<%= f.select(:room_type, Project::ROOM_TYPES, {:prompt => '...'}) %>
(replace Project with your actual model name.)
Super-straightforward, almost no setup. I can see how it would be difficult to maintain though, since there's no way to add items without accessing the Rails code, but it does get the job done quickly.
Even though the collection of rows never changes, it's still useful to have this as a table in your database in order to leverage ActiveRecord relations. A less contrived example would be a database table that has a list of US states. This is highly unlikely to change, and would likely have only a couple of columns (state name and state abbreviation). However, by storing these in the database and supporting them with ActiveRecord, you preserve the ability to do handy things like searching for all users in a state using conventional rails semantics.
That being said, an alternative would be to simply create a class that you stick in your models directory that does not inherit from ActiveRecord, and then either populate it once from the database when the application loads, or simply populate it by hand.
A similar question was asked yesterday, and one of the answers proposes a mechanism for supporting something similar to what you want to do:
How to create a static class that represents what is in the database?

Rails 3.0 - best practices: multiple subtypes of a model object

So this is probably a fairly easy question to answer but here goes anyway.
I want to have this view, say media_objects/ that shows a list of media objects. Easy enough, right? However, I want the list of media objects to be a collection of things that are subtypes of MediaObject, CDMediaObject, DVDMediaObject, for example. Each of these subtypes needs to be represented with a db table for specific set of metadata that is not entirely common across the subtypes.
My first pass at this was to create a model for each of the subtypes, alter the MediaObject to be smart enough to join into those tables on it's conceptual 'all' behavior. This seems straightforward enough but I end up doing a lot of little things that feel not so rails-O-rific so I wanted to ask for advice here.
I don't have any concrete code for this example yet, obviously, but if you have questions I'll gladly edit this question to provide that information...
thanks!
Creating a model for each sub-type is the way to go, but what you're talking about is multiple-table inheritance. Rails assumes single-table inheritance and provides really easy support for setting it up. Add a type column to your media_objects table, and add all the columns for each of the specific types of MediaObject to the table. Then make each of your models a sub-class of MediaObject:
class MediaObject < ActiveRecord::Base
end
class CDMediaObject < MediaObject
end
Rails will handle pulling the records out and instantiating the correct subclass, so that when you MediaObject.find(:all) the results will contain a mixture of instances of the various subclasses of MediaObject.
Note this doesn't meet your requirement:
Each of these subtypes needs to be represented with a db table for specific set of metadata that is not entirely common across the subtypes.
Rails is all about convention-over-configuration, and it will make your life very easy if you write your application to it's strengths rather than expecting Rails to adapt to your requirements. Yes, STI will waste space leaving some columns unpopulated for every record. Should you care? Probably not; database storage is cheap, and extra columns won't affect lookup performance if your important columns have indexes on them.
That said, you can setup something very close to multiple-table inheritance, but you probably shouldn't.
I know this question is pretty old but just putting down my thoughts, if somebody lands up here.
In case the DB is postgres, I would suggest use STI along hstore column for storing attributes not common across different objects. This will avoid wasting space in DB yet the attributes can be accessed for different operations.
I would say, it depends on your data: For example, if the differences between the specific media objects do not have to be searchable, you could use a single db table with a TEXT column, say "additional_attributes". With rails, you could then serialize arbitrary data into that column.
If you can't go with that, you could have a general table "media_objects" which "has one :dataset". Within the dataset, you could then store the specifics between CDMediaObject, DVDMediaObject, etc.
A completely different approach would be to go with MongoDB (instead of MySQL) which is a document store. Each document can have a completely different form. The entire document tree is also searchable.

Modeling design question (Ruby On Rails)

I am converting a php project into ruby on rails. The previous project used mysql as the database and the rails project will do the same.
In the project there is a concept of a "step", which a course can have many of. A step has 1 of 5 types. (Quiz, Video, Text..etc) Obviously the type of data required for each step is different.
The way it was modeled before is to store all steps in the same table and have a field that holds a serialized php object of the step. I think this is bad for a couple reasons...You can' really query against serialized data, and it makes adding data or changing the structure of an object very hard.
Would it make more sense to model this data in a database instead of objects?
Generally yes. Rails has two possible ways to model this:
Single Table Inheritance - Several models inheriting from a common ancestor, stored in the same table. Good if your steps are really similar to each other. Here you would call has_many on the parent model.
Polymorphic associations - the association macros have an option :polymorphic which allows you to have different models in one association (each can have it's own table). I'd go for that if your steps have little in common.

Best Practice for Model Design in Ruby on Rails

The RoR tutorials posit one model per table for the ORM to work.
My DB schema has some 70 tables divided conceptually into 5 groups of functionality
(eg, any given table lives in one and only one functional group, and relations between tables of different groups are minimised.)
So: should I design a model per conceptual group, or should I simply have 70 Rails models and leave the grouping 'conceptual'?
Thanks!
Most likely, you should have 70 models. You could namespace the models to have 5 namespaces, one for each group, but that can be more trouble than it's worth. More likely, you have some common functionality throughout each group. In that case, I'd make a module for each group containing its behavior, and include that in each relevant model. Even if there's no shared functionality, doing this can let you quickly query a model for its conceptual group.
I cover this in one of my large apps by just making sure that the tables/models are conceptually grouped by name (with almost 1:1 table-model relationship). Example:
events
event_types
event_groups
event_attendees
etc...
That way when I'm using TextMate or whatever, the model files are nicely grouped together by the alpha sort. I have 80 models in this app, and it works well enough to keep things organised.
You should definitely use one model per table in order to take advantage of all the ActiveRecord magic.
But you could also group your models together into namespaces using modules and sub-directories, in order to avoid having to manage 70 files in your models directory.
For example, you could have:
app/models/admin/user.rb
app/models/admin/group.rb
for models Admin::User and Admin::Group, and
app/models/publishing/article.rb
app/models/publishing/comment.rb
for Publishing::Article and Publishing::Comment
And so forth...
Without knowing more details about the nature of the seventy tables and their conceptual relations it isn't really possible to give a good answer. Are these legacy tables or have you designed this from scratch?
Are the tables related by some kind of inheritance pattern or could they be? Rails can do a limited form of inheritance. Look up Single Table Inheritance (STI).
Personally, I would put a lot of effort into avoiding working with seventy tables simply because that is an awful lot of work - seventy Models & Controllers and their 4+ views, helpers, layouts, and tests not to mention the memory load issue of keeping the design in ind. Unless of course I was getting paid by the hour and well enough to compensate for the repetition.
Before jumping in a making 70 models, please consider this question to help you decide:
Would each of your tables be considered an "object" for example a "cars" table or are some of the tables holding only relationship information, all foreign key columns for example?
In Rails only the "object" tables become models! (With some exception for specific types of associations) So it is very likely that if you have only 5 groups of functionality, you might not have 70 models. Also, if the groups of functionality you mentioned are vastly different, they may even be best suited in their own app.
There may be a small number of cases where you can use the Rails standard single-table-inheritance model. Perhaps all of the classes in one particular functional grouping have the same fields (or nearly all the same). In that case, take advantage of the DRYness STI offers. When it doesn't make sense, though, use class-per-table.
In the class-per-table version, you can't easily pull common functionality into a base class. Instead, pull it into a module. A hierarchy like the following might prove useful:
app/models/admin/base.rb - module Admin::Base, included by all other Admin::xxx
app/models/admin/user.rb - class Admin::User, includes Admin::Base
app/models/admin/group.rb - class Admin::Group, includes Admin::Base
It's already mentioned, it's hard to give decent advice without knowing your database schema etc, however, I would lean towards creating the 70+ models, (one for each of your tables.)
You may be able to get away with ditching some model, but for the cost (negliable) you may as well have them there.
You don't need to create a controller + views for each model (as answerd by srboisvert). You only need a controller for each resource (which I would expect to be a lot less than 70 - probably only 10 or 15 or so judging by your description).

Resources