在应用程序和引擎之间共享用户帐户的最佳方式是什么?

What's the best way to share user accounts between the application and an engine?

我的网站有 UserGroup 模型,一切正常。用户和组模型是我们拥有的两种类型的帐户,它们目前用于联系信息、身份验证和授权。

现在我正在构建网站的订阅部分,这样我们就可以开始向订阅我们高级服务的用户(和 groups/organizations)收费。我选择将此新代码放入 Rails 引擎中,因为我希望仅将引擎部署到可通过我们的 VPN 访问的主机上的环境中,如下所示:

mount Billing::Engine, :at => '/billing' if Rails.env.admin?

我正在使用三个模型来管理订阅:

module Billing
  class PricingPlan < ActiveRecord::Base
    has_many :subscriptions
  end
end

module Billing
  class Subscription < ActiveRecord::Base
    belongs_to :pricing_plan
    belongs_to :subscriber, :polymorphic => true

    # Used for eager loading
    belongs_to :users,  :foreign_key => 'subscriber_id', :class_name => '::User'
    belongs_to :groups, :foreign_key => 'subscriber_id', :class_name => '::Group'

    has_many   :payments
  end
end

module Billing
  class Payments < ActiveRecord::Base
    belongs_to :subscription
  end
end

Billing::Subscription.subscriber 部分是目前让我烦恼的部分。如您所见,我目前正在跨越引擎边界获取应用程序中存在的 ::User::Group 模型,但这感觉很糟糕。

我考虑过创建 Billing::UserBilling::Group AR 模型,以便引擎和应用程序可以完全相互隔离,但是在两个模型之间复制信息似乎有点奇怪,现在,在同一个数据库中(例如 first_name、last_name、电子邮件等)...而且我必须在它们之间复制信息,这是灾难的根源,我'我确定。

我还考虑过使用某种包装模型来抽象出实际的实现,像这样:

module Billing
  class User < ::User
  end
end

但是如果我没记错的话,我 运行 遇到了多态行为的问题 我在 and/or 问题之后 rspec 模拟和存根,所以我放弃了这种方法。

如有任何指导,我将不胜感激。我曾多次前往 Google 寻找答案,但到目前为止我所看到的似乎都没有直接适用。

更新

根据 Carl Zulauf 的建议,我得出以下结论:

# File: app/models/concerns/billing/subscribable.rb

require 'active_support/concern'

module Billing
  module Subscribable
    extend ActiveSupport::Concern

    included do
      has_one :subscription, {
        :class_name  => '::Billing::Subscription',
        :foreign_key => 'subscriber_id',
        :as          => :subscriber
      }

      base = self
      Billing::Subscription.class_eval do
        belongs_to base.name.tableize.to_sym, {
          :foreign_key => 'subscriber_id',
          :class_name  => base.to_s
        }
      end
    end
  end
end

然后我这样调用:

class User < ActiveRecord::Base
  include Billing::Subscribable

  can_subscribe
end

这行得通...只要我在 之前加载 User 我调用 Billing::Subscription.eager_load :users...这看起来真的很冒险。对我有什么建议吗?

更新 #2

我最终创建了一个初始化程序来处理这个问题。这行得通,但如果有更好的选择,我会洗耳恭听。

# File: config/initializers/setup_billing.rb

User.class_eval do
  include Billing::Subscribable
end

Group.class_eval do
  include Billing::Subscribable
end

一种方法是在您的引擎中添加一个模块,将 class 宏添加到 UserGroup.

class User < ActiveRecord::Base
  include Billing::ModelHelper
  has_subscription # new macro
end

has_subscription 然后可以:

  • 查找 class 名称 (User)
  • has_many/has_one/belongs_to 关联添加到 User
  • 将特殊关联添加到 Billing::Subscription (belongs_to :user, ...)