Module: Tasks

Includes:
Reap::TaskUtils
Included in:
TaskSpace
Defined in:
lib/reap/tasks.rb

Overview

Tasks is the namespace in which to define task generators.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Reap::TaskUtils

#ask, #initialize, #master, #preq_from_name, #provide_setup_rb, #sh, #tell

Class Method Details

.avail(name, &cond) ⇒ Object

Used to set task generator availablity condition.



31
32
33
# File 'lib/reap/tasks.rb', line 31

def self.avail( name, &cond )
  available[name] = cond
end

.availableObject

Used to determine task generator availablity. – TODO This is kind of a weak point in the design, I think. And tasks themselves also need to have Rake’s #needed? feature. ++



25
26
27
# File 'lib/reap/tasks.rb', line 25

def self.available
  @available ||= {}
end

Instance Method Details

#announce(name, &data) ⇒ Object

Announce

The announce task is intended for sending out a nice formated email message to a mailing address, especially mailing-lists.

Announcement specific settings:

server       Email server to route message.
port         Email server's port.
domain       Email server's domain name.
account      Email account name.
type         Login type, either plain, cram_md5 or login.
secure       Uses TLS security, true or false?
to           Email address to send announcemnt.
from         Email address sent from.
subject      Subject of email message.
links        Array of http links to related sites.
file         File that contains announcement message.
memo         Embedded announcement message.
slogan       Motto for you project.

Inherited settings:

title        Project title.
summary      Brief one-line description.
description  Long description of project.
homepage     Project homepage web address.


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/reap/tasks.rb', line 65

def announce( name, &data )

  require 'reap/class/announce.rb'

  desc "Email project announcement" unless desc

  task name do
    data = data.to_openobject
    data.title       ||= master.title
    data.description ||= master.description
    data.version     ||= master.version
    data.from        ||= master.email
    Reap::Announce.new( data ).write_and_email
  end

end

#backup(name, &data) ⇒ Object

Backup

Backup your project to a backup directory. The location of the backup will be under the backup dir given in the backup section of the ProjectInfo file, then under the name of the project’s dir and a subdirectory of today’s date. For example, today reap itself was backed up to:

../BACKUPS/reap/2006-06-21/reap

Backup specific settings:

dir    The backup directory (eg. '../BACKUPS').


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/reap/tasks.rb', line 97

def backup( name, &data )

  desc "Backup project folder" unless desc

  task name do
    data = data.to_openobject
    if data.dir
      bdir = ::File.join( ::File.basename(Dir.pwd), Time.now.strftime("%Y_%m_%d") )
      bdir = ::File.join( data.dir, bdir )
      if File.exist?( bdir ) and not $FORCE
        tell bdir
        tell "Backup folder already exists. Use -f to overwrite."
      else
        if $PRETEND
          puts "rm -r #{bdir}"
          puts "mkdir -p #{bdir}"
          puts "cp -r #{Dir.pwd} #{bdir}"
        else
          FileUtils.rm_r( bdir ) if File.exist?( bdir )
          FileUtils.mkdir_p( bdir )
          FileUtils.cp_r( Dir.pwd, bdir)
        end
        tell "Completed backup to '#{bdir}'."
      end
    else
      tell "No backup 'dir' setting given for backup task."
    end
  end

end

#count(name, &data) ⇒ Object

Count

Count the file and lines of code in your project.

dir     The directory of scripts to count.


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
# File 'lib/reap/tasks.rb', line 281

def count( name, &data )

  desc "Line count project scripts" unless desc

  task name do
    data = data.to_openobject
    incl = data.include || 'lib/**/*'

    fc, l, c, t, bt, r, rb = 0, 0, 0, 0, false, 0, false
    Dir.glob( incl ).each do |fname|
      next unless fname =~ /.*rb/      # TODO should this be done?
      fc += 1
      File.open( fname ) do |f|
        while line = f.gets
          l += 1
          next if line =~ /^\s*$/
          case line
          when /^=begin\s+test/
            tb = true; t+=1
          when /^=begin/
            rb = true; r+=1
          when /^=end/
            r+=1 if !(rb or tb)
            (rb = false; r+=1) if rb
            (tb = false; t+=1) if tb
          when /^\s*#/
            r += 1
          else
            c+=1 if !(rb or tb)
            r+=1 if rb
            t+=1 if tb
          end
        end
      end
    end
    s = l - c - r
    puts "FILES: #{fc}, LINES: #{l}, CODE: #{c}, DOC: #{r}, TEST: #{t}, SPACE: #{s}"
  end

end

#doap(name, &data) ⇒ Object

Doap

This task generates an XML DOAP project file.

DOAP is an XML/RTF format for describing a project. It contains much of the same information as Reap’s ProjectInfo file, but is more suitable to RESTful interapplication communications, like RSS/Atom feeds –useful to project tracking sites.

The task gets almost all of the required parametes from the root node (i.e. master) of ProjectInfo file.



140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/reap/tasks.rb', line 140

def doap( name, &data )

  require 'reap/class/doap.rb'

  desc "Generate DOAP project file" unless desc

  task name do
    data = data.to_openobject
    Reap::Doap.new( data.__merge__( master ) ).call
  end

end

#extest(name, &data) ⇒ Object

ExTest (Extract Tests)

This task scans every package script looking for sections of the form:

=begin test
  ...
=end

With appropriate headers, it copies these sections to files in your project’s test/ dir, which then can be run using the Reap test task. The exact directory layout of the files to be tested is reflected in the test directory. You can then use Reap’s test task to run the tests.

Specific Settings:

dir        Specify the test directory. Default is 'test'.
files      Specify files to extract. Default is 'lib/**/*.rb'.


176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/reap/tasks.rb', line 176

def extest( name, &data )

  require 'reap/class/extest.rb'

  desc "Extract embedded unit tests from scripts" unless desc

  task name do
    data = data.to_openobject
    Reap::ExTest.new( data ).extract
  end

end

#file(name, &exe) ⇒ Object

File



191
192
193
194
195
196
197
198
199
200
201
# File 'lib/reap/tasks.rb', line 191

def file( name, &exe )

  require 'reap/class/filer'

  name, source = preq_from_name( name )

  task name do
    Reap::Filer.new( name, source, &exe ).build
  end

end

#info(name) ⇒ Object

Info

Displays the ProjectInfo file. This task is of little use –it’s easy enough to look at a ProjectInfo file with less or your favorite editor. Mainly this task serves as a development example.



210
211
212
213
214
215
216
217
218
# File 'lib/reap/tasks.rb', line 210

def info( name )

  desc "Display ProjectInfo file" unless desc

  task name do
    puts ProjectInfo.instance.info_stream
  end

end

#manifest(name, &data) ⇒ Object

Manifest

Create a manifest file for the package. Presently it is a very simple md5 + filename manifest. In the future this will be exanded to build a varity of manifest formats.

Task specific settings:

include       Files to include
exclude       Files to exclude from the included.


334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/reap/tasks.rb', line 334

def manifest( name, &data )

  require 'reap/class/manifest'

  desc "Create a MANIFEST file for this package" unless desc

  task name do
    data = data.to_openobject
    Reap::Manifest.new( data ).generate
  end

end

#package(name, &data) ⇒ Object

Package

This task creates standard .zip, .tgz, or .tbz packages, plus .gem or .deb distributions.

Builds distribution packages. The package task supports tar.gz, tar.bz2, zip source packages and gem, pacman and debian ditribution packages.

Task specific settings:

dir             Directory in which to store distributions.
include         Files to include in distribution.
exclude         Files to exclude from those.
distribute      List of distribution types desired. Eg.
                  tgz, tar.gz, tbz, tar.bz2, zip
                  gem, deb, pac
project         Project name.
category        Software category.
architecture    Can be any, i368, i686, ppc, etc.
dependencies    List of packages this program depends.
recommends      List of packages that can be used with this package.
replaces        List of packages this one replaces.
executables     Executable files in this distribution.
rules           (see below)

RubyGems specific settings:

autorequire
platform
require_paths

The package task also has subsection for each type of distribution. These can be used to override settings in the package information if they in some way differ. Possible subsections are:

gems
pacman
debian

Finally there is one last parameter that you can use for creating packages called ‘rules’. The rules setting allows you to define how files are copied into the distribution package, so instead of a one to one copy of the included files, you can actually have a file placed in a different location within the distribution. This can be very handy if you wish to develop you project with one layout, but need to distribute it with another.

The rules parameter is a literal string that consists of one rule per line. A line consists three space separated entries.

from_path, file_glob, to_path

If no ‘to_path’ is given then it is considered the same as the ‘from_path’. It also supports two variables $name and $version which will be substitued any of these entries. Here is a possible example:

rules: |
  lib **/* lib/$name/$version

This will move any file under lib/ to the equivalent location under lib/$name/$version/. The default set of rules is a mirror image transfer, spelled out it would be:

rules: |
  bin  *
  ext  **/*
  lib  **/*
  data **/*
  conf **/*

If your using Rolls against a normal project folder the alterntive is to create versioned paths, probably as follows:

rules: |
  ext/$name   **/*  ext/$name/$version
  lib/$name   **/*  lib/$name/$version
  data/$name  **/*  ext/$name/$version
  conf/$name  **/*  ext/$name/$version  ?

Please note that the rules parameter is a new feature and is still considered beta.



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/reap/tasks.rb', line 427

def package( name, &data )

  require 'reap/class/package'

  desc "Create distribution packages" unless desc

  task name do
    data = data.to_openobject

    data.name         ||= master.name
    data.title        ||= master.title
    data.version      ||= master.version
    data.status       ||= master.status
    data.series       ||= master.series
    data.author       ||= master.author
    data.maintainer   ||= master.maintainer
    data.email        ||= master.email
    data.summary      ||= master.summary
    data.architecture ||= master.architecture
    data.license      ||= master.license
    data.project      ||= master.project
    data.homepage     ||= master.homepage

    Reap::Package.new( data ).generate_packages
  end

end

#publish(name, &data) ⇒ Object

Publish

Publish documents to hosting service. Three means of publishing are current supported, ftp uploading, scp, and web convenience option that recognizes particular hosts (only RubyForge is currently supported).

type           Type of publishing, 'web', 'scp' or 'ftp'.
host           Host server, default is 'rubyforge.org'
username       Username for host.
dir            Directory of files to publish. Or,
copy           Files to publish using from and to.
project        Project name (used for Rubyforge)

If the type is ‘scp’ or ‘ftp’ you will also need to provide the root directory parameter.

root           Document root directory at host.

The dir parameter allows you to simply specify a local directory, the contents of which will be published to host’s document root location (directory tree intact).

If you need more control over which files to publish where, you can use the copy parameter instead. Provide an array of pattern strings in the form of “from to”. If the desitination is the host’s document root you do not need to specify the to part. For example:

copy: [ 'web/*', 'doc/api/* doc/api' ]

The first copies the files under your project’s web directory to the host’s document root. The second copies your projects doc/api files to the doc/api location on the host.

When using the ‘scp’ type the internal template used for the outbound destination is ‘username@host:root/’.



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/reap/tasks.rb', line 493

def publish( name, &data )

  require 'reap/class/publish.rb'

  desc "Publish documents to the web or host" unless desc

  task name do
    data = data.to_openobject

    data.project  ||= master.rubyforge.project || master.name
    data.username ||= master.rubyforge.username

    Reap::Publish.new( data ).publish
  end

end

#rdoc(name, &data) ⇒ Object

RDoc

This task generates RDoc API documentation from source comments.

Task specific settings:

dir        Directory to store documentation [doc].
main       File to use as main page of RDocs.
template   Which RDoc template to use.
include    Files to include in RDocs.
exclude    Files to exclude from those.
options    Pass-thru extra options to RDoc command.

Inherited settings:

title      Project title to use in RDocs.


529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/reap/tasks.rb', line 529

def rdoc( name, &data )

  require 'reap/class/rdoc'

  desc "Generate RDoc API documentation" unless desc

  task name do
    data = data.to_openobject

    data.title ||= master.title

    Reap::RDoc.new( data ).call
  end

end

#release(name, &data) ⇒ Object

Release

This task releases files to RubyForge –it should work with other GForge instaces or SourceForge clones too.

While defaults are nice, you may want a little more control. You can specify additional attributes:

dir            Distribution directory
exclude        Distribution types to exclude
host           URL of host service
username       Username of host service
project        Project name at host
package        Package name
date           Date of release (defaults to Time.now)
groupid        Group id number
processor      Processor/Architecture (Any, i386, PPC, etc.)
release        Release name (default is "%s")
is_public      Public release?
changelog      Change log file
notelog        Release notes file

The release option can be a template by using %s in the string. The version number of your project will be sub’d in for the %s. This saves you from having to update the release name before every release.



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/reap/tasks.rb', line 572

def release( name, &data )

  require 'reap/class/release'

  desc "Release distribution files" unless desc

  task name do
    data = data.to_openobject

    data.version  = master.version
    data.project  ||= master.rubyforge.project || master.name
    data.username ||= master.rubyforge.username
    data.package  ||= master.rubyforge.package
    data.groupid  ||= master.rubyforge.groupid

    Reap::Release.new( data ).release
  end

end

#scaffold(name) ⇒ Object

Scaffold

Generates a project directory layout within the current directory. The scaffolding includes all the standard features such as directories lib/ bin/ doc/ and files such as README.

To use this task you should first create an empty directory and than change into it. This command will not create a new project directory. As a safegaurd, this command will not generate scaffolding in a directory with files already present, unless you use the -f (force) option.

There are currently two types of supported scaffoldings: ‘standard’, which is the default if no type is given, and ‘subversion’ which creates a “trunk” hierarchy.



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
# File 'lib/reap/tasks.rb', line 609

def scaffold( name )

  desc "Generate a project directory layout" unless desc

  task name do |type|
    content = Dir.entries('.') - [ '.', '..' ]
    if not content.empty? and not $FORCE
      tell "Directory already has content. Use -f option to force scaffolding."
      return nil
    end
    type ||= 'standard'
    type = 'standard' if type == 'std'
    type = 'subversion' if type == 'svn'
    # gems workaround
    if dir = Gem.gempath('reap')
      dir = File.join( dir, 'data', 'reap', 'scaffold', type )
    else
      dir = File.join( ::Config::CONFIG['datadir'], 'reap', 'scaffold', type )
    end
    #
    unless File.directory?( dir )
      tell "Unrecognized project type."
    else
      #FileUtils.mkdir_p( name )
      FileUtils.cp_r( File.join( dir, '.'), '.' )
      tell "Project ready."
    end
  end

end

#setup(name, &data) ⇒ Object

Setup

This task manually installs a project using setup.rb. If setup.rb doesn’t exist it will be created.



649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'lib/reap/tasks.rb', line 649

def setup( name, &data )

  desc "Locally install package using setup.rb" unless desc

  task name do
    data = data.to_openobject

    data.options ||= []

    unless provide_setup_rb
      puts "Setup.rb is missing. Forced to cancel task."
      return nil
    end

    puts "Reap is shelling out work to setup.rb..."

    #--
    # SHELL OUT! This will be fixed with swtich to Reap's installer instead of setup.rb.
    #++
    exe = %{ruby setup.rb}
    exe << ' -q ' unless $VERBOSE
    exe << data.options.join(' ')
    exe << ' all'

    #success = false
    success = sh( exe )

    puts "Setup complete!" if success
  end

end

#template(name) ⇒ Object

Template

Creates an empty ProjectInfo file in the current directory. It will not overwrite a ProjectInfo file if one is already present. Unlike ‘scaffold’, this is especially useful for pre-existing projects.



688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
# File 'lib/reap/tasks.rb', line 688

def template( name )

  require 'facet/gem/self/gempath'

  desc "Create a ProjectInfo template" unless desc

  task name do
    f = nil
    if ::ProjectInfo::INFO_FILES.any?{ |f| File.exists?(f) }
      puts "Project file '#{f}' already exists."
      return
    end
    filename = 'ProjectInfo'
    # if using gems
    if dir = Gem.gempath('reap')
      dir = File.join( dir, 'data', 'reap', 'scaffold', 'standard' )
    else
      dir = File.join( ::Config::CONFIG['datadir'], 'reap', 'scaffold', 'standard' )
    end
    f = File.join( dir, filename )
    raise "ProjectInfo template file #{f} is missing." unless File.file?( f )
    # copy
    FileUtils.install( f, '.' )
    tell "#{filename} created. You'll need to fill it out."
  end

end

#test(name, &data) ⇒ Object

Test

The Reap test class runs each test in it’s own proccess, making for a more pure test facility. Reap runs each test in a separate proccess to aviod potential conflicts between scripts.

files       Test files (eg. test/tc_**/*.rb)
            Defaults to typcial selection.

libs        List of lookup directories to include in
            load path. './lib' is always included.

live        Flag to quickly deactive use of local libs.
            Test against installed files instead.

requires    List of any files to pre-require.

NOTE This works well enough but it is a tad delicate. It actually marshals test results across stdout->stdin shell pipe. One consequence of this is that you can’t send debug info to stdout in your tests (including #p and #puts).



743
744
745
746
747
748
749
750
751
752
753
754
# File 'lib/reap/tasks.rb', line 743

def test( name, &data )

  require 'reap/class/test'

  desc "Run unit-tests (each in a separate process)" unless desc

  task name do
    data = data.to_openobject
    Reap::Test.new( data ).call
  end

end