Class: Hoe

Inherits:
Object
  • Object
show all
Defined in:
lib/hoe-patched.rb

Overview

hoe - a tool to help rake

Hoe is a simple rake/rubygems helper for project Rakefiles. It generates all the usual tasks for projects including rdoc generation, testing, packaging, and deployment.

Using Hoe

Basics

Use this as a minimal starting point:

require 'hoe'

Hoe.new("project_name", '1.0.0') do |p|
  p.rubyforge_name = "rf_project"
  # add other details here
end

# add other tasks here

Tasks Provided:

announce

Create news email file and post to rubyforge.

audit

Run ZenTest against the package.

check_manifest

Verify the manifest.

clean

Clean up all the extras.

config_hoe

Create a fresh ~/.hoerc file.

debug_gem

Show information about the gem.

default

Run the default tasks.

deps:email

Print a contact list for gems dependent on this gem

deps:fetch

Fetch all the dependent gems of this gem into tarballs

deps:list

List all the dependent gems of this gem

docs

Build the docs HTML Files

email

Generate email announcement file.

gem

Build the gem file hoe-1.8.0.gem

generate_key

Generate a key for signing your gems.

install_gem

Install the package as a gem.

multi

Run the test suite using multiruby.

package

Build all the packages

post_blog

Post announcement to blog.

post_news

Post announcement to rubyforge.

publish_docs

Publish RDoc to RubyForge.

release

Package and upload the release to rubyforge.

ridocs

Generate ri locally for testing.

tasks

Generate a list of tasks for doco.

test

Run the test suite.

test_deps

Show which test files fail when run alone.

Extra Configuration Options:

Run config_hoe to generate a new ~/.hoerc file. The file is a YAML formatted config file with the following settings:

exclude

A regular expression of files to exclude from check_manifest.

publish_on_announce

Run publish_docs when you run release.

signing_key_file

Signs your gems with this private key.

signing_cert_file

Signs your gem with this certificate.

blogs

An array of hashes of blog settings.

Run config_hoe and see ~/.hoerc for examples.

Signing Gems:

Run the ‘generate_key’ task. This will:

  1. Configure your ~/.hoerc.

  2. Generate a signing key and certificate.

  3. Install the private key and public certificate files into ~/.gem.

  4. Upload the certificate to RubyForge.

Hoe will now generate signed gems when the package task is run. If you have multiple machines you build gems on, be sure to install your key and certificate on each machine.

Keep your private key secret! Keep your private key safe!

To make sure your gems are signed run:

rake package; tar tf pkg/yourproject-1.2.3.gem

If your gem is signed you will see:

data.tar.gz
data.tar.gz.sig
.gz
.gz.sig

Platform awareness

Hoe allows bundling of pre-compiled extensions in the package task.

To create a package for your current platform:

rake package INLINE=1

This will force Hoe analize your Inline already compiled extensions and include them in your gem.

If somehow you need to force a specific platform:

rake package INLINE=1 FORCE_PLATFORM=mswin32

This will set the Gem::Specification platform to the one indicated in FORCE_PLATFORM (instead of default Gem::Platform::CURRENT)

Constant Summary collapse

VERSION =
'1.8.0'
GEMURL =

for namespace :deps below

URI.parse 'http://gems.rubyforge.org'
PREFIX =

Used to specify a custom install location (for rake install).

ENV['PREFIX'] || ruby_prefix
RUBY_DEBUG =

Used to add extra flags to RUBY_FLAGS.

ENV['RUBY_DEBUG']
RUBY_FLAGS =

Used to specify flags to ruby [has smart default].

ENV['RUBY_FLAGS'] || default_ruby_flags
FILTER =

Used to add flags to test_unit (e.g., -n test_borked).

ENV['FILTER']
RUBYLIB =

:stopdoc:

if PREFIX == ruby_prefix then
  sitelibdir
else
  File.join(PREFIX, sitelibdir[ruby_prefix.size..-1])
end
DLEXT =
Config::CONFIG['DLEXT']
WINDOZE =
/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM
DIFF =
if WINDOZE
  'diff.exe'
else
  if system("gdiff", __FILE__, __FILE__)
    'gdiff' # solaris and kin suck
  else
    'diff'
  end
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, version) {|_self| ... } ⇒ Hoe

:nodoc:

Yields:

  • (_self)

Yield Parameters:

  • _self (Hoe)

    the object that the method was called on



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/hoe-patched.rb', line 353

def initialize(name, version) # :nodoc:
  self.name = name
  self.version = version

  # Defaults
  self.author = []
  self.clean_globs = %w(diff diff.txt email.txt ri deps .source_index
                        *.gem *~ **/*~ *.rbc **/*.rbc)
  self.description_sections = %w(description)
  self.blog_categories = [name]
  self.email = []
  self.extra_deps = []
  self.extra_dev_deps = []
  self.multiruby_skip = []
  self.need_tar = true
  self.need_zip = false
  self.rdoc_pattern = /^(lib|bin|ext)|txt|rdoc$/
  self.remote_rdoc_dir = name
  self.rsync_args = '-av --delete'
  self.rubyforge_name = name.downcase
  self.spec_extras = {}
  self.summary_sentences = 1
  self.test_globs = ['test/**/test_*.rb']
  self.testlib = 'test/unit'
  self.post_install_message = nil

  yield self if block_given?

  # Intuit values:

  readme = File.read(readme_name).split(/^(=+ .*)$/)[1..-1] rescue ''
  unless readme.empty? then
    sections = readme.map { |s|
      s =~ /^=/ ? s.strip.downcase.chomp(':').split.last : s.strip
    }
    sections = Hash[*sections]
    desc = sections.values_at(*description_sections).join("\n\n")
    summ = desc.split(/\.\s+/).first(summary_sentences).join(". ")

    self.description ||= desc
    self.summary ||= summ
    self.url ||= readme[1].gsub(/^\* /, '').split(/\n/).grep(/\S+/)
  else
    missing readme_name
  end

  self.changes ||= begin
                     h = File.read("History.txt")
                     h.split(/^(===.*)/)[1..2].join.strip
                   rescue
                     missing 'History.txt'
                     ''
                   end

  %w(email author).each do |field|
    value = self.send(field)
    if value.nil? or value.empty? then
      if Time.now < Time.local(2008, 4, 1) then
        warn "Hoe #{field} value not set - Fix by 2008-04-01!"
        self.send "#{field}=", "doofus"
      else
        abort "Hoe #{field} value not set. aborting"
      end
    end
  end

  hoe_deps = {
    'rake' => ">= #{RAKEVERSION}",
    'rubyforge' => ">= #{::RubyForge::VERSION}",
  }

  self.extra_deps     = normalize_deps extra_deps
  self.extra_dev_deps = normalize_deps extra_dev_deps

  if name == 'hoe' then
    hoe_deps.each do |pkg, vers|
      extra_deps << [pkg, vers]
    end
  else
    extra_dev_deps << ['hoe', ">= #{VERSION}"] unless hoe_deps.has_key? name
  end

  define_tasks
end

Instance Attribute Details

#authorObject

Recommended: The author(s) of the package. (can be array) Really. Set this or we’ll tease you.



189
190
191
# File 'lib/hoe-patched.rb', line 189

def author
  @author
end

#bin_filesObject

Populated automatically from the manifest. List of executables.



194
195
196
# File 'lib/hoe-patched.rb', line 194

def bin_files
  @bin_files
end

#blog_categoriesObject

Optional: An array of the project’s blog categories. Defaults to project name.



199
200
201
# File 'lib/hoe-patched.rb', line 199

def blog_categories
  @blog_categories
end

#changesObject

Optional: A description of the release’s latest changes. Auto-populates.



204
205
206
# File 'lib/hoe-patched.rb', line 204

def changes
  @changes
end

#clean_globsObject

Optional: An array of file patterns to delete on clean.



209
210
211
# File 'lib/hoe-patched.rb', line 209

def clean_globs
  @clean_globs
end

#descriptionObject

Optional: A description of the project. Auto-populates.



214
215
216
# File 'lib/hoe-patched.rb', line 214

def description
  @description
end

#description_sectionsObject

Optional: What sections from the readme to use for auto-description. Defaults to %w(description).



219
220
221
# File 'lib/hoe-patched.rb', line 219

def description_sections
  @description_sections
end

#emailObject

Recommended: The author’s email address(es). (can be array)



224
225
226
# File 'lib/hoe-patched.rb', line 224

def email
  @email
end

#extra_depsObject

Optional: An array of rubygem dependencies.



229
230
231
# File 'lib/hoe-patched.rb', line 229

def extra_deps
  @extra_deps
end

#extra_dev_depsObject

Optional: An array of rubygem developer dependencies.



234
235
236
# File 'lib/hoe-patched.rb', line 234

def extra_dev_deps
  @extra_dev_deps
end

#lib_filesObject

Populated automatically from the manifest. List of library files.



239
240
241
# File 'lib/hoe-patched.rb', line 239

def lib_files
  @lib_files
end

#multiruby_skipObject

Optional: Array of incompatible versions for multiruby filtering. Used as a regex.



244
245
246
# File 'lib/hoe-patched.rb', line 244

def multiruby_skip
  @multiruby_skip
end

#nameObject

MANDATORY: The name of the release.



249
250
251
# File 'lib/hoe-patched.rb', line 249

def name
  @name
end

#need_tarObject

Optional: Should package create a tarball? [default: true]



254
255
256
# File 'lib/hoe-patched.rb', line 254

def need_tar
  @need_tar
end

#need_zipObject

Optional: Should package create a zipfile? [default: false]



259
260
261
# File 'lib/hoe-patched.rb', line 259

def need_zip
  @need_zip
end

#post_install_messageObject

Optional: A post-install message to be displayed when gem is installed.



264
265
266
# File 'lib/hoe-patched.rb', line 264

def post_install_message
  @post_install_message
end

#rdoc_patternObject

Optional: A regexp to match documentation files against the manifest.



269
270
271
# File 'lib/hoe-patched.rb', line 269

def rdoc_pattern
  @rdoc_pattern
end

#remote_rdoc_dirObject

Optional: Name of RDoc destination directory on Rubyforge. [default: name]



274
275
276
# File 'lib/hoe-patched.rb', line 274

def remote_rdoc_dir
  @remote_rdoc_dir
end

#rsync_argsObject

Optional: Flags for RDoc rsync. [default: “-av –delete”]



279
280
281
# File 'lib/hoe-patched.rb', line 279

def rsync_args
  @rsync_args
end

#rubyforge_nameObject

Optional: The name of the rubyforge project. [default: name.downcase]



284
285
286
# File 'lib/hoe-patched.rb', line 284

def rubyforge_name
  @rubyforge_name
end

#specObject

The Gem::Specification.



289
290
291
# File 'lib/hoe-patched.rb', line 289

def spec
  @spec
end

#spec_extrasObject

Optional: A hash of extra values to set in the gemspec. Value may be a proc.



294
295
296
# File 'lib/hoe-patched.rb', line 294

def spec_extras
  @spec_extras
end

#summaryObject

Optional: A short summary of the project. Auto-populates.



299
300
301
# File 'lib/hoe-patched.rb', line 299

def summary
  @summary
end

#summary_sentencesObject

Optional: Number of sentences from description for summary. Defaults to 1.



304
305
306
# File 'lib/hoe-patched.rb', line 304

def summary_sentences
  @summary_sentences
end

#test_filesObject

Populated automatically from the manifest. List of tests.



309
310
311
# File 'lib/hoe-patched.rb', line 309

def test_files
  @test_files
end

#test_globsObject

Optional: An array of test file patterns [default: test/*/test_.rb]



314
315
316
# File 'lib/hoe-patched.rb', line 314

def test_globs
  @test_globs
end

#testlibObject

Optional: What test library to require [default: test/unit]



319
320
321
# File 'lib/hoe-patched.rb', line 319

def testlib
  @testlib
end

#urlObject

Optional: The url(s) of the project. (can be array). Auto-populates.



324
325
326
# File 'lib/hoe-patched.rb', line 324

def url
  @url
end

#versionObject

MANDATORY: The version. Don’t hardcode! use a constant in the project.



329
330
331
# File 'lib/hoe-patched.rb', line 329

def version
  @version
end

Class Method Details

.add_include_dirs(*dirs) ⇒ Object

Add extra dirs to both $: and RUBY_FLAGS (for test runs)



334
335
336
337
338
# File 'lib/hoe-patched.rb', line 334

def self.add_include_dirs(*dirs)
  dirs = dirs.flatten
  $:.unshift(*dirs)
  Hoe::RUBY_FLAGS.sub!(/-I/, "-I#{dirs.join(":")}:")
end

Instance Method Details

#announcementObject

end define



969
970
971
972
973
974
975
976
977
# File 'lib/hoe-patched.rb', line 969

def announcement # :nodoc:
  changes = self.changes.rdoc_to_markdown
  subject = "#{name} #{version} Released"
  title   = "#{name} version #{version} has been released!"
  body    = "#{description}\n\nChanges:\n\n#{changes}".rdoc_to_markdown
  urls    = Array(url).map { |s| "* <#{s.strip.rdoc_to_markdown}>" }.join("\n")

  return subject, title, body, urls
end

#define_tasksObject

:nodoc:



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
# File 'lib/hoe-patched.rb', line 450

def define_tasks # :nodoc:
  desc 'Run the default tasks.'
  task :default => :test

  desc 'Run the test suite. Use FILTER to add to the command line.'
  task :test do
    run_tests
  end

  desc 'Show which test files fail when run alone.'
  task :test_deps do
    tests = Dir["test/**/test_*.rb"]  +  Dir["test/**/*_test.rb"]

    tests.each do |test|
      if not system "ruby -Ibin:lib:test #{test} &> /dev/null" then
        puts "Dependency Issues: #{test}"
      end
    end
  end

  desc 'Run the test suite using multiruby.'
  task :multi do
    run_tests :multi
  end

  ############################################################
  # Packaging and Installing

  signing_key = nil
  cert_chain = []

  with_config do |config, path|
    break unless config['signing_key_file'] and config['signing_cert_file']
    key_file = File.expand_path config['signing_key_file'].to_s
    signing_key = key_file if File.exist? key_file

    cert_file = File.expand_path config['signing_cert_file'].to_s
    cert_chain << cert_file if File.exist? cert_file
  end

  self.spec = Gem::Specification.new do |s|
    s.name = name
    s.version = version
    s.summary = summary
    case author
    when Array
      s.authors = author
    else
      s.author = author
    end
    s.email = email
    s.homepage = Array(url).first
    s.rubyforge_project = rubyforge_name

    s.description = description

    extra_deps.each do |dep|
      s.add_dependency(*dep)
    end

    extra_dev_deps.each do |dep|
      s.add_development_dependency(*dep)
    end

    s.files = File.read("Manifest.txt").delete("\r").split(/\n/)
    s.executables = s.files.grep(/^bin/) { |f| File.basename(f) }

    s.bindir = "bin"
    dirs = Dir['{lib,ext}']
    s.require_paths = dirs unless dirs.empty?

    s.rdoc_options = ['--main', readme_name]
    s.extra_rdoc_files = s.files.grep(/(txt|rdoc)$/)
    s.has_rdoc = true

    s.post_install_message = post_install_message

    if test ?f, "test/test_all.rb" then
      s.test_file = "test/test_all.rb"
    else
      s.test_files = Dir[*test_globs]
    end

    if signing_key and cert_chain then
      s.signing_key = signing_key
      s.cert_chain = cert_chain
    end

    ############################################################
    # Allow automatic inclusion of compiled extensions
    if ENV['INLINE'] then
      s.platform = ENV['FORCE_PLATFORM'] || Gem::Platform::CURRENT
      # name of the extension is CamelCase
      alternate_name = if name =~ /[A-Z]/ then
                         name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')
                       elsif name =~ /_/ then
                         name.capitalize.gsub(/_([a-z])/) { $1.upcase }
                       end

      # Try collecting Inline extensions for +name+
      if defined?(Inline) then
        directory 'lib/inline'

        extensions = Dir.chdir(Inline::directory) {
          Dir["Inline_{#{name},#{alternate_name}}_*.#{DLEXT}"]
        }
        extensions.each do |ext|
          # add the inlined extension to the spec files
          s.files += ["lib/inline/#{ext}"]

          # include the file in the tasks
          file "lib/inline/#{ext}" => ["lib/inline"] do
            cp File.join(Inline::directory, ext), "lib/inline"
          end
        end
      end
    end

    # Do any extra stuff the user wants
    spec_extras.each do |msg, val|
      case val
      when Proc
        val.call(s.send(msg))
      else
        s.send "#{msg}=", val
      end
    end
  end

  desc 'Show information about the gem.'
  task :debug_gem do
    puts spec.to_ruby
  end

  self.lib_files = spec.files.grep(/^(lib|ext)/)
  self.bin_files = spec.files.grep(/^bin/)
  self.test_files = spec.files.grep(/^test/)

  Rake::GemPackageTask.new spec do |pkg|
    pkg.need_tar = @need_tar
    pkg.need_zip = @need_zip
  end

  desc 'Install the package as a gem.'
  task :install_gem => [:clean, :package] do
    gem = Dir['pkg/*.gem'].first
    sh "#{'sudo ' unless WINDOZE}gem install --local #{gem}"
  end

  desc 'Package and upload the release to rubyforge.'
  task :release => [:clean, :package] do |t|
    v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
    abort "Versions don't match #{v} vs #{version}" if v != version
    pkg = "pkg/#{name}-#{version}"

    if $DEBUG then
      puts "release_id = rf.add_release #{rubyforge_name.inspect}, #{name.inspect}, #{version.inspect}, \"#{pkg}.tgz\""
      puts "rf.add_file #{rubyforge_name.inspect}, #{name.inspect}, release_id, \"#{pkg}.gem\""
    end

    rf = RubyForge.new.configure
    puts "Logging in"
    rf.

    c = rf.userconfig
    c["release_notes"] = description if description
    c["release_changes"] = changes if changes
    c["preformatted"] = true

    files = [(@need_tar ? "#{pkg}.tgz" : nil),
             (@need_zip ? "#{pkg}.zip" : nil),
             "#{pkg}.gem"].compact

    puts "Releasing #{name} v. #{version}"
    rf.add_release rubyforge_name, name, version, *files
  end

  ############################################################
  # Doco

  Rake::RDocTask.new(:docs) do |rd|
    rd.main = readme_name
    rd.options << '-d' if RUBY_PLATFORM !~ /win32/ and `which dot` =~ /\/dot/ and not ENV['NODOT']
    rd.rdoc_dir = 'doc'
    files = spec.files.grep(rdoc_pattern)
    files -= ['Manifest.txt']
    rd.rdoc_files.push(*files)

    title = "#{name}-#{version} Documentation"
    title = "#{rubyforge_name}'s " + title if rubyforge_name != name

    rd.options << "-t #{title}"
  end

  desc 'Generate ri locally for testing.'
  task :ridocs => :clean do
    sh %q{ rdoc --ri -o ri . }
  end

  desc 'Publish RDoc to RubyForge.'
  task :publish_docs => [:clean, :docs] do
    config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
    host = "#{config["username"]}@rubyforge.org"

    remote_dir = "/var/www/gforge-projects/#{rubyforge_name}/#{remote_rdoc_dir}"
    local_dir = 'doc'

    sh %{rsync #{rsync_args} #{local_dir}/ #{host}:#{remote_dir}}
  end

  # no doco for this one
  task :publish_on_announce do
    with_config do |config, _|
      Rake::Task['publish_docs'].invoke if config["publish_on_announce"]
    end
  end

  ############################################################
  # Dependencies:

  namespace :deps do
    require 'zlib' # HACK for rubygems 1.3.0
    require 'rubygems/remote_fetcher'

    @@index = nil

    def self.get_source_index
      return @@index if @@index

      dump = unless File.exist? '.source_index' then
               url = GEMURL + "Marshal.#{Gem.marshal_version}.Z"
               dump = Gem::RemoteFetcher.fetcher.fetch_path url
               dump = Gem.inflate dump
               open '.source_index', 'wb' do |io| io.write dump end
               dump
             else
               open '.source_index', 'rb' do |io| io.read end
             end

      @@index = Marshal.load dump
    end

    def self.get_latest_gems
      @@cache ||= get_source_index.latest_specs
    end

    def self.get_gems_by_name
      @@by_name ||= Hash[*get_latest_gems.map { |gem|
                           [gem.name, gem, gem.full_name, gem]
                         }.flatten]
    end

    def self.dependent_upon name
      get_latest_gems.find_all { |gem|
        gem.dependencies.any? { |dep| dep.name == name }
      }
    end


    desc "List all the dependent gems of this gem"
    task :list do
      gems = self.get_gems_by_name
      gem  = gems[self.name]

      abort "Couldn't find gem: #{self.name}" unless gem

      deps = self.dependent_upon self.name
      max  = deps.map { |s| s.full_name.size }.max

      puts "  dependents:"
      unless deps.empty? then
        deps.sort_by { |spec| spec.full_name }.each do |spec|
          vers = spec.dependencies.find {|s| s.name == name }.requirement_list
          puts "    %-*s - %s" % [max, spec.full_name, vers.join(", ")]
        end
      else
        puts "    none"
      end
    end

    desc "Print a contact list for gems dependent on this gem"
    task :email do
      gems = self.get_gems_by_name
      gem  = gems[self.name]

      abort "Couldn't find gem: #{self.name}" unless gem

      deps = self.dependent_upon self.name

      email = deps.map { |s| s.email }.flatten.sort.uniq
      email = email.map { |s| s.split(/,\s*/) }.flatten.sort.uniq

      email.map! { |s| # don't you people realize how easy this is?
        s.gsub(/ at | _at_ |\s*(atmark|@nospam@|-at?-|@at?@|<at?>|\[at?\]|\(at?\))\s*/i, '@').gsub(/\s*(dot|\[d(ot)?\]|\.dot\.)\s*/i, '.').gsub(/\s+com$/, '.com')
      }

      bad, good = email.partition { |e| e !~ /^[\w.+-]+\@[\w.+-]+$/ }

      warn "Rejecting #{bad.size} email. I couldn't unmunge them." unless
        bad.empty?

      puts good.join(", ")
    end

    desc "Fetch all the dependent gems of this gem into tarballs"
    task :fetch do
      gems = self.get_gems_by_name
      gem  = gems[self.name]
      deps = self.dependent_upon self.name

      mkdir "deps" unless File.directory? "deps"
      Dir.chdir "deps" do
        begin
          deps.sort_by { |spec| spec.full_name }.each do |spec|
            full_name = spec.full_name
            tgz_name  = "#{full_name}.tgz"
            gem_name  = "#{full_name}.gem"

            next if File.exist? tgz_name
            FileUtils.rm_rf [full_name, gem_name]

            begin
              warn "downloading #{full_name}"
              Gem::RemoteFetcher.fetcher.download(spec, GEMURL, Dir.pwd)
              FileUtils.mv "cache/#{gem_name}", '.'
            rescue Gem::RemoteFetcher::FetchError
              warn "  failed"
              next
            end

            warn "converting #{gem_name} to tarball"

            system "gem unpack #{gem_name} 2> /dev/null"
            system "gem spec -l #{gem_name} > #{full_name}/gemspec.rb"
            system "tar zmcf #{tgz_name} #{full_name}"
            FileUtils.rm_rf [full_name, gem_name, "cache"]
          end
        ensure
          FileUtils.rm_rf "cache"
        end
      end
    end
  end

  ############################################################
  # Misc/Maintenance:

  desc 'Run ZenTest against the package.'
  task :audit do
    libs = %w(lib test ext).join(File::PATH_SEPARATOR)
    sh "zentest -I=#{libs} #{spec.files.grep(/^(lib|test)/).join(' ')}"
  end

  desc 'Clean up all the extras.'
  task :clean => [ :clobber_docs, :clobber_package ] do
    clean_globs.each do |pattern|
      files = Dir[pattern]
      rm_rf files, :verbose => true unless files.empty?
    end
  end

  desc 'Create a fresh ~/.hoerc file.'
  task :config_hoe do
    with_config do |config, path|
      default_config = {
        "exclude" => /tmp$|CVS|\.svn/,
        "publish_on_announce" => false,
        "signing_key_file" => "~/.gem/gem-private_key.pem",
        "signing_cert_file" => "~/.gem/gem-public_cert.pem",
        "blogs" => [ {
                       "user" => "user",
                       "url" => "url",
                       "extra_headers" => {
                         "mt_convert_breaks" => "markdown"
                       },
                       "blog_id" => "blog_id",
                       "password"=>"password",
                     } ],
      }
      File.open(path, "w") do |f|
        YAML.dump(default_config.merge(config), f)
      end

      editor = ENV['EDITOR'] || 'vi'
      system "#{editor} #{path}" if ENV['SHOW_EDITOR'] != 'no'
    end
  end

  desc 'Generate email announcement file.'
  task :email do
    require 'rubyforge'
    subject, title, body, urls = announcement

    File.open("email.txt", "w") do |mail|
      mail.puts "Subject: [ANN] #{subject}"
      mail.puts
      mail.puts title
      mail.puts
      mail.puts urls
      mail.puts
      mail.puts body
      mail.puts
      mail.puts urls
    end
    puts "Created email.txt"
  end

  desc 'Post announcement to blog.'
  task :post_blog do
    require 'xmlrpc/client'

    with_config do |config, path|
      break unless config['blogs']

      subject, title, body, urls = announcement
      body += "\n\n#{urls}"

      config['blogs'].each do |site|
        server = XMLRPC::Client.new2(site['url'])
        content = site['extra_headers'].merge(:title => title,
                                              :description => body,
                                              :categories => blog_categories)

        result = server.call('metaWeblog.newPost',
                             site['blog_id'],
                             site['user'],
                             site['password'],
                             content,
                             true)
      end
    end
  end

  desc 'Post announcement to rubyforge.'
  task :post_news do
    require 'rubyforge'
    subject, title, body, urls = announcement

    rf = RubyForge.new.configure
    rf.
    rf.post_news(rubyforge_name, subject, "#{title}\n\n#{body}")
    puts "Posted to rubyforge"
  end

  desc 'Create news email file and post to rubyforge.'
  task :announce => [:email, :post_news, :post_blog, :publish_on_announce ]

  desc 'Verify the manifest.'
  task :check_manifest => :clean do
    f = "Manifest.tmp"
    require 'find'
    files = []
    with_config do |config, _|
      exclusions = config["exclude"]
      abort "exclude entry missing from .hoerc. Aborting." if exclusions.nil?
      Find.find '.' do |path|
        next unless File.file? path
        next if path =~ exclusions
        files << path[2..-1]
      end
      files = files.sort.join "\n"
      File.open f, 'w' do |fp| fp.puts files end
      system "#{DIFF} -du Manifest.txt #{f}"
      rm f
    end
  end

  desc 'Generate a key for signing your gems.'
  task :generate_key do
    email = spec.email
    abort "No email in your gemspec" if email.nil? or email.empty?

    key_file = with_config { |config, _| config['signing_key_file'] }
    cert_file = with_config { |config, _| config['signing_cert_file'] }

    if key_file.nil? or cert_file.nil? then
      ENV['SHOW_EDITOR'] ||= 'no'
      Rake::Task['config_hoe'].invoke

      key_file = with_config { |config, _| config['signing_key_file'] }
      cert_file = with_config { |config, _| config['signing_cert_file'] }
    end

    key_file = File.expand_path key_file
    cert_file = File.expand_path cert_file

    unless File.exist? key_file or File.exist? cert_file then
      sh "gem cert --build #{email}"
      mv "gem-private_key.pem", key_file, :verbose => true
      mv "gem-public_cert.pem", cert_file, :verbose => true

      puts "Installed key and certificate."

      rf = RubyForge.new.configure
      rf.

      cert_package = "#{rubyforge_name}-certificates"

      begin
        rf.lookup 'package', cert_package
      rescue
        rf.create_package rubyforge_name, cert_package
      end

      begin
        rf.lookup('release', cert_package)['certificates']
        rf.add_file rubyforge_name, cert_package, 'certificates', cert_file
      rescue
        rf.add_release rubyforge_name, cert_package, 'certificates', cert_file
      end

      puts "Uploaded certificate to release \"certificates\" in package #{cert_package}"
    else
      puts "Keys already exist."
    end
  end

end

#developer(name, email) ⇒ Object



438
439
440
441
# File 'lib/hoe-patched.rb', line 438

def developer name, email
  self.author << name
  self.email << email
end

#missing(name) ⇒ Object



344
345
346
347
# File 'lib/hoe-patched.rb', line 344

def missing name
  warn "** #{name} is missing or in the wrong format for auto-intuiting."
  warn "   run `sow blah` and look at its text files"
end

#normalize_deps(deps) ⇒ Object



340
341
342
# File 'lib/hoe-patched.rb', line 340

def normalize_deps deps
  Array(deps).map { |o| String === o ? [o] : o }
end

#paragraphs_of(path, *paragraphs) ⇒ Object

Reads a file at path and spits out an array of the paragraphs specified.

changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
summary, *description = p.paragraphs_of('README.txt', 3, 3..8)


1003
1004
1005
# File 'lib/hoe-patched.rb', line 1003

def paragraphs_of(path, *paragraphs)
  File.read(path).delete("\r").split(/\n\n+/).values_at(*paragraphs)
end

#readme_nameObject



349
350
351
# File 'lib/hoe-patched.rb', line 349

def readme_name
  Dir['README*'].first || 'README.txt'
end

#run_tests(multi = false) ⇒ Object

:nodoc:



979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
# File 'lib/hoe-patched.rb', line 979

def run_tests(multi=false) # :nodoc:
  msg = multi ? :sh : :ruby
  cmd = if test ?f, 'test/test_all.rb' then
          "#{RUBY_FLAGS} test/test_all.rb #{FILTER}"
        else
          tests = ["rubygems", self.testlib] +
            test_globs.map { |g| Dir.glob(g) }.flatten
          tests.map! {|f| %Q(require "#{f}")}
          "#{RUBY_FLAGS} -e '#{tests.join("; ")}' #{FILTER}"
        end

  excludes = multiruby_skip.join(":")
  ENV['EXCLUDED_VERSIONS'] = excludes
  cmd = "multiruby #{cmd}" if multi

  send msg, cmd
end

#with_config {|config, rc| ... } ⇒ Object

:nodoc:

Yields:

  • (config, rc)


443
444
445
446
447
448
# File 'lib/hoe-patched.rb', line 443

def with_config # :nodoc:
  rc = File.expand_path("~/.hoerc")
  exists = File.exist? rc
  config = exists ? YAML.load_file(rc) : {}
  yield(config, rc)
end