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

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
def self.add_include_dirs(*dirs)
  dirs = dirs.flatten
  $:.unshift(*dirs)
  s = File::PATH_SEPARATOR
  RUBY_FLAGS.sub!(/-I/, "-I#{dirs.join(s)}#{s}")
end
load_plugins(plugins = Hoe.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
def self.load_plugins plugins = Hoe.plugins
  @found  ||= {}
  @loaded ||= {}

  Gem.find_files("hoe/*.rb").reverse.each do |path|
    @found[File.basename(path, ".rb").intern] = path
  end

  :keep_doing_this while @found.map { |name, plugin|
    next unless plugins.include? name
    next if @loaded[name]
    begin
      warn "loading #{plugin}" if $DEBUG
      @loaded[name] = require plugin
    rescue LoadError => e
      warn "error loading #{plugin.inspect}: #{e.message}. skipping..."
    end
  }.any?

  return @loaded, @found
end
plugin(*plugins) click to toggle source

Activates plugins. If a plugin cannot be loaded it will be ignored.

Plugins may also be activated through a plugins array in ~/.hoerc. This should only be used for plugins that aren’t critical to your project and plugins that you want to use on other projects.

# File lib/hoe.rb, line 274
def self.plugin *plugins
  self.plugins.concat plugins
  self.plugins.uniq!
end
plugins() click to toggle source

The list of active plugins.

# File lib/hoe.rb, line 282
def self.plugins
  @@plugins
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 295
def self.spec name, &block
  Hoe.load_plugins

  spec = self.new name
  spec.activate_plugins
  spec.instance_eval(&block)
  spec.post_initialize
  spec # TODO: remove?
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 308
def activate_plugins
  plugins = Hoe.plugins

  with_config do |config, _|
    config_plugins = config['plugins']
    break unless config_plugins
    plugins += config_plugins.map { |plugin| plugin.intern }
  end

  Hoe.load_plugins plugins

  names = Hoe.constants.map { |s| s.to_s }
  names.reject! { |n| n =~ /^[A-Z_]+$/ }

  names.each do |name|
    next unless plugins.include? name.downcase.intern
    warn "extend #{name}" if $DEBUG
    self.extend Hoe.const_get(name)
  end

  Hoe.plugins.each do |plugin|
    msg = "initialize_#{plugin}"
    warn msg if $DEBUG
    send msg if self.respond_to? msg
  end
end
add_dependencies() click to toggle source

Add standard and user defined dependencies to the spec.

# File lib/hoe.rb, line 338
def add_dependencies
  self.extra_deps     = normalize_deps extra_deps
  self.extra_dev_deps = normalize_deps extra_dev_deps

  case name
  when 'hoe' then
    extra_deps << ['rake', ">= #{RAKEVERSION}"]
  when 'rubyforge', 'rake', 'gemcutter' then
    # avoid circular dependencies for hoe's (potentially) hoe'd dependencies
  else
    extra_dev_deps << ['hoe', ">= #{VERSION}"]
  end
end
define_spec() click to toggle source

Define the Gem::Specification.

# File lib/hoe.rb, line 362
def define_spec
  self.spec = Gem::Specification.new do |s|
    dirs = Dir['lib']

    s.name                 = name
    s.version              = version if version
    s.summary              = summary
    s.email                = email
    s.homepage             = Array(url).first
    s.rubyforge_project    = rubyforge_name
    s.description          = description
    s.files = files        = File.read_utf("Manifest.txt").split(/\r?\n\r?/)
    s.executables          = s.files.grep(/^bin/) { |f| File.basename(f) }
    s.bindir               = "bin"
    s.require_paths        = dirs unless dirs.empty?
    s.rdoc_options         = ['--main', readme_file]
    s.has_rdoc             = true
    s.post_install_message = post_install_message
    s.test_files           = Dir[*self.test_globs]

    missing "Manifest.txt" if files.empty?

    case author
    when Array
      s.authors = author
    else
      s.author  = author
    end

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

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

    s.extra_rdoc_files += s.files.grep(/txt$/)
    s.extra_rdoc_files.reject! { |f| f =~ %^(test|spec|vendor|template|data|tmp)/% }
    s.extra_rdoc_files += @extra_rdoc_files

  end

  unless self.version then
    version    = nil
    version_re = /VERSION += +([\"\'])([\d][\w\.]+)\11//

    spec.files.each do |file|
      next unless File.exist? file
      version = File.read_utf(file)[version_re, 2]
      break if version
    end

    spec.version = self.version = version if version

    unless self.version then
      spec.version = self.version = "0.borked"
      warn "** Add 'VERSION = \"x.y.z\"' to your code,"
      warn "   add a version to your hoe spec,"
      warn "   or fix your Manifest.txt"
    end
  end

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

Returns the proper dependency list for the thingy.

# File lib/hoe.rb, line 355
def dependency_target
  self.name == 'hoe' ? extra_deps : extra_dev_deps
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 439
def developer name, email
  self.author << name
  self.email  << email
end
intuit_values() click to toggle source

Intuit values from the readme and history files.

# File lib/hoe.rb, line 484
def intuit_values
  header_re = /^((?:=+|#+) .*)$/
  readme    = File.read_utf(readme_file).split(header_re)[1..-1] rescue ''

  unless readme.empty? then
    sections = Hash[*readme.map { |s|
      s =~ /^[=#]/ ? s.strip.downcase.chomp(':').split.last : s.strip
    }]
    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_file
  end

  self.changes ||= begin
                     h = File.read_utf(history_file)
                     h.split(/^(={2,}|\#{2,})/)[1..2].join.strip
                   rescue
                     missing history_file
                     ''
                   end
end
load_plugin_tasks() click to toggle source

Load activated plugins by calling their define tasks method.

# File lib/hoe.rb, line 514
def load_plugin_tasks
  bad = []

  $plugin_max = self.class.plugins.map { |s| s.to_s.size }.max

  self.class.plugins.each do |plugin|
    warn "define: #{plugin}" if $DEBUG

    old_tasks = Rake::Task.tasks.dup

    begin
      send "define_#{plugin}_tasks"
    rescue NoMethodError => e
      warn "warning: couldn't activate the #{plugin} plugin, skipping" if
        Rake.application.options.trace or $DEBUG

      bad << plugin
      next
    end

    (Rake::Task.tasks - old_tasks).each do |task|
      task.plugin = plugin
    end
  end
  @@plugins -= bad
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 544
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) click to toggle source

Normalize the dependencies.

# File lib/hoe.rb, line 552
def normalize_deps deps
  Array(deps).map { |o|
    if String === o then
      warn "WAR\NING: HOE DEPRECATION: Add '>= 0' to the '#{o}' dependency."
      [o, ">= 0"]
    else
      o
    end
  }
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 569
def paragraphs_of path, *paragraphs
  File.read_utf(path).delete("\r").split(/\n\n+/).values_at(*paragraphs)
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 579
def pluggable!
  abort "update rubygems to >= 1.3.1" unless  Gem.respond_to? :find_files
  require_rubygems_version '>= 1.3.1'
end
plugin?(name) click to toggle source

Is a plugin activated? Used for guarding missing plugins in your hoe spec:

Hoe.spec "blah" do
  if plugin? :enhancement then
    self.enhancement = true # or whatever...
  end
end
# File lib/hoe.rb, line 594
def plugin? name
  self.class.plugins.include? name
end
post_initialize() click to toggle source

Finalize configuration

# File lib/hoe.rb, line 601
def post_initialize
  intuit_values
  validate_fields
  add_dependencies
  define_spec
  load_plugin_tasks
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 619
def require_ruby_version version
  spec_extras[:required_ruby_version] = version
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 612
def require_rubygems_version version
  spec_extras[:required_rubygems_version] = version
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 626
def timebomb n, m, finis = '2010-04-01', start = '2009-03-14'
  require 'time'
  finis = Time.parse finis
  start = Time.parse start
  rest  = (finis - Time.now)
  full  = (finis - start)

  [((n - m) * rest / full).to_i + m, m].max
end
validate_fields() click to toggle source

Verify that mandatory fields are set.

# File lib/hoe.rb, line 639
def validate_fields
  %(email author).each do |field|
    value = self.send(field)
    abort "Hoe #{field} value not set. aborting" if value.nil? or value.empty?
  end
end
with_config() click to toggle source

Loads ~/.hoerc and yields the configuration and its path

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

[Validate]

Generated with the Darkfish Rdoc Generator 2.