<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,4 @@
 Dir['tasks/*.rake'].each { |file| load(file) }
- 
+
+GithubGem::RakeTasks.new(:gem)
 task :default =&gt; [:spec]</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,255 +1,312 @@
 require 'rubygems'
-require 'rubyforge'
 require 'rake'
 require 'rake/tasklib'
 require 'date'
+require 'git'
 
-module Rake 
+module GithubGem
   
-  class GithubGem &lt; TaskLib
+  # Detects the gemspc file of this project using heuristics.
+  def self.detect_gemspec_file
+    FileList['*.gemspec'].first
+  end
+  
+  # Detects the main include file of this project using heuristics
+  def self.detect_main_include
+    if detect_gemspec_file =~ /^(\.*)\.gemspec$/ &amp;&amp; File.exist?(&quot;lib/#{$1}.rb&quot;)
+      &quot;lib/#{$1}.rb&quot;
+    elsif FileList['lib/*.rb'].length == 1
+      FileList['lib/*.rb'].first
+    else
+      raise &quot;Could not detect main include file!&quot;
+    end
+  end
+  
+  class RakeTasks
+    
+    attr_reader   :gemspec, :modified_files, :git
+    attr_accessor :gemspec_file, :task_namespace, :main_include, :root_dir, :spec_pattern, :test_pattern, :remote, :remote_branch, :local_branch
     
-    attr_accessor :name
-    attr_accessor :specification
-   
-    def self.define_tasks!
-      gem_task_builder = Rake::GithubGem.new      
-      gem_task_builder.register_all_tasks!
+    # Initializes the settings, yields itself for configuration 
+    # and defines the rake tasks based on the gemspec file.
+    def initialize(task_namespace = :gem)
+      @gemspec_file   = GithubGem.detect_gemspec_file
+      @task_namespace = task_namespace
+      @main_include   = GithubGem.detect_main_include
+      @modified_files = []
+      @root_dir       = Dir.pwd
+      @test_pattern   = 'test/**/*_test.rb'
+      @spec_pattern   = 'spec/**/*_spec.rb'
+      @local_branch   = 'master'
+      @remote         = 'origin'
+      @remote_branch  = 'master'
+      
+      yield(self) if block_given?
+
+      @git = Git.open(@root_dir)
+      load_gemspec!
+      define_tasks!
     end
     
+    protected
+    
+    # Define Unit test tasks
+    def define_test_tasks!
+      require 'rake/testtask'
 
-    def initialize
-      reload_gemspec!
+      namespace(:test) do
+        Rake::TestTask.new(:basic) do |t|
+          t.pattern = test_pattern
+          t.verbose = true
+          t.libs &lt;&lt; 'test'
+        end
+      end
+      
+      desc &quot;Run all unit tests for #{gemspec.name}&quot;
+      task(:test =&gt; ['test:basic'])
     end
+    
+    # Defines RSpec tasks
+    def define_rspec_tasks!
+      require 'spec/rake/spectask'
 
-    def register_all_tasks!
-      namespace(:gem) do
-        desc &quot;Updates the file lists for this gem&quot;
-        task(:manifest) { manifest_task }
+      namespace(:spec) do
+        desc &quot;Verify all RSpec examples for #{gemspec.name}&quot;
+        Spec::Rake::SpecTask.new(:basic) do |t|
+          t.spec_files = FileList[spec_pattern]
+        end
+        
+        desc &quot;Verify all RSpec examples for #{gemspec.name} and output specdoc&quot;
+        Spec::Rake::SpecTask.new(:specdoc) do |t|
+          t.spec_files = FileList[spec_pattern]
+          t.spec_opts &lt;&lt; '--format' &lt;&lt; 'specdoc' &lt;&lt; '--color'
+        end
+        
+        desc &quot;Run RCov on specs for #{gemspec.name}&quot;
+        Spec::Rake::SpecTask.new(:rcov) do |t|
+          t.spec_files = FileList[spec_pattern]
+          t.rcov = true
+          t.rcov_opts = ['--exclude', '&quot;spec/*,gems/*&quot;', '--rails']          
+        end          
+      end
+      
+      desc &quot;Verify all RSpec examples for #{gemspec.name} and output specdoc&quot;
+      task(:spec =&gt; ['spec:specdoc'])      
+    end
+    
+    # Defines the rake tasks
+    def define_tasks!
+      
+      define_test_tasks!  if has_tests?
+      define_rspec_tasks! if has_specs?
+      
+      namespace(@task_namespace) do
+        desc &quot;Updates the filelist in the gemspec file&quot;
+        task(:manifest) { manifest_task } 
+        
+        desc &quot;Builds the .gem package&quot;
+        task(:build =&gt; :manifest) { build_task }
 
-        desc &quot;Releases a new version of #{@name}&quot;
-        task(:build =&gt; [:manifest]) { build_task } 
+        desc &quot;Sets the version of the gem in the gemspec&quot;
+        task(:set_version =&gt; [:check_version, :check_current_branch]) { version_task }
+        task(:check_version =&gt; :fetch_origin) { check_version_task }      
         
+        task(:fetch_origin) { fetch_origin_task }
+        task(:check_current_branch) { check_current_branch_task }        
+        task(:check_clean_status) { check_clean_status_task }
+        task(:check_not_diverged =&gt; :fetch_origin) { check_not_diverged_task }
         
-        release_dependencies = [:check_clean_master_branch, :version, :build, :create_tag]
-        release_dependencies.push 'doc:publish' if has_rdoc?
-        release_dependencies.unshift 'test' if has_tests?
-        release_dependencies.unshift 'spec' if has_specs?
+        checks = [:check_current_branch, :check_clean_status, :check_not_diverged, :check_version]
+        checks.unshift('spec:basic') if has_specs?
+        checks.unshift('test:basic') if has_tests?
+        checks.push &lt;&lt; [:check_rubyforge] if gemspec.rubyforge_project
                 
-        desc &quot;Releases a new version of #{@name}&quot;
-        task(:release =&gt; release_dependencies) { release_task } 
+        desc &quot;Perform all checks that would occur before a release&quot;
+        task(:release_checks =&gt; checks) 
+
+        release_tasks = [:release_checks, :set_version, :build, :github_release]
+        release_tasks &lt;&lt; [:rubyforge_release] if gemspec.rubyforge_project
         
-        # helper task for releasing
-        task(:check_clean_master_branch) { verify_clean_status('master') }
-        task(:check_version) { verify_version(ENV['VERSION'] || @specification.version) }
-        task(:version =&gt; [:check_version]) { set_gem_version! }
-        task(:create_tag) { create_version_tag! }
-      end
-      
-      # Register RDoc tasks
-      if has_rdoc?
-        require 'rake/rdoctask'
+        desc &quot;Release a new verison of the gem&quot;
+        task(:release =&gt; release_tasks) { release_task }
         
-        namespace(:doc) do 
-          desc 'Generate documentation for request-log-analyzer'
-          Rake::RDocTask.new(:compile) do |rdoc|
-            rdoc.rdoc_dir = 'doc'
-            rdoc.title    = @name
-            rdoc.options += @specification.rdoc_options
-            rdoc.rdoc_files.include(@specification.extra_rdoc_files)
-            rdoc.rdoc_files.include('lib/**/*.rb')
-          end
-          
-          desc &quot;Publish RDoc files for #{@name} to Github&quot;
-          task(:publish =&gt; :compile) do
-            sh 'git checkout gh-pages'
-            sh 'git pull origin gh-pages'
-            sh 'cp -rf doc/* .'
-            sh &quot;git commit -am \&quot;Publishing newest RDoc documentation for #{@name}\&quot;&quot;
-            sh &quot;git push origin gh-pages&quot;
-            sh &quot;git checkout master&quot;
-          end
-        end
+        task(:check_rubyforge)   { check_rubyforge_task }
+        task(:rubyforge_release) { rubyforge_release_task }
+        task(:github_release =&gt; [:commit_modified_files, :tag_version]) { github_release_task }
+        task(:tag_version) { tag_version_task }
+        task(:commit_modified_files) { commit_modified_files_task }
+        
+        desc &quot;Updates the gem release tasks with the latest version on Github&quot;
+        task(:update_tasks) { update_tasks_task }
       end
+    end
     
-      # Setup :spec task if RSpec files exist
-      if has_specs?
-        require 'spec/rake/spectask'
-
-        desc &quot;Run all specs for #{@name}&quot;
-        Spec::Rake::SpecTask.new(:spec) do |t|
-          t.spec_files = FileList['spec/**/*_spec.rb']
-        end
-      end
+    # Updates the files list and test_files list in the gemspec file using the list of files
+    # in the repository and the spec/test file pattern.
+    def manifest_task
+      # Load all the gem's files using &quot;git ls-files&quot;
+      repository_files = git.ls_files.keys
+      test_files       = Dir[test_pattern] + Dir[spec_pattern]
       
-      # Setup :test task if unit test files exist
-      if has_tests?
-        require 'rake/testtask'
-
-        desc &quot;Run all unit tests for #{@name}&quot;
-        Rake::TestTask.new(:test) do |t|
-          t.pattern = 'test/**/*_test.rb'
-          t.verbose = true
-          t.libs &lt;&lt; 'test'
-        end
-      end      
+      update_gemspec(:files, repository_files)
+      update_gemspec(:test_files, repository_files &amp; test_files)
     end
     
-    protected 
-
-    def has_rdoc?
-      branch_exist?('gh-pages')
+    # Builds the gem
+    def build_task
+      sh &quot;gem build -q #{gemspec_file}&quot;
+      Dir.mkdir('pkg') unless File.exist?('pkg')
+      sh &quot;mv #{gemspec.name}-#{gemspec.version}.gem pkg/#{gemspec.name}-#{gemspec.version}.gem&quot; 
     end
-
-    def has_specs?
-      Dir['spec/**/*_spec.rb'].any?
+    
+    # Updates the version number in the gemspec file, the VERSION constant in the main
+    # include file and the contents of the VERSION file.
+    def version_task
+      update_gemspec(:version, ENV['VERSION']) if ENV['VERSION']
+      update_gemspec(:date, Date.today)
+      
+      update_version_file(gemspec.version)
+      update_version_constant(gemspec.version)
     end
     
-    def has_tests?
-      Dir['test/**/*_test.rb'].any?
+    def check_version_task
+      raise &quot;#{ENV['VERSION']} is not a valid version number!&quot; if ENV['VERSION'] &amp;&amp; !Gem::Version.correct?(ENV['VERSION'])
+      proposed_version = Gem::Version.new(ENV['VERSION'] || gemspec.version)
+      # Loads the latest version number using the created tags
+      newest_version   = git.tags.map { |tag| tag.name.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max      
+      raise &quot;This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})&quot; if newest_version &amp;&amp; newest_version &gt;= proposed_version
     end
-
-    def reload_gemspec!
-      raise &quot;No gemspec file found!&quot; if gemspec_file.nil?      
-      spec = File.read(gemspec_file)
-      @specification = eval(spec)
-      @name = specification.name  
+    
+    # Checks whether the current branch is not diverged from the remote branch
+    def check_not_diverged_task
+      raise &quot;The current branch is diverged from the remote branch!&quot; if git.log.between('HEAD', git.branches[&quot;#{remote}/#{remote_branch}&quot;].gcommit).any?
     end
-
-    def run_command(command)
-      lines = []
-      IO.popen(command) { |f| lines = f.readlines }
-      return lines
+    
+    # Checks whether the repository status ic clean
+    def check_clean_status_task
+      raise &quot;The current working copy contains modifications&quot; if git.status.changed.any?
+    end
+    
+    # Checks whether the current branch is correct
+    def check_current_branch_task
+      raise &quot;Currently not on #{local_branch} branch!&quot; unless git.branch.name == local_branch.to_s
     end
     
-    def git_modified?(file)
-      return !run_command('git status').detect { |line| Regexp.new(Regexp.quote(file)) =~ line }.nil?
+    # Fetches the latest updates from Github
+    def fetch_origin_task
+      git.fetch('origin')
     end
     
-    def git_commit_file(file, message, branch = nil)
-      verify_current_branch(branch) unless branch.nil?
-      if git_modified?(file)
-        sh &quot;git add #{file}&quot;
-        sh &quot;git commit -m \&quot;#{message}\&quot;&quot;
-      else
-        raise &quot;#{file} is not modified and cannot be committed!&quot;
+    # Commits every file that has been changed by the release task.
+    def commit_modified_files_task
+      if modified_files.any?
+        modified_files.each { |file| git.add(file) }
+        git.commit(&quot;Released #{gemspec.name} gem version #{gemspec.version}&quot;)
       end
     end
     
-    def git_create_tag(tag_name, message)
-      sh &quot;git tag -a \&quot;#{tag_name}\&quot; -m \&quot;#{message}\&quot;&quot;
+    # Adds a tag for the released version
+    def tag_version_task
+      git.add_tag(&quot;#{gemspec.name}-#{gemspec.version}&quot;)
     end
     
-    def git_push(remote = 'origin', branch = 'master', options = [])
-      verify_clean_status(branch)
-      options_str = options.map { |o| &quot;--#{o}&quot;}.join(' ')
-      sh &quot;git push #{options_str} #{remote} #{branch}&quot;
+    # Pushes the changes and tag to github
+    def github_release_task
+      git.push(remote, remote_branch, true)
     end
     
-    def gemspec_version=(new_version)
-      spec = File.read(gemspec_file)
-      spec.gsub!(/^(\s*s\.version\s*=\s*)('|&quot;)(.+)('|&quot;)(\s*)$/) { &quot;#{$1}'#{new_version}'#{$5}&quot; }
-      spec.gsub!(/^(\s*s\.date\s*=\s*)('|&quot;)(.+)('|&quot;)(\s*)$/) { &quot;#{$1}'#{Date.today.strftime('%Y-%m-%d')}'#{$5}&quot; }    
-      File.open(gemspec_file, 'w') { |f| f &lt;&lt; spec }
-      reload_gemspec!      
+    # Checks whether Rubyforge is configured properly
+    def check_rubyforge_task
+      raise &quot;Could not login on rubyforge!&quot; unless `rubyforge login 2&gt;&amp;1`.strip.empty?
+      output = `rubyforge names`.split(&quot;\n&quot;)
+      raise &quot;Rubyforge group not found!&quot;   unless output.any? { |line| %r[^groups\s*\:.*\b#{Regexp.quote(gemspec.rubyforge_project)}\b.*] =~ line }
+      raise &quot;Rubyforge package not found!&quot; unless output.any? { |line| %r[^packages\s*\:.*\b#{Regexp.quote(gemspec.name)}\b.*] =~ line }      
     end
     
-    def gemspec_date=(new_date)
-      spec = File.read(gemspec_file)
-      spec.gsub!(/^(\s*s\.date\s*=\s*)('|&quot;)(.+)('|&quot;)(\s*)$/) { &quot;#{$1}'#{new_date.strftime('%Y-%m-%d')}'#{$5}&quot; }    
-      File.open(gemspec_file, 'w') { |f| f &lt;&lt; spec }
-      reload_gemspec!       
+    # Task to release the .gem file toRubyforge.
+    def rubyforge_release_task
+      sh 'rubyforge', 'add_release', gemspec.rubyforge_project, gemspec.name, gemspec.version.to_s, &quot;pkg/#{gemspec.name}-#{gemspec.version}.gem&quot;
     end
     
-    def gemspec_file 
-      @gemspec_file ||= Dir['*.gemspec'].first
+    # Gem release task.
+    # All work is done by the task's dependencies, so just display a release completed message.
+    def release_task
+      puts
+      puts '------------------------------------------------------------'
+      puts &quot;Released #{gemspec.name} version #{gemspec.version}&quot;
     end
     
-    def verify_current_branch(branch)
-      run_command('git branch').detect { |line| /^\* (.+)/ =~ line }
-      raise &quot;You are currently not working in the master branch!&quot; unless branch == $1
+    private
+    
+    # Checks whether this project has any RSpec files
+    def has_specs?
+      FileList[spec_pattern].any?
     end
-
-    def branch_exist?(branch)
-      run_command('git branch').any? { |line| line.split(' ').last == branch }
+    
+    # Checks whether this project has any unit test files
+    def has_tests?
+      FileList[test_pattern].any?
     end
-
     
-    def verify_clean_status(on_branch = nil)
-      sh &quot;git fetch&quot;
-      lines = run_command('git status')
-      raise &quot;You don't have the most recent version available. Run git pull first.&quot; if /^\# Your branch is behind/ =~ lines[1]
-      raise &quot;You are currently not working in the #{on_branch} branch!&quot; unless on_branch.nil? || (/^\# On branch (.+)/ =~ lines.first &amp;&amp; $1 == on_branch)
-      raise &quot;Your master branch contains modifications!&quot; unless /^nothing to commit \(working directory clean\)/ =~ lines.last
+    # Loads the gemspec file
+    def load_gemspec!
+      @gemspec = eval(File.read(@gemspec_file))
     end
     
-    def verify_version(new_version)
-      newest_version = run_command('git tag').map { |tag| tag.split(name + '-').last }.compact.map { |v| Gem::Version.new(v) }.max
-      raise &quot;This version number (#{new_version}) is not higher than the highest tagged version (#{newest_version})&quot; if !newest_version.nil? &amp;&amp; newest_version &gt;= Gem::Version.new(new_version.to_s)
+    # Updates the VERSION file with the new version
+    def update_version_file(version)
+      if File.exists?('VERSION')
+        File.open('VERSION', 'w') { |f| f &lt;&lt; version.to_s } 
+        modified_files &lt;&lt; 'VERSION'
+      end
     end
     
-    def set_gem_version!
-      # update gemspec file
-      self.gemspec_version = ENV['VERSION'] if Gem::Version.correct?(ENV['VERSION'])
-      self.gemspec_date    = Date.today
+    # Updates the VERSION constant in the main include file if it exists
+    def update_version_constant(version)
+     file_contents = File.read(main_include)
+     if file_contents.sub!(/^(\s+VERSION\s*=\s*)[^\s].*$/) { $1 + version.to_s.inspect }
+       File.open(main_include, 'w') { |f| f &lt;&lt; file_contents }      
+       modified_files &lt;&lt; main_include
+     end
     end
-
-    def manifest_task
-      verify_current_branch('master')
+    
+    # Updates an attribute of the gemspec file.
+    # This function will open the file, and search/replace the attribute using a regular expression.
+    def update_gemspec(attribute, new_value, literal = false)
       
-      list = Dir['**/*'].sort
-      list -= [gemspec_file]
-
-      if File.exist?('.gitignore')
-        File.read('.gitignore').each_line do |glob|
-          glob = glob.chomp.sub(/^\//, '')
-          list -= Dir[glob]
-          list -= Dir[&quot;#{glob}/**/*&quot;] if File.directory?(glob) and !File.symlink?(glob)
+      unless literal
+        new_value = case new_value
+          when Array        then &quot;%w(#{new_value.join(' ')})&quot;
+          when Hash, String then new_value.inspect
+          when Date         then new_value.strftime('%Y-%m-%d').inspect 
+          else              raise &quot;Cannot write value #{new_value.inspect} to gemspec file!&quot;
         end
       end
       
-      # update the spec file
-      spec = File.read(gemspec_file)
-      spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
-        assignment = $1
-        bunch = $2 ? list.grep(/^(test.*_test\.rb|spec.*_spec.rb)$/) : list
-        '%s%%w(%s)' % [assignment, bunch.join(' ')]
+      spec   = File.read(gemspec_file)
+      regexp = Regexp.new('^(\s+\w+\.' + Regexp.quote(attribute.to_s) + '\s*=\s*)[^\s].*$')
+      if spec.sub!(regexp) { $1 + new_value }
+        File.open(gemspec_file, 'w') { |f| f &lt;&lt; spec }      
+        modified_files &lt;&lt; gemspec_file
+            
+        # Reload the gemspec so the changes are incorporated
+        load_gemspec!
       end
-
-      File.open(gemspec_file, 'w') { |f| f &lt;&lt; spec }
-      reload_gemspec!
-    end
-    
-    def build_task
-      sh &quot;gem build #{gemspec_file}&quot;
-      Dir.mkdir('pkg') unless File.exist?('pkg')
-      sh &quot;mv #{name}-#{specification.version}.gem pkg/#{name}-#{specification.version}.gem&quot; 
-    end
-    
-    def install_task
-      raise &quot;#{name} .gem file not found&quot; unless File.exist?(&quot;pkg/#{name}-#{specification.version}.gem&quot;)
-      sh &quot;gem install pkg/#{name}-#{specification.version}.gem&quot;
     end
     
-    def uninstall_task
-      raise &quot;#{name} .gem file not found&quot; unless File.exist?(&quot;pkg/#{name}-#{specification.version}.gem&quot;)
-      sh &quot;gem uninstall #{name}&quot;
-    end    
-    
-    def create_version_tag!
-      # commit the gemspec file
-      git_commit_file(gemspec_file, &quot;Updated #{gemspec_file} for release of version #{@specification.version}&quot;) if git_modified?(gemspec_file)
-
-      # create tag and push changes
-      git_create_tag(&quot;#{@name}-#{@specification.version}&quot;, &quot;Tagged version #{@specification.version}&quot;)
-      git_push('origin', 'master', [:tags])     
+    # Updates the tasks file using the latest file found on Github
+    def update_tasks_task
+      require 'net/http'
+      
+      server = 'github.com'
+      path   = '/wvanbergen/github-gem/raw/master/tasks/github-gem.rake'
+      
+      Net::HTTP.start(server) do |http|
+        response = http.get(path)
+        open(__FILE__, &quot;w&quot;) { |file| file.write(response.body) }
+      end
+      puts &quot;Updated gem release tasks file with latest version.&quot;
     end
     
-    def release_task
-      puts
-      puts '------------------------------------------------------------'
-      puts &quot;Released #{@name} - version #{@specification.version}&quot;
-    end
   end
 end
-
-Rake::GithubGem.define_tasks!
\ No newline at end of file</diff>
      <filename>tasks/github-gem.rake</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>65c6f7de2aa58a64a15ad199fc42262f40837801</id>
    </parent>
  </parents>
  <author>
    <name>Willem van Bergen</name>
    <email>willem@vanbergen.org</email>
  </author>
  <url>http://github.com/wvanbergen/scoped_search/commit/81a0b4d64975653f2c5cf606e50348945bddb61d</url>
  <id>81a0b4d64975653f2c5cf606e50348945bddb61d</id>
  <committed-date>2009-09-10T00:51:42-07:00</committed-date>
  <authored-date>2009-09-10T00:51:42-07:00</authored-date>
  <message>Updated to latest gem release tasks</message>
  <tree>744e2d7ca61eea3b7e7bc4de4777e79bf99a7a1d</tree>
  <committer>
    <name>Willem van Bergen</name>
    <email>willem@vanbergen.org</email>
  </committer>
</commit>
