Her er en måde at modellere det på. Lad os sige, at vi har en model 'Engagement', som har et startdato-klokkeslæt, slutdato-klokkeslæt og navn. Et engagement har mange brugere gennem en anden jointabel kaldet 'user_engagements' (med tilsvarende UserEngagement-model). Så det har vi
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 et fint simpelt skema. Et engagement modellerer grundlæggende noget, der sker, og user_engagements modellerer brugere, der er booket til at gøre den ting. Vi har en antagelse (ikke skrevet ind i koden), at når de gør noget, er de ikke tilgængelige til at gøre noget andet.
Vores næste opgave er at skrive en metode, der returnerer tilgængelige brugere inden for en given tidsperiode, altså et nyt engagement. Så vi laver et engagement, og vi vil have alle de brugere, der ikke har et engagement, der krydser over med vores nye engagement. Jeg tror, at den enkleste måde at gøre dette på kan være at finde alle de brugere, der har et krydsende engagement og derefter returnere alle de brugere, der ikke er dem. Hvis du ved hvad jeg mener. En mere præcis måde at sige e2 krydser over med e1 er, at e2 starter før slutningen af e1 OG slutter efter starten af e1.
Lad os gøre dette til en metode til et engagementsobjekt, da det er fuldstændig afhængigt af et engagements data.
#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
Jeg føler, at der er en mere effektiv måde at få dette på i én forespørgsel, men jeg kan ikke helt sætte fingeren på sql.