Class: Hoe

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

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

  • debug_gem - Show information about the gem.

  • default - Run the default tasks

  • 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_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 =
'1.1.4'
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| ... } ⇒ Hoe

Returns a new instance of Hoe.

Yields:

  • (_self)

Yield Parameters:

  • _self (Hoe)

    the object that the method was called on



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/hoe.rb', line 111

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.txt email.txt ri *.gem **/*~)
  self.test_globs = ['test/**/test_*.rb']
  self.changes = "#{author} is too lazy to write a changeset"
  self.description = "#{author} is too lazy to write a description"
  self.summary = "#{author} is 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

  if name == 'hoe' then
    extra_deps << ['rake']
    extra_deps << ['rubyforge', '>= 0.3.1']
  else
    extra_deps << ['hoe', ">= #{VERSION}"]
  end

  yield self if block_given?

  define_tasks
end

Instance Attribute Details

#authorObject

Returns the value of attribute author.



109
110
111
# File 'lib/hoe.rb', line 109

def author
  @author
end

#bin_filesObject

Returns the value of attribute bin_files.



109
110
111
# File 'lib/hoe.rb', line 109

def bin_files
  @bin_files
end

#changesObject

Returns the value of attribute changes.



109
110
111
# File 'lib/hoe.rb', line 109

def changes
  @changes
end

#clean_globsObject

Returns the value of attribute clean_globs.



109
110
111
# File 'lib/hoe.rb', line 109

def clean_globs
  @clean_globs
end

#descriptionObject

Returns the value of attribute description.



109
110
111
# File 'lib/hoe.rb', line 109

def description
  @description
end

#emailObject

Returns the value of attribute email.



109
110
111
# File 'lib/hoe.rb', line 109

def email
  @email
end

#extra_depsObject

Returns the value of attribute extra_deps.



109
110
111
# File 'lib/hoe.rb', line 109

def extra_deps
  @extra_deps
end

#lib_filesObject

Returns the value of attribute lib_files.



109
110
111
# File 'lib/hoe.rb', line 109

def lib_files
  @lib_files
end

#nameObject

Returns the value of attribute name.



109
110
111
# File 'lib/hoe.rb', line 109

def name
  @name
end

#need_tarObject

Returns the value of attribute need_tar.



109
110
111
# File 'lib/hoe.rb', line 109

def need_tar
  @need_tar
end

#need_zipObject

Returns the value of attribute need_zip.



109
110
111
# File 'lib/hoe.rb', line 109

def need_zip
  @need_zip
end

#rdoc_patternObject

Returns the value of attribute rdoc_pattern.



109
110
111
# File 'lib/hoe.rb', line 109

def rdoc_pattern
  @rdoc_pattern
end

#rubyforge_nameObject

Returns the value of attribute rubyforge_name.



109
110
111
# File 'lib/hoe.rb', line 109

def rubyforge_name
  @rubyforge_name
end

#specObject

Returns the value of attribute spec.



109
110
111
# File 'lib/hoe.rb', line 109

def spec
  @spec
end

#spec_extrasObject

Returns the value of attribute spec_extras.



109
110
111
# File 'lib/hoe.rb', line 109

def spec_extras
  @spec_extras
end

#summaryObject

Returns the value of attribute summary.



109
110
111
# File 'lib/hoe.rb', line 109

def summary
  @summary
end

#test_filesObject

Returns the value of attribute test_files.



109
110
111
# File 'lib/hoe.rb', line 109

def test_files
  @test_files
end

#test_globsObject

Returns the value of attribute test_globs.



109
110
111
# File 'lib/hoe.rb', line 109

def test_globs
  @test_globs
end

#urlObject

Returns the value of attribute url.



109
110
111
# File 'lib/hoe.rb', line 109

def url
  @url
end

#versionObject

Returns the value of attribute version.



109
110
111
# File 'lib/hoe.rb', line 109

def version
  @version
end

Instance Method Details

#announcementObject

end define



393
394
395
396
397
398
399
400
401
# File 'lib/hoe.rb', line 393

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



143
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
184
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
# File 'lib/hoe.rb', line 143

def define_tasks
  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
  
  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.txt").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/)
  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
    require 'rubyforge'
    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.config
    c["release_notes"] = description if description
    c["release_changes"] = changes if changes
    c["preformatted"] = true

    puts "Releasing #{name} v. #{version} tarball"
    release_id = rf.add_release rubyforge_name, name, version, "#{pkg}.tgz"
    if release_id then
      puts "  release_id = #{release_id.inspect}"
      puts "Releasing #{name} v. #{version} gem"
      rf.add_file rubyforge_name, name, release_id, "#{pkg}.gem"
    else
      abort "Couldn't get release_id"
    end
  end

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

  Rake::RDocTask.new(:docs) do |rd|
    rd.main = "README.txt"
    rd.options << '-d' if RUBY_PLATFORM !~ /win32/ and `which dot` =~ /\/dot/
    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 != 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, :docs] do
    config = YAML.load(File.read(File.expand_path("~/.rubyforge/config.yml")))
    user = "#{config["username"]}@rubyforge.org"
    project = "/var/www/gforge-projects/#{rubyforge_name}"
    project += "/#{name}" if rubyforge_name != name
    local_dir = 'doc'
    pub = Rake::SshDirPublisher.new user, project, local_dir
    if rubyforge_name != name then
      def pub.upload
        begin
          super
        rescue
          # project directory probably doesn't exist, transfer as a whole
          sh %{scp -qr #{local_dir} #{host}:#{remote_dir}}
        end
      end
    end
    pub.upload
  end

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

  desc 'Run ZenTest against the package'
  task :audit do
    libs = %w(lib test).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 unless files.empty?
    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 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]

  desc "Verify the manifest"
  task :check_manifest => :clean do
    f = "Manifest.tmp"
    system "find . -type f | egrep -v 'svn|tmp$' | cut -c3- | sort > #{f}"
    system "diff -du Manifest.txt #{f}"
    rm f
  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.txt', 0..1).join("\n\n")
summary, *description = p.paragraphs_of('Readme.txt', 3, 3..8)


422
423
424
425
# File 'lib/hoe.rb', line 422

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

#run_tests(multi = false) ⇒ Object

:nodoc:



403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/hoe.rb', line 403

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