سؤال

I'm trying to build a text adventure game in Rails 3 (yes I know that's silly). Right now I have a model called Room. Each room needs to be associated with up to four other rooms. This relationship would be bi-directional, such that any two associated rooms would be exits for each other. So for example if I were to say:

@room1.north = @room2

@room2.south would automagically become @room1. Similarly, if I were to say:

@room1.east = nil

@room2.west also becomes nil. I would like to make this happen using only model associations, rather than doing it manually in the controller. Is this possible?

EDIT

The first example matzi gives doesn't quite work the way I want. Consider the following:

class Room < ActiveRecord::Base
  attr_accessible :north, :south, :east, :west

  has_one :north, :class_name => "Room", :foreign_key => "south_id"
  has_one :east, :class_name => "Room", :foreign_key => "west_id"

  belongs_to :south, :class_name => "Room", :foreign_key => "south_id"
  belongs_to :west, :class_name => "Room", :foreign_key => "west_id"
end

@room1 = Room.new
@room2 = Room.new
@room1.save
@room2.save

This works fine:

@room1.north = @room2

@room1.north #Outputs @room2
@room2.south #Outputs @room1

@room1.north = nil

@room1.north #Outputs nil
@room2.south #Outputs nil

So far so good. But:

@room1.north = @room2

@room2.south = nil

@room1.north #Outputs @room2, but it should be nil
@room2.south #Outputs nil

Furthermore:

@room2.south = @room1

@room1.north #Outputs nil, but it should be @room2
@room2.south #Outputs @room1

See the problem here? This isn't truly bi-directional.

SOLVED

It turns out Matzi's first solution was correct after all. As he pointed out, the issue I was having with that solution was one of saving. The following works:

@room1 = Room.create
@room2 = Room.create
@room1.north = @room2
@room1.save

Room.find(1).north #Room 2
Room.find(2).south #Room 1

@room2.south = nil
@room2.save

Room.find(1).north #nil
Room.find(2).south #nil
هل كانت مفيدة؟

المحلول

Of course it is possible. Select two direction (e.g north and east) which holds id (north_id and east_id), because there is no need for all four. Then set the relationship trough the :foreign_key. Mind for the id's, as has_one marks the id name of the other side, while the belongs_to marks the id in the same object.

It should work:

has_one :south, :class_name => 'Room', :foreign_key => 'north_id'
belongs_to :north, :class_name => 'Room', :foreign_key => 'north_id'

However I would suggest you to use a connector model and a HABTM realtionship. This model can hold information about the directions, and can be much more flexible. E.g. now you can't create a room with two "North" entrance, nor can you define a one way connection between. With it you can even find specific connections and you can alter them as you like. If you ever want more complex structre like this simple grid, then consider this.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top