在 Rails 中重构相同的模型

Refactoring identical models in Rails

我使用加入 table 解决方案来建立这样的友谊:

class Contact < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User", :foreign_key => :friend_id

  def self.request(user, friend)
    unless user == friend
      current = find_by_user_id_and_friend_id(user, friend)

      unless current
        transaction do
          create :user => user, :friend => friend, :status => "pending"
          create :user => friend, :friend => user, :status => "requested"
        end
      end
    end
  end
...

我去给群组创建成员资格,发现了相同的场景:

class Member < ActiveRecord::Base
  belongs_to :user
  belongs_to :network

  def self.request(user, network)
    current = find_by_user_id_and_network_id(user, network)

    unless current
      transaction do
        create :user => user, :network => network, :status => "pending"
        create :user => network, :network => user, :status => "requested"
      end
    end
  end
...

我用阻塞把这个复杂化了,这是很多重复的代码。

由于 belongs_to :friend, :class_name => "User", :foreign_key => :friend_id,将该键识别为具体代表用户,我认为我无法为用户 <-> 用户和用户 <-> 使用 Contact网络。我也可以通过 network_id 使它成为 belongs_to 一个网络,但我仍然觉得我必须将方法加倍才能解决这个问题。我想我可以将 type:friend:network)传递给每个方法并像这样使用它:

      create :user => user, type => target, :status => "pending"
      create :user => target, type => user, :status => "requested"

但是,有更好的解决方案吗?

我的另一个想法是始终使用术语 :friend,但可能使用混入或抽象 class 并且只是在两个文件之间以不同方式定义 belongs_to :friend 行.

多态关联应该有助于使用一种模型:

http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

您可以将此行为抽象到名为 Requestable 的模块中。

module Requestable
  extend ActiveSupport::Concern

  class_methods do
    def by_name=(name)
      @name = name
    end

    def request(user, instance)
      # use @name or class name as default
      name = @name || self.class.name.dasherize.to_sym
      # find current relation
      current = self.class.where(user: user, name => instance)

      # create new relations, if needed
      unless current
        transaction do
          create :user => user, name => network, :status => "pending"
          create :user => network, name => user, :status => "requested"
        end
      end
    end
  end
end

现在你可以晒干你的模型了:

class Contact < ActiveRecord::Base
  include Requestable
  by_name 'friend'

  belongs_to :user
  belongs_to :friend, :class_name => "User", :foreign_key => :friend_id
end

class Member < ActiveRecord::Base
  include Requestable

  belongs_to :user
  belongs_to :network
end

我没有调试代码,但思路清晰,希望。