Forum: Ruby on Rails Two-to-one mappings

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
A6e8497213547aac53e68d57c03ee4af?d=identicon&s=25 Greg Brockman (Guest)
on 2009-02-14 23:54
(Received via mailing list)
Hey all,

I have a pretty simple question, but I'm not sure of a good solution
to it.  Essentially, I want to provide a two-to-one mapping of models.
 I'm working on an application for a contest, where every (unordered)
combination of two Rounds is supposed to be assigned to one Room.  Any
Room might have many different combinations of Rounds, however.

What is the Right Way of doing this in Rails?  Maybe create a model
that holds the associated foreign keys?  Also, what would be a good
way to scale this out, if I wanted to be able to map unordered
n-element collections of Rounds to obtain a Room?

Thanks,

Greg
A91bd6cef23eb3516245a092e196c4da?d=identicon&s=25 Maurício Linhares (mauricio)
on 2009-02-15 05:42
(Received via mailing list)
Imagining that every round must belong to a room, here's a simple and
straightforward implementation:

class Room < ActiveRecord::Base
  # this class needs a max_rounds and rounds_count integer columns
  has_many :rounds

  def accepts_more_rounds?
    max_rounds < rounds_count
  end

end

class Round < ActiveRecord::Base
  # this class needs a room_id integer column
  belongs_to :room, :counter_cache => true

  validate_on_create :validate_rounds_count

  protected

  def validate_rounds_count
    self.room.reload
    unless self.room.accepts_more_rounds?
      errors.add( :room, "has already met it's rounds limit" )
    end
  end


end

room = Room.create( :max_rounds => 2 )
round_1 = room.rounds.create( :name => 'round 1' )
round_2 = room.rounds.create( :name => 'round 2' )
round_3 = room.rounds.create( :name => 'round 3' ) # this one isn't
going to be created
round_3.new_record? == true


-
Maurício Linhares
http://alinhavado.wordpress.com/ (pt-br) | http://blog.codevader.com/
(en)



On Sat, Feb 14, 2009 at 7:53 PM, Greg Brockman
53be54e5db4dc58e4980db5a8255621b?d=identicon&s=25 Harold (Guest)
on 2009-02-15 05:47
(Received via mailing list)
That's a clean solution, however I don't know if it satisfies the fact
that "any Room might have many different combinations of Rounds"

Not sure I understand correctly, but if a room can have many
combination of rounds, and each combination of rounds has more than
one round, you could try this:

Room and Round models I assume you already have. A room can have many
round_combinations (create the RoundCombination model with a room_id.
Room :has_many :room_combinations, and
RoomCombination :belongs_to :room). Then create a join table between
round_combinations and rounds (HABTM).

You can use callbacks or validation to limit the relationship to two
rounds maximum.

On Feb 14, 11:42 pm, Maurício Linhares <mauricio.linha...@gmail.com>
A6e8497213547aac53e68d57c03ee4af?d=identicon&s=25 Greg (Guest)
on 2009-02-15 07:58
(Received via mailing list)
Cool!  Harold, your solution strikes me as being exactly the way to do
it.  I've implemented it, and things seem to be sailing smoothly.
Thanks a lot to both of you.

Sincerely,

Greg
53be54e5db4dc58e4980db5a8255621b?d=identicon&s=25 Harold A. Giménez Ch. (Guest)
on 2009-02-15 08:50
(Received via mailing list)
Great! Glad it worked.
A6e8497213547aac53e68d57c03ee4af?d=identicon&s=25 Greg (Guest)
on 2009-02-15 17:17
(Received via mailing list)
Ah, so one follow up question: each Competitor in the competition is
signed up for two rounds, and needs to know which room he or she is
assigned.  This is a has_one relationship; however, I'm not quite sure
of the right way to map it.  Thoughts?

Greg

On Feb 15, 2:49 am, Harold A. Giménez Ch. <harold.gime...@gmail.com>
53be54e5db4dc58e4980db5a8255621b?d=identicon&s=25 Harold (Guest)
on 2009-02-15 23:56
(Received via mailing list)
Does a user have many round_combinations? and a round_combination has
many users? Seems like it. If so, first thought is to create another
HABTM table between round_combinations and users. Then
@user.round_combinations.each { |rc| rc.room } gives you the rooms...
53be54e5db4dc58e4980db5a8255621b?d=identicon&s=25 Harold A. Gimenez (Guest)
on 2009-02-16 00:07
(Received via mailing list)
Actually, by user I mean competitor, but you get the point...
A6e8497213547aac53e68d57c03ee4af?d=identicon&s=25 Greg (Guest)
on 2009-02-16 01:49
(Received via mailing list)
Ah, sorry, my specification was not very good.  The Big Picture here
is that we assign each Competitor to a room based on the particular
combination of Rounds he or she is signed up for.  So for example, we
could have

r = RoundCombination.new
r.room = room_1
r.rounds = [round_1, round_2]
r.save

Now if c is a Competitor who is signed up for round_1 and round_2, I
would like to be able to do

c.room
#=> room_1

So essentially, a HABTM table does not seem to be the right way of
capturing this relationship.  A Competitor "knows" its
RoundCombination because the Competitor is signed up for a particular
combination of Rounds.  I've managed a provisional, rather inefficient
implementation, of c.room as

class Competitor < ActiveRecord::Base
  has_many :rounds

  def room
    RoundCombination.all.each do |rc|
      return rc.room if rc.rounds.sort_by {|r| r.id} == rounds.sort_by
{|r| r.id}
    end
    nil
  end
end

I feel this should be done with some SQL joins or something.  Does
Rails provide a relationship to manage this?

Thanks again,

Greg


On Feb 15, 6:06 pm, "Harold A. Gimenez" <harold.gime...@gmail.com>
53be54e5db4dc58e4980db5a8255621b?d=identicon&s=25 Harold A. Giménez Ch. (Guest)
on 2009-02-16 03:29
(Received via mailing list)
From what I understand, one competitor will have as many rooms as
RoundCombinations. Your room definition below would return the first one
it
finds.

I think you can do something like:

@competitor.rounds.each { |round| round.round_combination.room }

Would return an array of rooms for each of @competitor's rounds.

That sound right?
A6e8497213547aac53e68d57c03ee4af?d=identicon&s=25 Greg (Guest)
on 2009-02-16 05:48
(Received via mailing list)
Not quite, unfortunately.  The point here (that is making things
complex), is that

round.round_combination

has no semantic.  Rather, the semantic is more like

[round_1, round_2].round_combination

That is, an (unordered) set of two rounds determines the
round_combination.

On the flip side, a Competitor has one Room.  As motivation, what's
going on here is that Competitors are competing in a two-round math
competition.  They each sign up for two rounds ahead of time, and we
put them in a room determined by which two rounds they signed up for.
(Hence, a Competitor also has one RoundCombination; it's just not
clear to me how the join query should work... I'm starting to think
maybe I should just write that by hand.)  In any case, the code I
provided has the right functionality, but it's bad in the sense that
it loads all of the RoundCombinations as AR objects each time its
called, which is a lot of overhead...

Greg

On Feb 15, 9:29 pm, Harold A. Giménez Ch. <harold.gime...@gmail.com>
This topic is locked and can not be replied to.