views:

36

answers:

2

My app has

  • "normal" users: those which come through a typical signup page
  • facebook(FB) users: those which come from Facebook connect
  • "FB-normal" users: a user that can log with both email/password * FB connect

Further, there's the a slew of other openID-ish login methods (I don't think openID itself will be acceptable since it doesn't link up the accounts and allow the 3rd party specific features (posting to twitter, adding a FB post, etc etc))

So, how do I model this?

Right now we have User class with #facebook_user? defined -- but it gets messy with the "FB-normal" users - plus all the validations become very tricky and hard to interpret. Also, there are methods like #deliver_password_reset! which make no sense in the context for facebook-only users. (this is lame)

I've thought out STI (User::Facebook, User::Normal, User::FBNormal, etc.) This makes validations super slick, but it doesn't scale to other connection types, and all the permutations between them... User::FacebookLinkedInNormal(wtf?) Doing this with a bunch of modules I think would suck a lot.

Any other ideas?

A: 

You're running into one of those object-relational impedance mismatch landmines.

Facebook and LinkedIn each solve this by using non-relational key/value stores. Facebook uses Cassandra, and LinkedIn uses Project Voldemort.

When you're not bound by the conventions of the relational model (e.g. all rows of a given table have the same columns), you can gain more flexibility and vary the attributes per row. But doing this, you sacrifice some strengths of the relational model (e.g. all rows of a given table have the same columns).

In an RDBMS, I would architect this using Class Table Inheritance.

Bill Karwin
A: 

I would split up the concerns, seems like it doesn't make sense to keep records that have vastly different fields in one table.

Just have one User model/table that doesn't save any credentials, and make that have a has_many relationship to an Account/Login Method, where you could use STI to model the different types.

class User < AR::B
  has_many :accounts
end

class Account < AR::B;end

class PasswordAccount < Account;end 

class GoogleAccount < Account;end

class FacebookAccount < Account;end

…
gerrit