views:

132

answers:

3

Take the following simple object model for example:

class Course
  has_many :enrollments
  has_many :students, :through => :enrollments, :after_add => :send_email

  def send_email(student)
    puts "Email Sent"
  end
end

class Enrollment
  belongs_to :course
  belongs_to :student
end

class Student
  has_many :enrollments
  has_many :courses, :through => :enrollments
end

I would like to perform send_email after one or more Students are added to a Course, however after_add is fired after each item is added to the students collection.

bill = Student.first
carl = Student.last

Course.first.students << [bill, carl]

Will output

Email Sent
Email Sent

How can I catch a SINGLE event after ALL items are added to a collection?

Is it possible to insert records into the enrollments table in a single query for all students rather than one per student?

Does Rails have any built in mechanism or do I need to write custom logic to catch then batch flush calls to send_email?

Thanks for any information!

A: 

I checked and there doesn't look like any of the built-in callbacks will do what you need to do. You may want to experiment with custom callback classes though to see if you can achieve what you need.

http://guides.rubyonrails.org/activerecord%5Fvalidations%5Fcallbacks.html#callback-classes

Good luck :)

cakeforcerberus
A: 

how about :

@last_student = Student.last

def send_email(student)
    put 'email sent' if student == @last_student
end

It maybe a little messy to use a instance variable, but should do the job.

ez
+2  A: 

Adding an after_create callback in the Enrollment model will get the behaviour you want.

On batch assignments in a has_many :through relationship will create a entries in the enrollments table as necessary. For each new entry Rails will apply all the validations and callbacks in the join model. If a student's enrollment in a course is noted by an entry in the enrollments join table. Then this will trigger no matter how you add the student to the course.

This is the code to modify the Enrollment class

class Enrollment < ActiveRecord::Base
  belongs_to :course
  belongs_to :student
  after_create :send_email

  protected
    def send_email
      course.send_email(student)
    end
end

It will call the course's send_email routine so long as you crate an enrollment record with Rails.

The following will send emails.

@student.courses << @course
@course.students << @student
Enrollment.create :student => @student, :course => @course
EmFi