Ruby ActiveRecord:如何用出口连接两个房间
Ruby ActiveRecord: How to connect two rooms with an exit
我正在做一个小文本冒险,我想使用 ActiveRecord 作为对象关系映射。
我遇到的问题是了解如何使用出口将两个房间连接在一起。
以下是给定的事实:
- 一个房间可以有多个出口
- 出口可以在不同的方向(它有一个 'direction' 字段)。此外,它可能还有其他参数,例如 'locked' 等,我想稍后添加。
- 一个出口连接两个房间。
然而,现在我卡住了:
到目前为止我有什么
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
和 room2
的出口)
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() 等。
我正在做一个小文本冒险,我想使用 ActiveRecord 作为对象关系映射。
我遇到的问题是了解如何使用出口将两个房间连接在一起。 以下是给定的事实:
- 一个房间可以有多个出口
- 出口可以在不同的方向(它有一个 'direction' 字段)。此外,它可能还有其他参数,例如 'locked' 等,我想稍后添加。
- 一个出口连接两个房间。
然而,现在我卡住了:
到目前为止我有什么
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
和 room2
的出口)
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() 等。