Object
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.
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.
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.
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
duh
Used to add extra flags to RUBY_FLAGS.
Used to specify flags to ruby [has smart default].
Default configuration values for .hoerc. Plugins should populate this on load.
True if you’re a masochistic developer. Used for building commands.
Optional: A description of the release’s latest changes. Auto-populates to the top entry of History.txt.
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.
Optional: What sections from the readme to use for auto-description. Defaults to %w(description).
MANDATORY: The author’s email address(es). (can be array)
Use the # method to fill in both author and email cleanly.
Optional: Extra files you want to add to RDoc.
.txt files are automatically included (excluding the obvious).
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)
Optional: A short summary of the project. Auto-populates from the first sentence of the description.
See also: Hoe#description and Hoe.paragraphs_of.
Optional: The url(s) of the project. (can be array). Auto-populates to a list of urls read from the beginning of README.txt.
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
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
Activate plugins.
# File lib/hoe.rb, line 266
266: def self.plugin *names
267: self.plugins.concat names
268: self.plugins.uniq!
269: end
Return the list of activated plugins.
# File lib/hoe.rb, line 274
274: def self.plugins
275: @@plugins
276: end
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
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 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 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
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
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 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 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
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 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
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
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
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
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
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
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
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.