how to avoid polymorphic associations - ruby-on-rails

Given you have to implement a news feed like the one seen in social networks, ex facebook.
Currently I'm using a News class which has a polymorphic assocation which can be of any kind like Image, Comment, Friendship, GroupMembership, etc. Whenever an Object is created, as News is created too. It's working fine with AR(ActiveRecords) but I get into trouble when I'd switch to DM(DataMapper) or Sequel as both don't natevily support polymorphic associations and discourage it's usage.
One workaround would be to use a big SQL clause with lot's of UNIONs to merge all the different tables which should be considered as news. But this has some drawbacks, escpecially performance would be terrible.
So I wonder how to solve without polymorphic associations while still getting good performance and no other drawbacks, like having the possibility of adding meta data to a news?

Disclaimer: I'm the lead developer of Sequel.
The best way to go about it usually depends on what types of things you want to do with the data. One way to go about it is to have foreign key columns for all possible relationships:
news:
id
... (Other columns)
image_id
comment_id
friendship_id
group_membership_id
There is really no performance difference in doing things this way versus having a generic foreign key and storing a class name. For lazy loading, you just pick the one foreign key field that's not nil/NULL, and choose the appropriate association to load. For query-per-table eager loading, you just load all associations at once. This also is more flexible in that you can eagerly load using JOINs, which isn't possible with the polymorphic approach. Plus, you get the benefit of real referential integrity.
The one downside is that if you want to add more association types in the future, you need to add foreign keys to the table.

Here's a gem to maintain the referential integrity of Polymorphic Associations at the database level in Rails:
https://github.com/mkraft/fides
As of this posting there are adapters for SQLite3 and Postgresql.
Disclaimer: I wrote the gem.

Related

Ruby on Rails has_many polymorphic

I have a model, Item.
Each Item has_many traits.
The traits can be many different things:
A SizeTrait
A ToppingsTrait
A FlavorTrait
etc
How can I set up this relationship in Ruby on Rails? In a normal Ruby app I could just use duck typing, but this needs to be stored the the database.
It's like the reverse of belongs_to with polymorphic: true
I would like to avoid single table inheritance as these traits, while they will share a duck type interface, will have very different functionality. This would cause a lot of unused fields in the table and that seems like a messy, confusing way of doing it.
There are a couple posts about this already on StackOverflow, but one has a different situation than mine and the other has a pretty dodgy solution, and I want to see if there's a better one.
You can't make joins across a polymorphic association. The best approach is to just buckle down with the STI as you mentioned previously. Although you may argue that you will have multiple fields witch will be null, at least rails will be able to make sensible joins to aid you in your queries.

Rails has_many without a belongs_to

I'm using Rails 4 and am trying to implement a Notif model which should have an array of users that have seen it.
My idea is to use a has_many relationship (notif has_many :users) where I add users which have seen the notif to the users. The current issue I'm experiencing is that I cannot call #notif.users because it states column users.notif_id does not exist since I'm not using a belongs_to.
One solution is to use a many-to-many relationship; however, I'd like to avoid having to create an individual association for each user that views a notification (trying to save database space).
Is there a way to effectively have a users field without the has_many relationship? I guess I'm simply trying to store an array of user ids in my notif model.
IF you're using a relational database, although this is a correct omnidirectional relationship, ActiveRecord isn't going to play very nicely with you (if at all).
Also, it's important to note that, in the year 2015, trying to find an omnidirectional ActiveRecord workaround is far more expensive than the extra database entries.
It is technically possible - but it's not how ActiveRecord works and it won't save you money.
Not all databases actually support array types. Without array types you would have to store the ids in a string, which pretty much eliminates any effective form of querying and joins.
Even the DBs that support arrays don't really support storing foreign keys in arrays. This means you can kiss referential integrity goodbye. Indexing arrays might not work either.
Add to this the fact that you can't use Rails associations without major hacks.
I hope you realize by now that it's a pretty stupid plan to save money. Especially since database space is cheap compared to developer time.

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/

Rails Multi Tenant SAAS application with user customizable fields

We are building an a SAAS application. We need to allow customers to customize the fields of a certain model. They should be able to add or remove fields. This application is basically an asset management where customers should be able to customize the attributes of an asset.
We have tables for the assets. And these tables have multiple attribute tables like warranty, vendor, maintenance etc.
i.e
class Computer < ..
has_one :warranty
has_one :vendor
end
We have default set of attributes there but a customer should be able to alter the fields.
How do we achieve this?
Ok heres what I know. For Multitenancy we can use scopes, or multiple dbs or the awesome PostgreSQL's Schemas. But how do we implement custom fields for each customer.
Should we go for NoSQL? or should we use RDBMS for base tables and NoSQL for attribute tables?
Whats the right combination or solution or architecture for a scalable SAAS app with this requirement?
It depends on use cases and you are many choices. But, in my opinion, it's better to avoid using two kinds of db together when possible.
If you are not using PostgreSQL and no heavy queries needed for those attributes, ActiveRecord serialize can help. It utilize a text field to stored serialized hash and arrays. Nesting acceptable, speed slower.
If all of the attributes are one level key/value pair, say, warranty: 18 months, vendor: 123, then "hstore" is a good choice, built-in in PostgreSQL.
"hstore" is flexible and fast, but not suitable for nested hash. And there is a great gem dealing with it: https://github.com/diogob/activerecord-postgres-hstore
If nested hash, PostgreSQL "JSON" fieldtype may help. http://www.postgresql.org/docs/devel/static/functions-json.html It's new and I havn't used that before, just heard it's more stable and practical.
I think those choices should be enough to solve your problem. If they still can't do, maybe you can consider MongoDB.
You should definitely go for NoSQL like MongoDB. The base tables like user, profiles and other stuff which needs transaction processing should go in RDBMS.

Rails Basecamp style subdomains best practice

My goal is to have separate user accounts for each subdomain. Under no circumstance do I want cross-pollination between subdomains.
I've looked over Robby Russle, and DHH's thoughts (both are pre-Rails3 though).
The controller handling is pretty straight forward, my questions is about keeping the model's data separated. What's the best way to keep user1 from seeing user2's data?
Some idea's might include:
Add a subdomain_id foreign key to every model - Advantage, simple one-to-many relationship can be used to scope each model to a subdomain. - Disadvantage, this is pretty tight coupling between the data and the larger application logic, which seems inappropriate.
One-to-many :through for each model associating it with a subdomain - Advantage, no need to add a subdomain_id foreign key column to existing tables associating them with their sub domain. - Disadvantage, My gut feeling is that this is way overkill. Multiple join queries may get complicated and cross-pollination bugs may occur.
Separate applications or databases for each subdomain - Advantage, the data is completely segregated. - Disadvantage, a large number of individual applications/databases will need to be managed/updated/secured/hosted/etc.
Your idea?
Option 5. Guy Naor's Schema solution - Advantage, This just blew my mind. Mostly transparent to rails, COMPLETE data separation, only one database, works really great for applications that weren't originally designed as multi-tenant. a-mazing. - Disadvantage, need to be using Postgres, or some other database that supports schemas (I'm already using PG anyway), you'll need to iterate over existing schemas when you migrate.
Right now this seems far and away the best way. Are there any major drawbacks?.
If you're sure the object-to-subdomain relation will always be one-to-one, I would pick option 1. If objects might be related to multiple subdomains in the future you're bound to option 2. It incurs more overhead, but it's easily managed when using something like cancan.
I would stay away from option 3 for the reasons you mentioned. Rails doesn't do multiple databases well and besides, using multiple databases in one application doesn't guarantee any more security than the other options.

Resources