Här är ett sätt att modellera det. Låt oss säga att vi har en modell 'Engagement' som har ett startdatumtid, slutdatumtid och namn. Ett engagemang har många användare, genom en annan kopplingstabell som heter 'user_engagements' (med motsvarande UserEngagement-modell). Så vi har
User
has_many :user_engagements
has_many :engagements, :through => :user_engagements
Engagement
#fields - starts_at, ends_at (both datetime)
has_many :user_engagements
has_many :users, :through => :user_engagements
UserEngagement
belongs_to :user
belongs_to :engagement
Nu har vi ett trevligt enkelt schema. Ett engagemang modellerar i princip något som händer, och user_engagements modellerar användare som är bokade att göra den saken. Vi har ett antagande (inte inskrivet i koden) att när de gör något är de inte tillgängliga för att göra något annat.
Vår nästa uppgift är att skriva en metod som returnerar användare tillgängliga inom en given tidsperiod, alltså ett nytt engagemang. Så vi gör ett engagemang och vi vill att alla användare som inte har ett engagemang som korsar över med vårt nya engagemang. Jag tror att det enklaste sättet att göra detta kan vara att hitta alla användare som har ett korsande engagemang och sedan returnera alla användare som inte är dem. Om du vet vad jag menar. Ett mer exakt sätt att säga att e2 korsar över med e1 är att e2 börjar före slutet av e1 OCH slutar efter början av e1.
Låt oss göra detta till en metod för ett engagemangobjekt eftersom det är helt beroende av ett engagemangsdata.
#in Engagement
def unavailable_user_ids
User.find(:all, :include => [:user_engagements], :select => "users.id", :conditions => ["user_engagements.starts_at < ? and user_engagements.ends_at > ?", self.ends_at, self.starts_at]).collect(&:id)
end
def available_users
User.find(:all, :conditions => ["id not in (?)", self.unavailable_user_ids])
end
Jag känner att det finns ett mer effektivt sätt att få detta i en fråga, men jag kan inte riktigt sätta fingret på sql.