Используйте полиморфизм, когда у вас есть общее свойство (например, возможность участвовать в назначениях), совместно используемое различными объектами (такими как студенты и преподаватели). Я думаю, что ваша ситуация требует полиморфизма для участников, а не назначений.
Задайте себе вопрос: существуют ли разные типы встреч или разные типы участников? Из предоставленной вами информации кажется, что у вас есть один вид встречи и разные виды участников.
Пример полиморфного участника
Назначение
class Appointment < ActiveRecord::Base
has_many :participants
has_one :tutor, :through => participants
has_many :students, :through => participants
end
Student
class Student < ActiveRecord::Base
has_many :appointments, :as => appointable
end
Репетитор
class Tutor < ActiveRecord::Base
has_many :appointments, :as => :appointable
end
Участник
# This model joins your appointable entities (Tutors and Students)
# to Appointments
class Participant < ActiveRecord::Base
belongs_to :appointment
belongs_to :appointable, :polymorphic => true
end
Что касается объявления ваших внешних ключей, Rails позаботится об этом за вас.
Миграция для участника
class CreateParticipants < ActiveRecord::Migration
def up
create_table :partipants do |t|
t.references :appointment
t.references :appointable, :polymorphic => true
end
end
def down
drop_table :participants
end
end
Чтобы лучше понять, как Rails преобразует ключевые слова, такие как polymorphic
, в ассоциации SQL, см. Руководство: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Я думаю, что конечные автоматы - интересный вариант - у меня нет опыта ни в одном из проектов конечных автоматов Ruby / Rails, поэтому я не могу дать вам совет по этому поводу.
Планирование
Это неполная картина того, как настроить планирование. Надеюсь, этого будет достаточно, чтобы вы начали.
Добавьте эти методы в Назначение:
class Appointment < ActiveRecord::Base
# Anybody may request a new appointment,
# specifying the initiator, and other participants
# they would like to attend.
def self.request requester, requested_participants=nil
a = self.new
a.status = "requested"
a.requester = requester
a.request_participation_of requested_participants
a.save!
end
# Anybody may call this method
# to request that the appointment be
# rescheduled, specifying the requester
def reschedule requester
self.status = "reschedule_requested"
requester.participation_in(self).update_attribute :status=> "requester_of_reschedule"
self.participants.where("appointable_id != ?", requester.id)
.update_all :status => "reschedule_requested"
self.save!
end
protected
def requester= requester
requester.participation_in(self).update_attribute :status => "requester"
end
def request_participation_of participants
if participants.is_a? Array
participants.each do |participant|
participant.request_participation_in self
end
else
request_participation_of [participants]
end
end
end
Модуль планирования содержит методы для преподавателей и студентов, поэтому вы можете выполнять такие действия, как student_3.request_appointment tutor_1
или tutor_1.reschedule_appointment appointment_4
.
lib/appointments/scheduling.rb
module Appointments::Scheduling
# When a Student or Tutor's participation
# in an Appointment has been requested,
# he/she indicates acceptance of the request
# with this method
def accept_participation_in appointment
self.participation_in(appointment).update_attribute :status => "accepted"
end
# Same as above, to decline the request
def decline_participation_in appointment
self.participation_in(appointment).update_attribute :status => "declined"
end
# This method finds the Student or Tutor's
# Participant object for a particular Appointment
def participation_in appointment
Participant.where(:appointment_id => appointment.id)
.find_by_appointable_id self.id
end
# A student or tutor can request a new
# Appointment with a participant or
# group of participants with this method
def request_appointment participants
Appointment.request self, participants
end
# This Student or Tutor's participation
# in an appointment can be requested with
# this method
def request_participation_in appointment
Participant.find_or_create_by_appointment_id_and_appointable_id(
:appointment_id => appointment.id,
:appointable_id => self.id
)
end
# This Student or Tutor's confirmation of
# a scheduled Appointment may be requested
# with this method
def request_reschedule_of appointment
new_status = "reschedule_requested"
if participant.id.eql? requester.id
new_status = "requester_of_reschedule"
end
self.participation_in(appointment).update_attribute :status => new_status
end
# A Student or Tutor may reschedule a
# particular Appointment with this method
def reschedule_appointment appointment
appointment.reschedule self
end
end
Как только эти модули будут установлены, вы можете включить их в соответствующие объекты:
class Appointment < ActiveRecord::Base
include Appointments::Schedulable
end
class Student < ActiveRecord::Base
include Appointments::Scheduling
end
class Tutor < ActiveRecord::Base
include Appointments::Scheduling
end
В моем примере также требуется, чтобы вы добавили поле status
для Встречи и Участника. В конце концов, я бы создал AppointmentStatus и ParticipantStatus - сначала я бы заставил систему работать без этого.
Вот полезный ресурс по созданию модулей для использования в ваших моделях: http://henrik.nyh.se/2008/02/rails-model-extensions