In Files

Parent

Namespace

Hoe

Hoe is a simple rake/rubygems helper for project Rakefiles. It helps generate rubygems and includes a dynamic plug-in system allowing for easy extensibility. Hoe ships with plug-ins for all your usual project tasks including rdoc generation, testing, packaging, and deployment.

Using Hoe

Basics

Sow generates a new project from scratch. Sow uses a simple ERB templating system allowing you to capture patterns common to your projects. Run `sow` and then see ~/.hoe_template for more info:

  % sow project_name
  ...
  % cd project_name

and have at it.

Extra Configuration Options:

Hoe maintains a config file for cross-project values. The file is located at ~/.hoerc. The file is a YAML formatted config file with the following settings (extended by plugins):

exclude

A regular expression of files to exclude from check_manifest.

Run `rake config_hoe` and see ~/.hoerc for examples.

Extending Hoe

Hoe can be extended via its plugin system. Hoe searches out all installed files matching 'hoe/*.rb' and loads them. Those files are expected to define a module matching the file name. The module must define a define task method and can optionally define an initialize method. Both methods must be named to match the file. eg

  module Hoe::Blah
    def initialize_blah # optional
      # ...
    end

    def define_blah_tasks
      # ...
    end
  end

Constants

VERSION

duh

RUBY_DEBUG

Used to add extra flags to RUBY_FLAGS.

RUBY_FLAGS

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

DEFAULT_CONFIG

Default configuration values for .hoerc. Plugins should populate this on load.

WINDOZE

True if you’re a masochistic developer. Used for building commands.

Attributes

author[RW]

MANDATORY: The author(s) of the package. (can be array)

Use the # method to fill in both author and email cleanly.

changes[RW]

Optional: A description of the release’s latest changes. Auto-populates to the top entry of History.txt.

description[RW]

Optional: A description of the project. Auto-populates from the first paragraph of the DESCRIPTION section of README.txt.

See also: Hoe#summary and Hoe.paragraphs_of.

description_sections[RW]

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

email[RW]

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

Use the # method to fill in both author and email cleanly.

extra_deps[RW]

Optional: An array of rubygem dependencies.

  extra_deps << ['blah', '~> 1.0']
extra_dev_deps[RW]

Optional: An array of rubygem developer dependencies.

extra_rdoc_files[RW]

Optional: Extra files you want to add to RDoc.

.txt files are automatically included (excluding the obvious).

history_file[RW]

Optional: The filename for the project history. [default: History.txt]

name[RW]

MANDATORY: The name of the release.

Set via Hoe.spec.

post_install_message[RW]

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

readme_file[RW]

Optional: The filename for the project readme. [default: README.txt]

rubyforge_name[RW]

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

spec_extras[RW]

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

  spec_extras[:required_rubygems_version] = '>= 1.3.2'

(tho, see # if that’s all you want to do)

summary[RW]

Optional: A short summary of the project. Auto-populates from the first sentence of the description.

See also: Hoe#description and Hoe.paragraphs_of.

summary_sentences[RW]

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

test_globs[RW]

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

url[RW]

Optional: The url(s) of the project. (can be array). Auto-populates to a list of urls read from the beginning of README.txt.

version[RW]

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

Public Class Methods

add_include_dirs(*dirs) click to toggle source

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

     # File lib/hoe.rb, line 219
219:   def self.add_include_dirs(*dirs)
220:     dirs = dirs.flatten
221:     $:.unshift(*dirs)
222:     s = File::PATH_SEPARATOR
223:     RUBY_FLAGS.sub!(/-I/, "-I#{dirs.join(s)}#{s}")
224:   end
load_plugins() click to toggle source

Find and load all plugin files.

It is called at the end of hoe.rb

     # File lib/hoe.rb, line 231
231:   def self.load_plugins
232:     loaded, found = {}, {}
233: 
234:     Gem.find_files("hoe/*.rb").reverse.each do |path|
235:       found[File.basename(path, ".rb").intern] = path
236:     end
237: 
238:     :keep_doing_this while found.map { |name, plugin|
239:       next unless Hoe.plugins.include? name
240:       next if loaded[name]
241:       begin
242:         warn "loading #{plugin}" if $DEBUG
243:         loaded[name] = load plugin
244:       rescue LoadError => e
245:         warn "error loading #{plugin.inspect}: #{e.message}. skipping..."
246:       end
247:     }.any?
248:   end
plugin(*names) click to toggle source

Activate plugins.

     # File lib/hoe.rb, line 266
266:   def self.plugin *names
267:     self.plugins.concat names
268:     self.plugins.uniq!
269:   end
plugins() click to toggle source

Return the list of activated plugins.

     # File lib/hoe.rb, line 274
274:   def self.plugins
275:     @@plugins
276:   end
spec(name, &block) click to toggle source

Execute the Hoe DSL to define your project’s Hoe specification (which interally creates a gem specification). All hoe attributes and methods are available within block. Eg:

  Hoe.spec name do
    # ... project specific data ...
  end
     # File lib/hoe.rb, line 287
287:   def self.spec name, &block
288:     Hoe.load_plugins
289: 
290:     spec = self.new name
291:     spec.activate_plugins
292:     spec.instance_eval(&block)
293:     spec.post_initialize
294:     spec # TODO: remove?
295:   end

Public Instance Methods

activate_plugins() click to toggle source

Activate plugin modules and add them to the current instance.

     # File lib/hoe.rb, line 300
300:   def activate_plugins
301:     names = self.class.constants.map { |s| s.to_s }
302:     names.reject! { |n| n =~ /^[A-Z_]+$/ }
303: 
304:     names.each do |name|
305:       next unless Hoe.plugins.include? name.downcase.intern
306:       warn "extend #{name}" if $DEBUG
307:       self.extend Hoe.const_get(name)
308:     end
309: 
310:     Hoe.plugins.each do |plugin|
311:       msg = "initialize_#{plugin}"
312:       warn msg if $DEBUG
313:       send msg if self.respond_to? msg
314:     end
315:   end
add_dependencies() click to toggle source

Add standard and user defined dependencies to the spec.

     # File lib/hoe.rb, line 320
320:   def add_dependencies
321:     self.extra_deps     = normalize_deps extra_deps
322:     self.extra_dev_deps = normalize_deps extra_dev_deps
323: 
324:     case name
325:     when 'hoe' then
326:       extra_deps << ['rake', ">= #{RAKEVERSION}"]
327:     when 'rubyforge', 'rake', 'gemcutter' then
328:       # avoid circular dependencies for hoe's (potentially) hoe'd dependencies
329:     else
330:       extra_dev_deps << ['hoe', ">= #{VERSION}"]
331:     end
332:   end
define_spec() click to toggle source

Define the Gem::Specification.

     # File lib/hoe.rb, line 344
344:   def define_spec
345:     self.spec = Gem::Specification.new do |s|
346:       dirs = Dir['lib']
347: 
348:       s.name                 = name
349:       s.version              = version if version
350:       s.summary              = summary
351:       s.email                = email
352:       s.homepage             = Array(url).first
353:       s.rubyforge_project    = rubyforge_name
354:       s.description          = description
355:       s.files = files        = File.read_utf("Manifest.txt").split(/\r?\n\r?/)
356:       s.executables          = s.files.grep(/^bin/) { |f| File.basename(f) }
357:       s.bindir               = "bin"
358:       s.require_paths        = dirs unless dirs.empty?
359:       s.rdoc_options         = ['--main', readme_file]
360:       s.has_rdoc             = true
361:       s.post_install_message = post_install_message
362:       s.test_files           = Dir[*self.test_globs]
363: 
364:       missing "Manifest.txt" if files.empty?
365: 
366:       case author
367:       when Array
368:         s.authors = author
369:       else
370:         s.author  = author
371:       end
372: 
373:       extra_deps.each do |dep|
374:         s.add_dependency(*dep)
375:       end
376: 
377:       extra_dev_deps.each do |dep|
378:         s.add_development_dependency(*dep)
379:       end
380: 
381:       s.extra_rdoc_files += s.files.grep(/txt$/)
382:       s.extra_rdoc_files.reject! { |f| f =~ %^(test|spec|vendor|template|data|tmp)/% }
383:       s.extra_rdoc_files += @extra_rdoc_files
384: 
385:     end
386: 
387:     unless self.version then
388:       version    = nil
389:       version_re = /VERSION += +([\"\'])([\d][\w\.]+)\11//
390: 
391:       spec.files.each do |file|
392:         next unless File.exist? file
393:         version = File.read_utf(file)[version_re, 2]
394:         break if version
395:       end
396: 
397:       spec.version = self.version = version if version
398: 
399:       unless self.version then
400:         spec.version = self.version = "0.borked"
401:         warn "** Add 'VERSION = \"x.y.z\"' to your code,"
402:         warn "   add a version to your hoe spec,"
403:         warn "   or fix your Manifest.txt"
404:       end
405:     end
406: 
407:     # Do any extra stuff the user wants
408:     spec_extras.each do |msg, val|
409:       case val
410:       when Proc
411:         val.call spec.send(msg)
412:       else
413:         spec.send "#{msg}=", val
414:       end
415:     end
416:   end
dependency_target() click to toggle source

Returns the proper dependency list for the thingy.

     # File lib/hoe.rb, line 337
337:   def dependency_target
338:     self.name == 'hoe' ? extra_deps : extra_dev_deps
339:   end
developer(name, email) click to toggle source

Convenience method to set add to both the author and email fields.

     # File lib/hoe.rb, line 421
421:   def developer name, email
422:     self.author << name
423:     self.email  << email
424:   end
intuit_values() click to toggle source

Intuit values from the readme and history files.

     # File lib/hoe.rb, line 466
466:   def intuit_values
467:     header_re = /^((?:=+|#+) .*)$/
468:     readme    = File.read_utf(readme_file).split(header_re)[1..1] rescue ''
469: 
470:     unless readme.empty? then
471:       sections = Hash[*readme.map { |s|
472:         s =~ /^[=#]/ ? s.strip.downcase.chomp(':').split.last : s.strip
473:       }]
474:       desc     = sections.values_at(*description_sections).join("\n\n")
475:       summ     = desc.split(/\.\s+/).first(summary_sentences).join(". ")
476: 
477:       self.description ||= desc
478:       self.summary     ||= summ
479:       self.url         ||= readme[1].gsub(/^\* /, '').split(/\n/).grep(/\S+/)
480:     else
481:       missing readme_file
482:     end
483: 
484:     self.changes ||= begin
485:                        h = File.read_utf(history_file)
486:                        h.split(/^(={2,}|\#{2,})/)[1..2].join.strip
487:                      rescue
488:                        missing history_file
489:                        ''
490:                      end
491:   end
load_plugin_tasks() click to toggle source

Load activated plugins by calling their define tasks method.

     # File lib/hoe.rb, line 496
496:   def load_plugin_tasks
497:     bad = []
498: 
499:     $plugin_max = self.class.plugins.map { |s| s.to_s.size }.max
500: 
501:     self.class.plugins.each do |plugin|
502:       warn "define: #{plugin}" if $DEBUG
503: 
504:       old_tasks = Rake::Task.tasks.dup
505: 
506:       begin
507:         send "define_#{plugin}_tasks"
508:       rescue NoMethodError => e
509:         warn "warning: couldn't activate the #{plugin} plugin, skipping"
510:         bad << plugin
511:         next
512:       end
513: 
514:       (Rake::Task.tasks - old_tasks).each do |task|
515:         task.plugin = plugin
516:       end
517:     end
518:     @@plugins -= bad
519:   end
missing(name) click to toggle source

Bitch about a file that is missing data or unparsable for intuiting values.

     # File lib/hoe.rb, line 524
524:   def missing name
525:     warn "** #{name} is missing or in the wrong format for auto-intuiting."
526:     warn "   run `sow blah` and look at its text files"
527:   end
normalize_deps(deps) click to toggle source

Normalize the dependencies.

     # File lib/hoe.rb, line 532
532:   def normalize_deps deps
533:     Array(deps).map { |o|
534:       if String === o then
535:         warn "WAR\NING: HOE DEPRECATION: Add '>= 0' to the '#{o}' dependency."
536:         [o, ">= 0"]
537:       else
538:         o
539:       end
540:     }
541:   end
paragraphs_of(path, *paragraphs) click to toggle source

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)
     # File lib/hoe.rb, line 549
549:   def paragraphs_of path, *paragraphs
550:     File.read_utf(path).delete("\r").split(/\n\n+/).values_at(*paragraphs)
551:   end
pluggable!() click to toggle source

Tell the world you’re a pluggable package (ie you require rubygems 1.3.1+)

This uses require_rubygems_version. Last one wins. Make sure you account for that.

     # File lib/hoe.rb, line 559
559:   def pluggable!
560:     abort "update rubygems to >= 1.3.1" unless  Gem.respond_to? :find_files
561:     require_rubygems_version '>= 1.3.1'
562:   end
post_initialize() click to toggle source

Finalize configuration

     # File lib/hoe.rb, line 567
567:   def post_initialize
568:     intuit_values
569:     validate_fields
570:     add_dependencies
571:     define_spec
572:     load_plugin_tasks
573:   end
require_ruby_version(version) click to toggle source

Declare that your gem requires a specific ruby version. Last one wins.

     # File lib/hoe.rb, line 585
585:   def require_ruby_version version
586:     spec_extras[:required_ruby_version] = version
587:   end
require_rubygems_version(version) click to toggle source

Declare that your gem requires a specific rubygems version. Last one wins.

     # File lib/hoe.rb, line 578
578:   def require_rubygems_version version
579:     spec_extras[:required_rubygems_version] = version
580:   end
timebomb(n, m, finis = '2010-04-01', start = '2009-03-14') click to toggle source

Provide a linear degrading value from n to m over start to finis dates.

     # File lib/hoe.rb, line 592
592:   def timebomb n, m, finis = '2010-04-01', start = '2009-03-14'
593:     require 'time'
594:     finis = Time.parse finis
595:     start = Time.parse start
596:     rest  = (finis - Time.now)
597:     full  = (finis - start)
598: 
599:     ((n - m) * rest / full).to_i + m
600:   end
validate_fields() click to toggle source

Verify that mandatory fields are set.

     # File lib/hoe.rb, line 605
605:   def validate_fields
606:     %(email author).each do |field|
607:       value = self.send(field)
608:       abort "Hoe #{field} value not set. aborting" if value.nil? or value.empty?
609:     end
610:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.