Class: FixedHoe

Inherits:
Object
  • Object
show all
Defined in:
lib/util/fixed_hoe.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.

M@: THIS IS A Fixed HOE! It doesn’t cater to Windoze by adding ‘.txt’ extensions to all the files. Nor does it add itself as a dependency to the generated gem.

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 - Generate email announcement 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

  • api_docs - Build the docs HTML Files

  • email - Generate email announcement file.

  • install - Install the package. Uses PREFIX and RUBYLIB

  • 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

  • test - Run the test suite. Use FILTER to add to the command line.

  • test_deps - Show which test files fail when run alone.

  • uninstall - Uninstall the package.

Attributes

The attributes that you can provide inside the new block above are:

Mandatory

  • name - The name of the release.

  • version - The version. Don’t hardcode! use a constant in the project.

Damn Good to Set

  • author - The author of the package. (can be array of authors)

  • changes - A description of the release’s latest changes.

  • description - A description of the project.

  • email - The author’s email address. (can be array of urls)

  • summary - A short summary of the project.

  • url - The url of the project.

Optional

  • clean_globs - An array of file patterns to delete on clean.

  • extra_deps - An array of rubygem dependencies.

  • need_tar - Should package create a tarball? [default: true]

  • need_zip - Should package create a zipfile? [default: false]

  • rdoc_pattern - A regexp to match documentation files against the manifest.

  • rubyforge_name - The name of the rubyforge project. [default: name.downcase]

  • spec_extras - A hash of extra values to set in the gemspec.

  • test_globs - An array of test file patterns [default: test/*/test_.rb]

Environment Variables

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

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

  • RUBY_DEBUG - Used to add extra flags to RUBY_FLAGS.

  • RUBY_FLAGS - Used to specify flags to ruby [has smart default].

Constant Summary collapse

VERSION =

:nodoc:

'1.2.0'
PREFIX =
ENV['PREFIX'] || rubyprefix
RUBYLIB =
if PREFIX == rubyprefix then
  sitelibdir
else
  File.join(PREFIX, sitelibdir[rubyprefix.size..-1])
end
RUBY_DEBUG =
ENV['RUBY_DEBUG']
RUBY_FLAGS =
ENV['RUBY_FLAGS'] ||
"-w -I#{%w(lib ext bin test).join(File::PATH_SEPARATOR)}" +
(RUBY_DEBUG ? " #{RUBY_DEBUG}" : '')
FILTER =

for tests (eg FILTER=“-n test_blah”)

ENV['FILTER']

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of FixedHoe.

Yields:

  • (_self)

Yield Parameters:

  • _self (FixedHoe)

    the object that the method was called on



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/util/fixed_hoe.rb', line 144

def initialize(name, version)
  self.name = name
  self.version = version

  # Defaults
  self.rubyforge_name = name.downcase
  self.url = "http://www.zenspider.com/ZSS/Products/#{name}/"
  self.author = "Ryan Davis"
  self.email = "[email protected]"
  self.clean_globs = %w(diff diff email ri *.gem **/*~)
  self.test_globs = ['test/**/test_*.rb']
  self.changes = "The author was too lazy to write a changeset"
  self.description = "The author was too lazy to write a description"
  self.summary = "The author was too lazy to write a summary"
  self.rdoc_pattern = /^(lib|bin)|txt$/
  self.extra_deps = []
  self.spec_extras = {}
  self.need_tar = true
  self.need_zip = false

  yield self if block_given?

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

  self.extra_deps = Array(extra_deps) # just in case user used = instead of <<
  self.extra_deps = [extra_deps] unless
    extra_deps.empty? or Array === extra_deps.first
  if name == 'hoe' then
    hoe_deps.each do |pkg, version|
      extra_deps << [pkg, version]
    end
  #else
  #  extra_deps << ['hoe', ">= #{VERSION}"] unless hoe_deps.has_key? name
  end

  define_tasks
end

Instance Attribute Details

#authorObject

Returns the value of attribute author.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def author
  @author
end

#bin_filesObject

Returns the value of attribute bin_files.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def bin_files
  @bin_files
end

#changesObject

Returns the value of attribute changes.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def changes
  @changes
end

#clean_globsObject

Returns the value of attribute clean_globs.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def clean_globs
  @clean_globs
end

#descriptionObject

Returns the value of attribute description.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def description
  @description
end

#emailObject

Returns the value of attribute email.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def email
  @email
end

#extra_depsObject

Returns the value of attribute extra_deps.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def extra_deps
  @extra_deps
end

#lib_filesObject

Returns the value of attribute lib_files.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def lib_files
  @lib_files
end

#nameObject

Returns the value of attribute name.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def name
  @name
end

#need_tarObject

Returns the value of attribute need_tar.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def need_tar
  @need_tar
end

#need_zipObject

Returns the value of attribute need_zip.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def need_zip
  @need_zip
end

#rdoc_patternObject

Returns the value of attribute rdoc_pattern.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def rdoc_pattern
  @rdoc_pattern
end

#rubyforge_nameObject

Returns the value of attribute rubyforge_name.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def rubyforge_name
  @rubyforge_name
end

#specObject

Returns the value of attribute spec.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def spec
  @spec
end

#spec_extrasObject

Returns the value of attribute spec_extras.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def spec_extras
  @spec_extras
end

#summaryObject

Returns the value of attribute summary.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def summary
  @summary
end

#test_filesObject

Returns the value of attribute test_files.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def test_files
  @test_files
end

#test_globsObject

Returns the value of attribute test_globs.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def test_globs
  @test_globs
end

#urlObject

Returns the value of attribute url.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def url
  @url
end

#versionObject

Returns the value of attribute version.



142
143
144
# File 'lib/util/fixed_hoe.rb', line 142

def version
  @version
end

Instance Method Details

#announcementObject

end define



511
512
513
514
515
516
517
518
519
# File 'lib/util/fixed_hoe.rb', line 511

def announcement
  urls = "  " + Array(url).map {|s| s.strip}.join("\n  ")

  subject = "#{name} #{version} Released"
  title = "#{name} version #{version} has been released!"
  body = "#{description}\n\nChanges:\n\n#{changes}"

  return subject, title, body, urls
end

#define_tasksObject



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
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
437
438
439
440
441
442
443
444
445
446
447
448
449
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
# File 'lib/util/fixed_hoe.rb', line 185

def define_tasks
  desc 'Run the test suite. Use FILTER to add to the command line.'
  task :all_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

  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

    s.files = File.read("Manifest").split
    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.has_rdoc = true

    if test ?f, "test/test_all.rb" then
      s.test_file = "test/test_all.rb"
    else
      s.test_files = Dir[*test_globs]
    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. Uses PREFIX and RUBYLIB'
  task :install do
    [
     [lib_files + test_files, RUBYLIB, 0444],
     [bin_files, File.join(PREFIX, 'bin'), 0555]
    ].each do |files, dest, mode|
      FileUtils.mkdir_p dest unless test ?d, dest
      files.each do |file|
        install file, dest, :mode => mode
      end
    end
  end

  desc 'Install the package as a gem'
  task :install_gem => [:clean, :package] do
    sh "sudo gem install pkg/*.gem"
  end

  desc 'Uninstall the package.'
  task :uninstall do
    Dir.chdir RUBYLIB do
      rm_f((lib_files + test_files).map { |f| File.basename f })
    end
    Dir.chdir File.join(PREFIX, 'bin') do
      rm_f bin_files.map { |f| File.basename f }
    end
  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
    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(:api_docs) do |rd|
    rd.main = "ReadMe"
    rd.options << '-d' if RUBY_PLATFORM !~ /win32/ and `which dot` =~ /\/dot/
    rd.rdoc_dir = 'docs/api_docs'
    files = spec.files.grep(rdoc_pattern)
    files << 'ReadMe'
    files << 'History'
    files -= ['Manifest']
    rd.rdoc_files.push(*files)

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

    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, :api_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}/#{name}"
    local_dir = 'docs/api_docs'
    sh %{rsync -av --delete #{local_dir}/ #{host}:#{remote_dir}}
  end

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

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

  def with_config(create=false)
    require 'yaml'
    rc = File.expand_path("~/.hoerc")

    unless create then
      if test ?f, rc then
        config = YAML.load_file(rc)
        yield(config, rc)
      end
    else
      unless test ?f, rc then
        yield(rc)
      end
    end
  end

  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_api_docs, :clobber_package ] do
    clean_globs.each do |pattern|
      files = Dir[pattern]
      rm_rf files unless files.empty?
    end
  end

  desc 'Create a fresh ~/.hoerc file'
  task :config_hoe do
    with_config(:create) do |rc, path|
      blog = {
        "publish_on_announce" => false,
        "blogs" => [ {
                       "user" => "user",
                       "url" => "url",
                       "extra_headers" => {
                         "mt_convert_breaks" => "markdown"
                       },
                       "blog_id" => "blog_id",
                       "password"=>"password",
                     } ],
      }
      File.open(rc, "w") do |f|
        YAML.dump(blog, f)
      end
    end

    with_config do |rc, path|
      editor = ENV['EDITOR'] || 'vi'
      system "#{editor} #{path}"
    end
  end

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

    File.open("email", "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"
  end

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

    with_config do |config, path|
      subject, title, body, urls = announcement
      config['blogs'].each do |site|
        server = XMLRPC::Client.new2(site['url'])
        content = site['extra_headers'].merge(:title => title,
                                              :description => body)
        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
    rf.
    rf.post_news(rubyforge_name, subject, "#{title}\n\n#{body}")
    puts "Posted to rubyforge"
  end

  desc 'Generate email announcement 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 = []
    Find.find '.' do |path|
      next unless File.file? path
      next if path =~ /\.svn|tmp$|CVS/
      files << path[2..-1]
    end
    files = files.sort.join "\n"
    File.open f, 'w' do |fp| fp.puts files end
    system "diff -du Manifest #{f}"
    rm f
  end
  
  desc "Create the manifest"
  task :create_manifest => :clean do
    f = "Manifest"
    require 'find'
    files = []
    Find.find '.' do |path|
      next unless File.file? path
      next if path =~ /\.svn|tmp$|CVS|\.git/
      files << path[2..-1]
    end
    files = files.sort.join "\n"
    File.open f, 'w' do |fp| fp.puts files end
  end

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', 0..1).join("\n\n")
summary, *description = p.paragraphs_of('Readme', 3, 3..8)


540
541
542
543
# File 'lib/util/fixed_hoe.rb', line 540

def paragraphs_of(path, *paragraphs)
  file = File.read(path)
  file.split(/\n\n+/).values_at(*paragraphs)
end

#run_tests(multi = false) ⇒ Object

:nodoc:



521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/util/fixed_hoe.rb', line 521

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 = test_globs.map { |g| Dir.glob(g) }.flatten << 'test/unit'
          tests.map! {|f| %Q(require "#{f}")}
          "#{RUBY_FLAGS} -e '#{tests.join("; ")}' #{FILTER}"
        end
  cmd = "multiruby #{cmd}" if multi
  send msg, cmd
end

#with_config(create = false) ⇒ Object

Misc/Maintenance:



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/util/fixed_hoe.rb', line 370

def with_config(create=false)
  require 'yaml'
  rc = File.expand_path("~/.hoerc")

  unless create then
    if test ?f, rc then
      config = YAML.load_file(rc)
      yield(config, rc)
    end
  else
    unless test ?f, rc then
      yield(rc)
    end
  end
end