开发者

Is Single-Table Inheritance the right solution for my Rails problem?

开发者 https://www.devze.com 2023-01-17 06:12 出处:网络
Greetings, all, I\'m working on an application in Ruby on Rails where we need to keep track of a bunch of external services for each user (for example, Facebook, MySpace, Google, SalesForce, Twitter,

Greetings, all,

I'm working on an application in Ruby on Rails where we need to keep track of a bunch of external services for each user (for example, Facebook, MySpace, Google, SalesForce, Twitter, WordPress, etc) that the app will access on behalf of the user. For some services, we will need to store an (encrypted) username and password, for some we will need to save OAuth data, for some OAuth2 data, and so on. As the app grows, we will need to support even more types of accounts, each with its own set of authentication data.

Each user can create posts within the application, and we will take those posts and send them to the external services to be published for the user. We then track the response to the published post (retweets on Twitter, likes/shares on Facebook, and so on).

So:

class User < ActiveRecord::Base
  has_many :services
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :publishes
end

class Publish < ActiveRecord::Base
  has_one :service
  belongs_to :post
end

class Service < ActiveRecord::Base
  belongs_to :user
  belongs_to :publish
end

I am debating between using Single-Table Inheritance for my Service types (for example, WordpressService, FacebookService, TwitterService, and simply serializing a simple hash to save the authentication data) and using a traditional, normalized scheme where every type of service is its o开发者_JS百科wn model and table. I would like to be able to easily iterate over all the Services associated with a User, and a Publish needs to be able to be associated to any type of Service (a Publish might be sent to WordPress, Facebook, or Twitter, for example).

Can I achieve that kind of model relationship using a traditional normalized approach? Or is this exactly what STI was meant to solve?

Thanks.


You may want to check out the omniauth plugin which is pretty easy to setup and handles storing authentication credentials for a number of services out of the box. There are a couple of railscasts that show how to set it up. If nothing else, you could see how they suggest storing things.


As an alternative to STI, you could use polymorphic associations:

class AccountAuth < AR::Base
  belongs_to :account
  belongs_to :authentication, :polymorphic => true
end
# account_id          :integer
# authentication_id   :integer
# authentication_type :string

module Auth
  #common logic
end

class FacebookAuth < AR::Base
  include Auth
  has_one :account_auth,:as=>:authentication
  has_one :account, :through => :account_auth
end

class Account < AR::Base
  has_many :account_auths
  has_many :authentications, :through => :account_auths
end

This and this may help you.


How many millions of users will you store, how many times per second will you be querying the table? I'd say in general that your physical design would suffer from that type of storage but hardware will overcome those deficiencies of design for a large number of applications. If you're not operating on large scale date or high volume transaction, you'll be fine with anything you whip up.


Though I'm still not sure if it's the "right" way to solve this problem, I've decided on using Single-Table Inheritance so that I can easily get a list of all the services that another model has_many of (since each subclass of Service is also a Service, I can call model_instance.services to get them all).

To solve the problem of code duplication, I created a module for use in any model that should has_many :services as well as each type of service:

module HasServices
  extend ActiveSupport::Concern
  included do
    has_many :services
    has_many :facebook_services
    has_many :twitter_services
    has_many :wordpress_services
  end
end

Service is also aware of its subclasses, so that menus, etc. can easily be created:

class Service < ActiveRecord::Base

  @child_classes = []

  ...

  protected

    def self.inherited(child)
      @child_classes << child
      super
    end

    def self.child_classes
      @child_classes
    end
end
0

精彩评论

暂无评论...
验证码 换一张
取 消