<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>features/coverage_of.feature</filename>
    </added>
    <added>
      <filename>lib/cucover/cli.rb</filename>
    </added>
    <added>
      <filename>lib/cucover/commands/coverage_of.rb</filename>
    </added>
    <added>
      <filename>lib/cucover/commands/cucumber.rb</filename>
    </added>
    <added>
      <filename>lib/cucover/recording.rb</filename>
    </added>
    <added>
      <filename>lib/cucover/store.rb</filename>
    </added>
    <added>
      <filename>spec/cucover/cli_spec.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,3 @@
 #!/usr/bin/env ruby
 require File.expand_path(File.dirname(__FILE__) + '../../lib/cucover')
-load Cucumber::BINARY
+Cucover::Cli.new(ARGV).start</diff>
      <filename>bin/cucover</filename>
    </modified>
    <modified>
      <diff>@@ -2,4 +2,8 @@ class Foo
   def execute
     true
   end
+
+  def sloppy_method
+    1/0
+  end
 end
\ No newline at end of file</diff>
      <filename>examples/self_test/simple/lib/foo.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ def within_examples_dir
 end
 
 Given /^I have run cucover (.*)$/ do |args|
-  When %{I run cucover &quot;#{args}&quot;}
+  When %{I run cucover #{args}}
 end
 
 Given /^the cache is clear$/ do</diff>
      <filename>features/step_definitions/main_steps.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,308 +8,234 @@ require 'rcov'
 require 'spec'
 
 $:.unshift(File.dirname(__FILE__)) 
+require 'cucover/commands/coverage_of'
+require 'cucover/commands/cucumber'
+require 'cucover/cli'
 require 'cucover/monkey'
 require 'cucover/rails'
+require 'cucover/recording'
+require 'cucover/store'
+
+# 
+# module Cucover
+#   class TestIdentifier &lt; Struct.new(:file, :line)
+#     def initialize(file_colon_line)
+#       file, line = file_colon_line.split(':')
+#       super(file, line)
+#       self.freeze
+#     end
+#     
+#     def to_s
+#       &quot;#{file}:#{line.to_s}&quot;
+#     end
+#   end
+#   
+#   class Executor
+#     def initialize(test_identifier)
+#       @source_files_cache = SourceFileCache.new(test_identifier)      
+#       @status_cache       = StatusCache.new(test_identifier)
+#     end
+#     
+#     def should_execute?
+#       dirty? || failed_on_last_run?
+#     end
+#     
+#     private
+#     
+#     def failed_on_last_run?
+#       return false unless @status_cache.exists?
+#       @status_cache.last_run_status == &quot;failed&quot;
+#     end
+#     
+#     def dirty?
+#       return true unless @source_files_cache.exists?
+#       @source_files_cache.any_dirty_files?
+#     end
+#   end
+#   
+#   class TestRun
+#     def initialize(test_identifier, visitor)
+#       @test_identifier, @visitor = test_identifier, visitor
+#       @coverage_recording = CoverageRecording.new(test_identifier)
+#       @status_cache       = StatusCache.new(test_identifier)
+#     end
+#     
+#     def record(source_file)
+#       @coverage_recording.record_file(source_file)
+#     end
+#     
+#     def fail!
+#       @failed = true
+#     end
+#     
+#     def watch(&amp;block)
+#       record(@test_identifier.file)
+#       @coverage_recording.record_coverage(&amp;block)
+#       @coverage_recording.save
+#       
+#       @status_cache.record(status)
+#     end
+#     
+#     private
+#     
+#     def status
+#       @failed ? :failed : :passed
+#     end
+#   end
+#   
+#   class &lt;&lt; self
+#     def start_test(test_identifier, visitor, &amp;block)
+#       @current_test = TestRun.new(test_identifier, visitor)
+#       @current_test.watch(&amp;block)
+#     end
+#     
+#     def fail_current_test!
+#       current_test.fail!
+#     end
+#     
+#     def can_skip?
+#       not current_test.should_execute?
+#     end
+#     
+#     private
+#     
+#     def current_test
+#       @current_test or raise(&quot;You need to start a test first, with a call to #start_test&quot;)
+#     end
+#   end
+# 
+#   module RecordsFailures
+#     def failed(exception, clear_backtrace)
+#       Cucover.fail_current_test!
+#       super
+#     end
+#   end
+#   
+#   class Controller
+#     class &lt;&lt; self
+#       def [](scenario)
+#         new(TestIdentifier.new(scenario.file_colon_line))
+#       end
+#     end
+#     
+#     def initialize(test_id)
+#       @test_id  = test_id
+#       @executor = Executor.new(test_id)
+#     end
+#     
+#     def should_skip?
+#       yield if (block_given? and !should_execute?)
+#       return !should_execute?
+#     end
+#     
+#     def should_execute?
+#       result = @executor.should_execute?      
+#       yield if block_given? and result
+#       result
+#     end
+#   end
+# 
+#   module RecordsCoverage
+#     def accept(visitor)
+#       Cucover.start_test(TestIdentifier.new(file_colon_line), visitor) do
+#         super
+#       end
+#     end
+#   end
+#   
+#   module ScenarioExtensions
+#     module SkipsStableTests    
+#       def accept(visitor)
+#         if should_skip?
+#           skip_invoke!
+#           visitor.announce &quot;[ Cucover - Skipping clean scenario ]&quot;
+#         end
+#         super
+#       end
+#       
+#       def should_skip?
+#         Cucover::Controller[self].should_skip? and (!@background or Cucover::Controller[@background].should_skip?)
+#       end
+#     end
+# 
+#     include SkipsStableTests
+#     include RecordsCoverage
+#   end
+#   
+#   module FeatureExtensions
+#     def should_skip?
+#       @feature_elements.all?{ |e| Cucover::Controller[e].should_skip? }
+#     end
+#   end
+#   
+#   module BackgroundExtensions
+#     module SkipsStableTests
+#       def accept(visitor)
+#         if (@feature.should_skip? and Cucover::Controller[self].should_skip?)
+#           skip_invoke!
+#           visitor.announce &quot;[ Cucover - Skipping background for clean feature ]&quot;
+#         end
+#         super
+#       end
+#       
+#       def skip_invoke!
+#         @step_invocations.each{ |i| i.skip_invoke! }        
+#       end
+#     end
+# 
+#     include RecordsCoverage
+#     include SkipsStableTests
+#   end
+#   
+# end
+# 
+# Cucover::Monkey.extend_every Cucumber::Ast::Feature        =&gt; Cucover::FeatureExtensions
+# Cucover::Monkey.extend_every Cucumber::Ast::Scenario       =&gt; Cucover::ScenarioExtensions
+# Cucover::Monkey.extend_every Cucumber::Ast::Background     =&gt; Cucover::BackgroundExtensions
+# Cucover::Monkey.extend_every Cucumber::Ast::StepInvocation =&gt; Cucover::RecordsFailures
 
 module Cucover
-  class TestIdentifier &lt; Struct.new(:file, :line)
-    def initialize(file_colon_line)
-      file, line = file_colon_line.split(':')
-      super(file, line)
-      self.freeze
-    end
-    
-    def to_s
-      &quot;#{file}:#{line.to_s}&quot;
-    end
-  end
-  
-  class CoverageRecording
-    def initialize(test_identifier)
-      @analyzer = Rcov::CodeCoverageAnalyzer.new        
-      @cache = SourceFileCache.new(test_identifier)
-      @covered_files = []
-    end
-    
-    def record_file(source_file)
-      @covered_files &lt;&lt; source_file unless @covered_files.include?(source_file)
-    end
-    
-    def record_coverage
-      @analyzer.run_hooked do
-        yield
-      end
-      @covered_files.concat @analyzer.analyzed_files
-    end
-    
-    def save
-      @cache.save filter(normalized_files)
-    end
-    
-    private
-    
-    def filter(files)
-      files.reject!{ |f| boring?(f) }
-    end
-    
-    def boring?(file)
-      (file.match /gem/) || (file.match /vendor/) || (file.match /lib\/ruby/)
-    end
-    
-    def normalized_files
-      @covered_files.map{ |f| File.expand_path(f).gsub(/^#{Dir.pwd}\//, '') }
-    end
-  end
-  
-  class Executor
-    def initialize(test_identifier)
-      @source_files_cache = SourceFileCache.new(test_identifier)      
-      @status_cache       = StatusCache.new(test_identifier)
-    end
-    
-    def should_execute?
-      dirty? || failed_on_last_run?
-    end
-    
-    private
-    
-    def failed_on_last_run?
-      return false unless @status_cache.exists?
-      @status_cache.last_run_status == &quot;failed&quot;
-    end
-    
-    def dirty?
-      return true unless @source_files_cache.exists?
-      @source_files_cache.any_dirty_files?
-    end
-  end
-  
-  class TestRun
-    def initialize(test_identifier, visitor)
-      @test_identifier, @visitor = test_identifier, visitor
-      @coverage_recording = CoverageRecording.new(test_identifier)
-      @status_cache       = StatusCache.new(test_identifier)
-    end
-    
-    def record(source_file)
-      @coverage_recording.record_file(source_file)
-    end
-    
-    def fail!
-      @failed = true
-    end
-    
-    def watch(&amp;block)
-      record(@test_identifier.file)
-      @coverage_recording.record_coverage(&amp;block)
-      @coverage_recording.save
-      
-      @status_cache.record(status)
-    end
-    
-    private
-    
-    def status
-      @failed ? :failed : :passed
-    end
-  end
-  
   class &lt;&lt; self
-    def start_test(test_identifier, visitor, &amp;block)
-      @current_test = TestRun.new(test_identifier, visitor)
-      @current_test.watch(&amp;block)
-    end
-    
-    def fail_current_test!
-      current_test.fail!
-    end
-    
-    def record(source_file)
-      current_test.record(source_file)
-    end
-    
-    def can_skip?
-      not current_test.should_execute?
-    end
-    
-    private
-    
-    def current_test
-      @current_test or raise(&quot;You need to start a test first, with a call to #start_test&quot;)
-    end
-  end
-  
-  class Cache
-    def initialize(test_identifier)
-      @test_identifier = test_identifier
-    end
-    
-    def exists?
-      File.exist?(cache_file)
-    end
-    
-    private
-    
-    def cache_file
-      cache_folder + '/' + cache_filename
+    def start_recording(scenario_or_table_row)
+      raise(&quot;Already recording. Please call stop first.&quot;) if recording?
+      
+      @current_recording = Recording.new(scenario_or_table_row)
+      @current_recording.start
     end
     
-    def cache_folder
-      @test_identifier.file.gsub(/([^\/]*\.feature)/, &quot;.coverage/\\1/#{@test_identifier.line.to_s}&quot;)
+    def record_file(source_file)
+      @current_recording.record_file(source_file)
     end
     
-    def time
-      File.mtime(cache_file)
+    def record_exception(exception)
+      @current_recording.fail!(exception)
     end
 
-    def write_to_cache
-      FileUtils.mkdir_p File.dirname(cache_file)
-      File.open(cache_file, &quot;w&quot;) do |file|
-        yield file
-      end
-    end
-    
-    def cache_content
-      File.readlines(cache_file)
-    end
-  end
-  
-  class StatusCache &lt; Cache
-    def last_run_status
-      cache_content.to_s.strip
-    end
-    
-    def record(status)
-      write_to_cache do |file|
-        file.puts status
-      end
+    def stop_recording
+      return unless recording?
+      @current_recording.stop
+      store.keep(@current_recording)
+      @current_recording = nil
     end
     
     private
-
-    def cache_filename
-      'last_run_status'
-    end
-  end
-  
-  class SourceFileCache &lt; Cache
-    def save(analyzed_files)
-      write_to_cache do |file|
-        file.puts analyzed_files
-      end
-    end
     
-    def any_dirty_files?
-      not dirty_files.empty?
+    def recording?
+      !!@current_recording
     end
     
-    private
-    
-    def cache_filename
-      'covered_source_files'
-    end
-
-    def source_files
-      cache_content
-    end
-
-    def dirty_files
-      source_files.select do |source_file|
-        !File.exist?(source_file.strip) or (File.mtime(source_file.strip) &gt;= time)
-      end
+    def store
+      store ||= Store.new
     end
   end
+end
 
-  module RecordsFailures
-    def failed(exception, clear_backtrace)
-      Cucover.fail_current_test!
-      super
-    end
-  end
-  
-  class Controller
-    class &lt;&lt; self
-      def [](scenario)
-        new(TestIdentifier.new(scenario.file_colon_line))
-      end
-    end
-    
-    def initialize(test_id)
-      @test_id  = test_id
-      @executor = Executor.new(test_id)
-    end
-    
-    def should_skip?
-      yield if (block_given? and !should_execute?)
-      return !should_execute?
-    end
-    
-    def should_execute?
-      result = @executor.should_execute?      
-      yield if block_given? and result
-      result
-    end
-  end
-
-  module RecordsCoverage
-    def accept(visitor)
-      Cucover.start_test(TestIdentifier.new(file_colon_line), visitor) do
-        super
-      end
-    end
-  end
-  
-  module ScenarioExtensions
-    module SkipsStableTests    
-      def accept(visitor)
-        if should_skip?
-          skip_invoke!
-          visitor.announce &quot;[ Cucover - Skipping clean scenario ]&quot;
-        end
-        super
-      end
-      
-      def should_skip?
-        Cucover::Controller[self].should_skip? and (!@background or Cucover::Controller[@background].should_skip?)
-      end
-    end
-
-    include SkipsStableTests
-    include RecordsCoverage
-  end
-  
-  module FeatureExtensions
-    def should_skip?
-      @feature_elements.all?{ |e| Cucover::Controller[e].should_skip? }
-    end
-  end
-  
-  module BackgroundExtensions
-    module SkipsStableTests
-      def accept(visitor)
-        if (@feature.should_skip? and Cucover::Controller[self].should_skip?)
-          skip_invoke!
-          visitor.announce &quot;[ Cucover - Skipping background for clean feature ]&quot;
-        end
-        super
-      end
-      
-      def skip_invoke!
-        @step_invocations.each{ |i| i.skip_invoke! }        
-      end
-    end
-
-    include RecordsCoverage
-    include SkipsStableTests
-  end
+Before do |scenario_or_table_row|
+  Cucover::Rails.patch_if_necessary  
   
+  Cucover.start_recording(scenario_or_table_row)
 end
 
-Cucover::Monkey.extend_every Cucumber::Ast::Feature        =&gt; Cucover::FeatureExtensions
-Cucover::Monkey.extend_every Cucumber::Ast::Scenario       =&gt; Cucover::ScenarioExtensions
-Cucover::Monkey.extend_every Cucumber::Ast::Background     =&gt; Cucover::BackgroundExtensions
-Cucover::Monkey.extend_every Cucumber::Ast::StepInvocation =&gt; Cucover::RecordsFailures
-
-Before do
-  Cucover::Rails.patch_if_necessary
-end
+After do
+  Cucover.stop_recording
+end
\ No newline at end of file</diff>
      <filename>lib/cucover.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>spec/cucover/lazy_scenario_spec.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>4834d5fc10dd421ccfe61f66eb70f6a282972648</id>
    </parent>
  </parents>
  <author>
    <name>Matt Wynne</name>
    <email>matt@mattwynne.net</email>
  </author>
  <url>http://github.com/mattwynne/cucover/commit/3422d80eabdffeaca975166e70637ec5476b9aef</url>
  <id>3422d80eabdffeaca975166e70637ec5476b9aef</id>
  <committed-date>2009-06-15T16:02:05-07:00</committed-date>
  <authored-date>2009-06-15T16:02:05-07:00</authored-date>
  <message>Going on a mad refactoring tangent, adding a coverage-of feature to report the coverage of a specific source file</message>
  <tree>196b1f25ddab185851d3c3bed8e3170e5aedb16b</tree>
  <committer>
    <name>Matt Wynne</name>
    <email>matt@mattwynne.net</email>
  </committer>
</commit>
