Aquí hay una forma de modelarlo. Digamos que tenemos un modelo 'Compromiso' que tiene una fecha y hora de inicio, una fecha y hora de finalización y un nombre. Un compromiso tiene muchos usuarios, a través de otra tabla de unión llamada 'user_engagements' (con el modelo UserEngagement correspondiente). Entonces tenemos
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
Ahora tenemos un bonito esquema simple. Un compromiso básicamente modela algo que está sucediendo, y user_engagements modela a los usuarios que están reservados para hacer eso. Suponemos (no está escrito en el código) que cuando están haciendo algo, no están disponibles para hacer nada más.
Nuestra próxima tarea es escribir un método que devuelva a los usuarios disponibles dentro de un período de tiempo determinado, es decir, un nuevo compromiso. Entonces, hacemos un compromiso y queremos que todos los usuarios que no tienen un compromiso se crucen con nuestro nuevo compromiso. Creo que la forma más sencilla de hacer esto podría ser encontrar a todos los usuarios que tienen un compromiso cruzado y luego devolver a todos los usuarios que no son ellos. Si sabes a lo que me refiero. Una forma más precisa de decir que e2 se cruza con e1 es que e2 comienza antes del final de e1 Y termina después del inicio de e1.
Hagamos de esto un método de un objeto de compromiso, ya que depende totalmente de los datos de un compromiso.
#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
Siento que hay una forma más eficiente de obtener esto en una sola consulta, pero no puedo identificar el sql.