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:

  • audit - Run ZenTest against the package

  • clean - Clean up all the extras

  • debug_gem - Show information about the gem

  • default - Run the default tasks

  • docs - Build the docs HTML Files

  • 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

  • publish_docs - Publish RDoc to RubyForge

  • release - Package and upload the release to RubyForge

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

  • uninstall - Uninstall the package.

  • upload - Upload RDoc to RubyForge

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.

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

  • extra_deps - An array of rubygem dependencies.

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

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

Environment Variables

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

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

  • RUBY_DEBUG - Used to add extra flags to RUBY_FLAGS.

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

Constant Summary collapse

VERSION =
'1.1.2'
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 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



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/hoe.rb', line 103

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.extra_deps = []
  self.spec_extras = {}

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

  yield self if block_given?

  define_tasks
end

Instance Attribute Details

#authorObject

Returns the value of attribute author.



101
102
103
# File 'lib/hoe.rb', line 101

def author
  @author
end

#bin_filesObject

Returns the value of attribute bin_files.



101
102
103
# File 'lib/hoe.rb', line 101

def bin_files
  @bin_files
end

#changesObject

Returns the value of attribute changes.



101
102
103
# File 'lib/hoe.rb', line 101

def changes
  @changes
end

#clean_globsObject

Returns the value of attribute clean_globs.



101
102
103
# File 'lib/hoe.rb', line 101

def clean_globs
  @clean_globs
end

#descriptionObject

Returns the value of attribute description.



101
102
103
# File 'lib/hoe.rb', line 101

def description
  @description
end

#emailObject

Returns the value of attribute email.



101
102
103
# File 'lib/hoe.rb', line 101

def email
  @email
end

#extra_depsObject

Returns the value of attribute extra_deps.



101
102
103
# File 'lib/hoe.rb', line 101

def extra_deps
  @extra_deps
end

#lib_filesObject

Returns the value of attribute lib_files.



101
102
103
# File 'lib/hoe.rb', line 101

def lib_files
  @lib_files
end

#nameObject

Returns the value of attribute name.



101
102
103
# File 'lib/hoe.rb', line 101

def name
  @name
end

#rubyforge_nameObject

Returns the value of attribute rubyforge_name.



101
102
103
# File 'lib/hoe.rb', line 101

def rubyforge_name
  @rubyforge_name
end

#specObject

Returns the value of attribute spec.



101
102
103
# File 'lib/hoe.rb', line 101

def spec
  @spec
end

#spec_extrasObject

Returns the value of attribute spec_extras.



101
102
103
# File 'lib/hoe.rb', line 101

def spec_extras
  @spec_extras
end

#summaryObject

Returns the value of attribute summary.



101
102
103
# File 'lib/hoe.rb', line 101

def summary
  @summary
end

#test_filesObject

Returns the value of attribute test_files.



101
102
103
# File 'lib/hoe.rb', line 101

def test_files
  @test_files
end

#test_globsObject

Returns the value of attribute test_globs.



101
102
103
# File 'lib/hoe.rb', line 101

def test_globs
  @test_globs
end

#urlObject

Returns the value of attribute url.



101
102
103
# File 'lib/hoe.rb', line 101

def url
  @url
end

#versionObject

Returns the value of attribute version.



101
102
103
# File 'lib/hoe.rb', line 101

def version
  @version
end

Instance Method Details

#announcementObject

end define



359
360
361
362
363
364
365
366
367
# File 'lib/hoe.rb', line 359

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



132
133
134
135
136
137
138
139
140
141
142
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
# File 'lib/hoe.rb', line 132

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 '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

    spec_extras.each do |msg, val|
      s.send "#{msg}=", val
    end

    s.files = File.read("Manifest.txt").split
    s.executables = s.files.grep(/bin/) { |f| File.basename(f) }

    s.bindir = "bin"
    dirs = Dir['{lib,test}']
    s.require_paths = dirs unless dirs.empty?
    s.has_rdoc = true
    s.test_suite_file = "test/test_all.rb" if test ?f, "test/test_all.rb"
  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 = %w(test/test_sexp_processor.rb) # for ruby_to_c's tests.

  Rake::GemPackageTask.new spec do |pkg|
    pkg.need_tar = true
  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(/^(lib|bin)|txt$/)
    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)


388
389
390
391
# File 'lib/hoe.rb', line 388

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

#run_tests(multi = false) ⇒ Object

:nodoc:



369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/hoe.rb', line 369

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