1

我正在制作一个小型文本冒险,我想使用 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另一个房间的列表中。

有效的是:(给定一个连接room1和的出口room2room1.first.exits.first.room_dest(这是room2)但是room2.exits是空的,并room1.neighbours显示一个仅包含自身的列表。

这是如何正确完成的?

4

1 回答 1

1

为了让 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: 选项,因为您可能不想在删除出口时破坏房间。你想要依赖: :destroy 与出口的 has_many 关系。

现在我们已经真正巩固了 Exit 的单向绑定。但仔细想想,“退出”不就是定义上的单向吗?虽然起初这似乎是有限的,但您可以利用它来定义房间的“入口”。那是您从邻居到原始房间的连接。就像是:

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

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

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() 等。

于 2015-06-02T18:01:40.470 回答