Ruby ActiveRecord:如何用出口连接两个房间

Ruby ActiveRecord: How to connect two rooms with an exit

我正在做一个小文本冒险,我想使用 ActiveRecord 作为对象关系映射。

我遇到的问题是了解如何使用出口将两个房间连接在一起。 以下是给定的事实:

然而,现在我卡住了:

到目前为止我有什么

class Room < ActiveRecord::Base
    has_many :exits
    has_many :neighbours, through: :exits
end

class Exit < ActiveRecord::Base

    belongs_to :room, dependent: :destroy
    belongs_to :room_dest, foreign_key: "room_dest_id", class_name: "Room", dependent: :destroy

end

但这还不完整。 room.neighbours,例如,根本不起作用。

最让我困惑的是如何使出口双向工作:如果我在一个房间添加出口,它不会出现在另一个房间的 room.exits 列表中。

有效的是:(给定连接 room1room2 的出口) room1.first.exits.first.room_dest(这是room2) 但是 room2.exits 是空的,而 room1.neighbours 显示了一个只包含它自己的列表。

这是如何正确完成的?

要使 room.neighbours 正常工作,我相信您首先需要更改

belongs_to :room_dest, foreign_key: "room_dest_id", class_name: "Room", dependent: :destroy

 belongs_to :neighbour, foreign_key: "room_dest_id", class_name: "Room"

请注意,我删除了 dependent: 选项,因为您可能不想在删除 Exit 时销毁 Rooms。您需要 dependent: :destroy 与退出的 has_many 关系。

现在我们真正巩固了 Exit 的单向绑定。但是如果你仔细想想,"Exit" 不就是定义上的单向吗?虽然乍一看这似乎很有限,但您可以利用它来定义房间的 "entrances"。那是您从邻居到原始房间的连接。类似于:

has_many :entrances, class_name: "Exit", foreign_key: "room_dest_id"

或者您可以定义一个方法来查询出口并检查 room_id 或 room_dest_id 是否是房间 ID。在这种情况下,我会将 "Exit" class 重命名为更通用的名称。不幸的是,我想不出任何内置的 AR 关联可以为您执行此多键关联。它不会真正正常工作,因为像 association.build/create 这样的东西不知道要设置哪个键。但它是一个相对简单的方法或一组方法,仍然可以 return 您可以操作的范围:

has_many :connections, dependent: :destroy # For ease of creating connection, not as useful for querying them

def exits
  Connection.where(["room_id = :id OR dest_room_id = :id", id: self.id])
end

def neighbours
  exits.map do |conn|
    conn.room_id == self.id ? conn.dest : conn.source
  end
end

如果您确实希望某些连接是单向的,则可以使查询更复杂。或者编写其他以此为基础的方法,因为它不会立即执行。您仍然可以在其上链接 .first()、另一个 .where() 等。