<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/unit/plugins/builder_plugin_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -298,9 +298,8 @@ class Project
       log_changeset(build.artifacts_directory, reasons)
       update_project_to_revision(build, revision)
 
-      if config_tracker.config_modified?
+      if config_modified?
         build.abort
-        notify(:configuration_modified)
         throw :reload_project
       end
     
@@ -324,10 +323,14 @@ class Project
   end
 
   def notify(event, *event_parameters)
+    unless BuilderPlugin.known_event? event
+      raise &quot;You attempted to notify the project of the #{event} event, but the plugin architecture does not understand this event. Add a method to BuilderPlugin, and document it.&quot;
+    end
+    
     errors = []
     results = @plugins.collect do |plugin| 
       begin
-        plugin.send(event, *event_parameters) if plugin.respond_to?(event)
+        plugin.send(event, *event_parameters) if plugin.respond_to? event
       rescue =&gt; plugin_error
         CruiseControl::Log.error(plugin_error)
         if (event_parameters.first and event_parameters.first.respond_to? :artifacts_directory)</diff>
      <filename>app/models/project.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,21 @@
+# BuilderPlugin is the superclass of all CC.rb plugins. It does not provide any functionality
+# except a basic initializer that accepts as an argument the current project.
+# 
+# CC.rb plugins offer a rich notification system for tracking every aspect of the build lifecycle. In rough order,
+# they are:
+#
+# * polling_source_control
+# * no_new_revisions_detected OR new_revisions_detected(revisions)
+# * build_requested
+# * queued
+# * timed_out
+# * build_initiated
+# * configuration_modified
+# * build_started
+# * build_finished
+# * build_broken OR build_fixed
+# * build_loop_failed
+# * sleeping
 class BuilderPlugin
   attr_reader :project
   
@@ -5,4 +23,69 @@ class BuilderPlugin
     @project = project
   end
   
+  class &lt;&lt; self
+    def known_event?(event_name)
+      self.instance_methods(false).include? event_name.to_s
+    end
+  end
+  
+  # Called by ChangeInSourceControlTrigger to indicate that it is about to poll source control.
+  def polling_source_control
+  end
+  
+  # Called by ChangeInSourceControlTrigger to indicate that no new revisions have been detected.
+  def no_new_revisions_detected
+  end
+  
+  # Called by ChangeInSourceControlTrigger to indicate that new revisions were detected.
+  def new_revisions_detected(revisions)
+  end
+  
+  # Called by Project to indicate that a build has explicitly been requested by the user.
+  def build_requested
+  end  
+  
+  # Called by BuildSerializer if it another build is still running and it cannot acquire the build serialization lock.
+  # It will retry until it times out. Occurs only if build serialization is enabled in your CC.rb configuration.
+  def queued
+  end
+  
+  # Called by BuildSerializer if it times out attempting to acquire the build serialization lock due to another build
+  # still running. Occurs only if build serialization is enabled in your CC.rb configuration.
+  def timed_out
+  end
+  
+  # Called by Project at the start of a new build before any other build events.
+  def build_initiated
+  end
+  
+  # Called by Project at the start of a new build to indicate that the configuration has been modified,
+  # after which the build is aborted.
+  def configuration_modified
+  end
+
+  # Called by Project after some basic logging and the configuration_modified check and just before the build begins running, 
+  def build_started(build)
+  end
+  
+  # Called by Project immediately after the build has finished running.
+  def build_finished(build)
+  end
+  
+  # Called by Project after the completion of a build if the previous build was successful and this one is a failure.
+  def build_broken(build, previous_build)
+  end
+  
+  # Called by Project after the completion of a build if the previous build was a failure and this one was successful.
+  def build_fixed(build, previous_build)
+  end
+  
+  # Called by Project if the build fails internally with a CC.rb exception.
+  def build_loop_failed(exception)
+  end
+  
+  # Called by Project at the end of a build to indicate that the build loop is once again sleeping.
+  def sleeping
+  end
+  
 end
\ No newline at end of file</diff>
      <filename>lib/builder_plugins/builder_plugin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -227,6 +227,7 @@ class ProjectTest &lt; Test::Unit::TestCase
       listener = Object.new
       listener.expects(:sleeping).raises(StandardError.new(&quot;Listener failed&quot;))
       listener.expects(:doing_something).with(:foo).raises(StandardError.new(&quot;Listener failed with :foo&quot;))
+      BuilderPlugin.stubs(:known_event?).returns true
 
       @project.add_plugin listener
 
@@ -313,8 +314,8 @@ class ProjectTest &lt; Test::Unit::TestCase
   end
 
   def test_notify_should_handle_plugin_error
+    BuilderPlugin.expects(:known_event?).with(:hey_you).returns true
     plugin = Object.new
-    
     @project.plugins &lt;&lt; plugin
     
     plugin.expects(:hey_you).raises(&quot;Plugin talking&quot;)
@@ -323,6 +324,7 @@ class ProjectTest &lt; Test::Unit::TestCase
   end
 
   def test_notify_should_handle_multiple_plugin_errors
+    BuilderPlugin.stubs(:known_event?).with(:hey_you).returns true
     plugin1 = Object.new
     plugin2 = Object.new
     
@@ -647,6 +649,13 @@ class ProjectTest &lt; Test::Unit::TestCase
     end
   end
   
+  def test_notifying_project_of_an_unknown_event_raises_exception
+    BuilderPlugin.expects(:known_event?).returns false
+    assert_raises RuntimeError do
+      @project.notify :some_random_event
+    end
+  end
+  
   private
   
   def stub_build(label)</diff>
      <filename>test/unit/project_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>6731b7adfca98e57b5c7db363420b148040f796b</id>
    </parent>
  </parents>
  <author>
    <name>Brian Guthrie</name>
    <email>btguthrie@gmail.com</email>
  </author>
  <url>http://github.com/thoughtworks/cruisecontrol.rb/commit/de819e6f63027e00d9389facc8c3f5ffba8c612b</url>
  <id>de819e6f63027e00d9389facc8c3f5ffba8c612b</id>
  <committed-date>2009-07-01T13:13:16-07:00</committed-date>
  <authored-date>2009-07-01T13:13:16-07:00</authored-date>
  <message>Project#notify raises an exception if you try to notify plugins of an event that hasn't been documented in BuilderPlugin. Not coincidentally, BuilderPlugin now provides documentation and an interface for all event callbacks.</message>
  <tree>e4514a2dbce8b23777595c09e7dc842cccd05172</tree>
  <committer>
    <name>Brian Guthrie</name>
    <email>btguthrie@gmail.com</email>
  </committer>
</commit>
