Class: Reap::Project

Inherits:
Object
  • Object
show all
Includes:
Utilities
Defined in:
lib/reap/project.rb,
lib/reap/defaults.rb,
lib/reap/metadata.rb,
lib/reap/settings.rb,
lib/reap/project/gem.rb,
lib/reap/project/log.rb,
lib/reap/project/scm.rb,
lib/reap/project/html.rb,
lib/reap/project/make.rb,
lib/reap/project/rdoc.rb,
lib/reap/project/site.rb,
lib/reap/project/spec.rb,
lib/reap/project/test.rb,
lib/reap/project/check.rb,
lib/reap/project/clean.rb,
lib/reap/project/stats.rb,
lib/reap/project/package.rb,
lib/reap/project/publish.rb,
lib/reap/project/release.rb,
lib/reap/project/version.rb,
lib/reap/project/announce.rb,
lib/reap/project/scaffold.rb

Overview

Project

The Project class is the main class of Reap. It provides the tools for working with a project. The CLI Application class delegates to this class, for instance.

Defined Under Namespace

Classes: Defaults, Metadata, Settings

Constant Summary collapse

MAKE_COMMAND =

The Make tool routes to extension Makefile(s). Presently, it is designed to support only extconf.rb design.

TODO: win32 cross-compile ?

ENV['make'] || (RUBY_PLATFORM =~ /(win|w)32$/ ? 'nmake' : 'make')
PACKAGE_STORE =
'pkg'

Instance Method Summary collapse

Methods included from Utilities

#ask, #bin?, #cd, #command_paths, #compress, #dir!, #dir?, directory!, directory?, #email, exist!, exist?, #exists!, #exists?, #file!, #file?, #fileutils, #glob, #list_option, #multiglob, #multiglob_r, #out_of_date?, #password, path!, path?, #read, #rm_r, #safe?, #sh, #stage, #stage_manifest, #status, #write, #ziputils

Constructor Details

#initialize(options = nil) ⇒ Project

New Project.

Raises:

  • (LoadError)


43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/reap/project.rb', line 43

def initialize(options=nil)
  @options        = options || {}

  @location       = locate

  raise LoadError, "no .reap configuration file" unless @location

  @metadata = Metadata.read(location)
  @settings = Settings.read(location, @metadata)
  @defaults = Defaults.new(@metadata)
  @runmodes = RunModes.new(options)
end

Instance Method Details

#announce(options = nil) ⇒ Object

Generate and email a release announcement. The announcement text is read from ANNOUNCEtxt or another template file is specified, or if no template is given, the announcemnet is automatically built from project metadata, ant the CHANGES and NOTES files.

Templates support metadata substitutions using $name$ syntax. Also, it will subsititue the first line matching /please see notes/i for the notelog. And /please see change/i for the changelog.

The following settings apply:

template     Announcement template file (ANNOUNCE.txt).
to           Email address(es) to send announcemnt.

If mailto is set then these also apply:

from         Message FROM address [email].
subject      Subject of email message ([ANN] title verison).
server       Email server to route message.
port         Email server's port.
domain       Email server's domain name.
account      Email account name [email].
login        Login type: plain, cram_md5 or login.
secure       Uses TLS security, true or false?

The announcement will be printed to standard out before sending so it can be verified.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/reap/project/announce.rb', line 36

def announce(options=nil)
  options = configure_options(options, 'announce')

  announcement = Announcement.new do |ann|
    ann.cutoff   = options['cutoff']
    ann.template = options['template']
    ann. = 
  end

  if dryrun?
    puts "\n#{announcement.message}\n\n" if verbose?
  else
    puts "\n#{announcement.message}\n\n"
  end

  options['message'] = announcement.message
  options['version'] = .version

  options['title']   ||= .title
  options['subject'] ||= "%s, v%s release"
  options['subject'] = options['subject'] % [ options['title'], .version ]

  actions = []
  select  = options['hosts']

  hosts(select).each do |host|
    if host.respond_to?(:announce)
      if host.announce_confirm?(options)
        actions << lambda{ host.announce(options) }
      end
    end
  end

  actions.each{ |a| a.call }
end

#chdir_to_project(&block) ⇒ Object

Change directory to project’s root location, and execute block if given. If a block is provided, the current directory will revert back to what it was prior to this call, otherwise it will remain changed.



132
133
134
135
136
137
138
# File 'lib/reap/project.rb', line 132

def chdir_to_project(&block)
  if block
    Dir.chdir(location, &block)
  else
    Dir.chdir(location)
  end
end

#check_load(options = nil) ⇒ Object

Load each script independently to ensure there are no require dependency issues.

WARNING! You should only run this on scripts that have no toplevel side-effects!!!

This takes one option :scripts which is a glob or list of globs of the scripts to check. By default this is all scripts in the libpath(s).

FIXME: This isn’t routing output to dev/null as expected ?



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/reap/project/check.rb', line 63

def check_load(options=nil)
  options = configure_options(options, 'check-load', 'check')

  make if compiles?

  libpath = options['libpath'] || .libpath
  exclude = options['exclude']

  libpath = list_option(libpath)
  exclude = list_option(exclude)

  files = multiglob_r(*libpath) - multiglob_r(exclude)
  files   = files.select{ |f| File.extname(f) == '.rb' }
  max   = files.collect{ |f| f.size }.max
  list  = []

  files.each do |s|
    next unless File.file?(s)
    #if not system "ruby -c -Ibin:lib:test #{s} &> /dev/null" then
    cmd = "ruby -I#{libpath.join(':')} #{s} > /dev/null 2>&1"
    puts cmd if debug?
    if r = system(cmd)
      puts "%-#{max}s  [PASS]" % [s]
    else
      puts "%-#{max}s  [FAIL]" % [s]
      list << s #:load
    end
  end

  puts "  #{list.size} Load Failures"

  if verbose?
    unless list.empty?
      puts "\n-- Load Failures --\n"
      list.each do |f|
        print "* "
        system "ruby -I#{libpath} #{f} 2>&1"
        #puts
      end
      puts
    end
  end
end

#check_syntax(options = nil) ⇒ Object

Verify syntax of ruby scripts.

This takes one option :scripts which is a glob or list of globs of the scripts to check. By default this is all scripts in the libpath(s).



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/reap/project/check.rb', line 10

def check_syntax(options=nil)
  options = configure_options(options, 'check-syntax', 'check')

  libpath = options['loadpath'] || .loadpath
  exclude = options['exclude']

  #libpath = libpath.split(/[:;]/) unless Array===libpath
  libpath = list_option(libpath)
  exclude = list_option(exclude)

  files   = multiglob_r(*libpath) - multiglob_r(exclude)
  files   = files.select{ |f| File.extname(f) == '.rb' }
  max     = files.collect{ |f| f.size }.max
  list    = []

  files.each do |s|
    next unless File.file?(s)
    #if not system "ruby -c -Ibin:lib:test #{s} &> /dev/null" then
    r = system "ruby -c -I#{libpath} #{s} > /dev/null 2>&1"
    if r
      puts("%-#{max}s  [PASS]" % [s]) #if verbose?
    else
      puts("%-#{max}s  [FAIL]" % [s]) #if verbose?
      list << s #:syntax
    end
  end

  puts "  #{list.size} Syntax Errors"

  if verbose?
    unless list.empty?
      puts "\n-- Syntax Errors --\n"
      list.each do |f|
        print "* "
        system "ruby -c -I#{libpath} #{f} 2>&1"
        #puts
      end
      puts
    end
  end
end

#clean(options = nil) ⇒ Object

Clean scrap products. All directory paths and or file globs listed under the clean configuration entry, can be removed via this method. By default all files ending with “~” or .back are removed. To specifcy an alternate provide a list of files and/or glibs under remove: sub-entry. You can also provide an exclude: sub-entry to isolate files not to be removed. For example, on might do:

clean:
  remove [ '**/*~', '**/*.bak', '.config' ]

Clean is run as a prerequiste to #release via #prepare.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/reap/project/clean.rb', line 18

def clean(options=nil)
  options = configure_options(options, 'clean')

  remove  = options['remove']
  exclude = options['exclude']

  remove  = list_option(remove)
  exclude = list_option(exclude)

  files   = multiglob_r(*remove) - multiglob_r(exclude)

  make_clean if compiles?

  return if files.empty?

  puts files.join("\n")

  if verbose?
    ans = ask("Remove files?", "yN").downcase
    return unless ans == 'y' or ans == 'yes'
  end

  files.each do |f|
    rm(f) if File.exist?(f)
  end
end

#clobber(options = nil) ⇒ Object

Run all clobber methods. This method literally looks for all other methods starting with the phrase “clobber_” and calls them, then runs #clean as well.



49
50
51
52
53
54
55
# File 'lib/reap/project/clean.rb', line 49

def clobber(options=nil)
  clean
  clobber_methods = methods.select{ |m| m.to_s =~ /^clobber_/ }
  clobber_methods.each do |m|
    send(m)
  end
end

#clobber_packages(options = nil) ⇒ Object

Remove packages.



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/reap/project/package.rb', line 25

def clobber_packages(options=nil)
  options = configure_options(options, 'package')

  store = 'pkg'

  packages = glob(File.join(store, '*'))

  packages.each do |path|
    rm_r(path)
    puts "Removed #{path}" unless dryrun?
  end
end

#clobber_rdoc(options = nil) ⇒ Object

Remove rdocs products.



79
80
81
82
83
84
85
86
87
88
# File 'lib/reap/project/rdoc.rb', line 79

def clobber_rdoc(options=nil)
  options = configure_options(options, 'doc-rdoc', 'rdoc')

  output = options['output']

  if File.directory?(output)
    rm_r(output)
    puts "Removed #{output}" unless dryrun?
  end
end

#clobber_ridoc(options = nil) ⇒ Object

Remove ri products.



159
160
161
162
163
164
165
166
167
168
# File 'lib/reap/project/rdoc.rb', line 159

def clobber_ridoc(options=nil)
  options = configure_options(options, 'doc-ri', 'ri')

  output = options['output']

  if File.directory?(output)
    rm_r(output)
    puts "Removed #{output}" unless dryrun?
  end
end

#compiles?Boolean

Check to see if this project has extensions that need to be compiled.

Returns:

  • (Boolean)


17
18
19
# File 'lib/reap/project/make.rb', line 17

def compiles?
  !extensions.empty?
end

#current_platformObject

Current platform.



19
20
21
# File 'lib/reap/project/package.rb', line 19

def current_platform
  Platform.local.to_s
end

#debug=(x) ⇒ Object



92
# File 'lib/reap/project.rb', line 92

def debug=(x)   ; runmodes.debug   = x ; end

#debug?Boolean

Returns:

  • (Boolean)


86
# File 'lib/reap/project.rb', line 86

def debug?      ; runmodes.debug?   ; end

#defaultsObject

Task defaults.



66
# File 'lib/reap/project.rb', line 66

def defaults ; @defaults ; end

#display_locationObject

Display the project’s root location.



122
123
124
# File 'lib/reap/project.rb', line 122

def display_location
  puts "[#{location}]"
end

#document(options) ⇒ Object

Generate documentation. At this time it simply means generating rdocs.



8
9
10
11
# File 'lib/reap/project/rdoc.rb', line 8

def document(options)
  rdoc(options)
  ridoc(options)
end

#dryrun=(x) ⇒ Object Also known as: noharm=



88
# File 'lib/reap/project.rb', line 88

def dryrun=(x)  ; runmodes.dryrun  = x ; end

#dryrun?Boolean Also known as: noharm?

alias_method :init_options, :options # TODO: Improve me! (see stamp.rb)

Returns:

  • (Boolean)


82
# File 'lib/reap/project.rb', line 82

def dryrun?     ; runmodes.dryrun?  ; end

#extensionsObject

Extension directories. Often this will simply be ‘ext’. but sometimes more then one extension is needed and are kept in separate directories. This works by looking for ext/*/.c files, where ever they are is considered an extension directory.



26
27
28
# File 'lib/reap/project/make.rb', line 26

def extensions
  @extensions ||= Dir['ext/**/*.c'].collect{ |file| File.dirname(file) }.uniq
end

#force=(x) ⇒ Object



90
# File 'lib/reap/project.rb', line 90

def force=(x)   ; runmodes.force   = x ; end

#force?Boolean

Returns:

  • (Boolean)


84
# File 'lib/reap/project.rb', line 84

def force?      ; runmodes.force?   ; end

#gem_clobber(options = nil) ⇒ Object

Remove gem package products.



9
10
11
12
13
14
15
16
17
# File 'lib/reap/project/gem.rb', line 9

def gem_clobber(options=nil)
  store = "pkg"

  packages = glob(File.join(store, '*.gem'))

  packages.each do |path|
    File.directory?(path) ? rm_r(path) : rm(path)
  end
end

#gem_install(options = nil) ⇒ Object

Install gem package, creating the package if not already created.

TODO: Endure that we even need a gem package using #out_of_date?



31
32
33
34
# File 'lib/reap/project/gem.rb', line 31

def gem_install(options=nil)
  file = gem_package(options)
  sh "gem install #{file}"
end

#gem_package(options = nil) ⇒ Object

Create a Gem package.

TODO: Should this use staging too, like zip/tgz?



23
24
25
# File 'lib/reap/project/gem.rb', line 23

def gem_package(options=nil)
  return package_gem(options=nil)
end

#gem_uninstallObject

Uninstall gem package.

TODO: Sepcify version?



40
41
42
43
44
# File 'lib/reap/project/gem.rb', line 40

def gem_uninstall
  i = .package_name.rindex('-')
  name, version = .package_name[0...i], .package_name[i+1..-1]
  sh "gem uninstall #{name} -v #{version}"
end

#htmlObject

Create web index.html from README. (Not yet used)



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/reap/project/html.rb', line 7

def html
  status_title "Creating HTML documents"

  require 'rdoc/markup/simple_markup'
  require 'rdoc/markup/simple_markup/to_html'

  options = configure_options(options, 'html')

  output = options['output']
  files  = options['files']
  style  = options['css']

  output ||= 'doc'
  files  ||= '[A-Z]*'
  style  ||= Dir.glob('*.css').first

  files = Dir.glob(files)

  s = SM::SimpleMarkup.new
  h = SM::ToHtml.new

  files.each do |file|
    unless File.exist?(file)
      puts "Warning: file does not exist -- #{file}"
    end
  end

  mkdir_p(output) unless dryrun?

  files.each do |file|
    name = file.downcase.chomp('.txt')
    if /^readme/ =~ name
      name = "index"
    end
    path = File.join(output, name + '.html')

    next unless out_of_date?(path, file)

    title = "#{package.title} #{name.upcase}"

    input  = File.read(file)
    output = s.convert(input, h)  # FIX

    text = ''
    text << %{<html>}
    text << %{<head>}
    text << %{  <title>#{title}<title>}
    text << %{  <link rel="stylesheet" TYPE="text/css" HREF="#{style}">} if style
    text << %{</head>}
    text << %{<body>}
    text << output
    text << %{</body>}
    text << %{</html>}

    write(path, text)

    puts "Created #{path}"
  end
end

#introspect(options) ⇒ Object

Query infromation about reap settings and/or the current project.

NOTE: This would dhave been naed #inspect but for the built-in method.



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

def introspect(options)
  args = options['arguments']
  if args
    args.each do |field|
      case field
      when 'defaults'
        y defaults
      when 'settings'
        y settings
      when 'metadata'
        y 
      else
        puts .send(field)
      end
    end
  else
    puts
    puts "#{.title} #{.version}"
    puts .brief
    puts .homepage
    puts
    puts .description
    puts
    puts .copyright
    puts
  end
end

#invoke(command, *args) ⇒ Object

Invoke a tool.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/reap/project.rb', line 99

def invoke(command, *args)
  begin
    #display_location
    meth = method(command)
    chdir_to_project do
      case meth.arity
      when 0
        meth.call
      else
        meth.call(*args)
      end
    end
  rescue LoadError => e
    if trace?
      raise e
    else
      abort e.message.capitalize + '.'
    end
  end
end

#locationObject

Location of project.



58
# File 'lib/reap/project.rb', line 58

def location ; @location ; end

#log(*args) ⇒ Object

Update all logs.



9
10
11
12
# File 'lib/reap/project/log.rb', line 9

def log(*args)
  log_changes(*args)
  log_notes(*args)
end

#log_changes(options = nil) ⇒ Object

Generate ChangeLog. This routes to the source control manager library.



17
18
19
20
# File 'lib/reap/project/log.rb', line 17

def log_changes(options=nil)
  options = configure_options(options, 'log-changes', 'log')
  scm_log(options)
end

#log_notes(options = {}) ⇒ Object

Collect embedded notes.

This task scans source code for developer notes and writes to well organized files. This tool can lookup and list TODO, FIXME and other types of labeled comments from source code.

files    Glob(s) of files to search.
labels   Labels to search for. Defaults to [ 'TODO', 'FIXME' ].
output   Output directory. Defaults to log/.

TODO: Remove format field, and ultimately use XML as primary format?



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/reap/project/log.rb', line 36

def log_notes(options={})
  options = configure_options(options, 'log-notes', 'log')

  loadpath = options['loadpath'] || .loadpath
  labels   = options['labels']   || ['TODO', 'FIXME', 'OPTIMIZE']
  output   = options['output']   || 'log'

  loadpath = list_option(loadpath)

  labels = labels.split(',') if String === labels
  labels = [labels].flatten.compact

  output.chomp!('/')

  records, counts = log_notes_extract(labels, loadpath)
  notes = log_notes_format(labels, records, (format=nil))

  if records.empty?
    puts "No #{labels.join(', ')} notes."
  else
    files_saved = log_notes_save(output, notes, labels)
    files_saved.each do |file|
      puts "Updated #{file}"
    end
    puts counts.collect{|l,n| "#{n} #{l}s"}.join(', ')
  end
end

#makeObject

Compile extensions.



32
33
34
35
# File 'lib/reap/project/make.rb', line 32

def make
  make_config
  make_target
end

#make_cleanObject

Remove enouhg compile products for a clean compile.



46
47
48
# File 'lib/reap/project/make.rb', line 46

def make_clean
  make_target 'clean'
end

#make_configObject

Create Makefile(s).



64
65
66
67
68
69
70
71
# File 'lib/reap/project/make.rb', line 64

def make_config
  extensions.each do |directory|
    next if File.exist?(File.join(directory, 'Makefile'))
    cd(directory) do
      sh "ruby extconf.rb"
    end
  end  
end

#make_distcleanObject Also known as: clobber_make

Remove all compile products.



52
53
54
55
56
57
58
# File 'lib/reap/project/make.rb', line 52

def make_distclean
  make_target 'distclean'
  extensions.each do |directory|
    makefile = File.join(directory, 'Makefile')
    rm(makefile) if File.exist?(makefile)
  end
end

#make_staticObject

Compile static.



39
40
41
42
# File 'lib/reap/project/make.rb', line 39

def make_static
  make_config
  make_target 'static'
end

#manifest_fileObject

Project manifest file.



181
182
183
# File 'lib/reap/project.rb', line 181

def manifest_file 
  Dir.glob('Manifest{,.txt}', File::FNM_CASEFOLD).first
end

#metadataObject

Project metadata.



62
# File 'lib/reap/project.rb', line 62

def  ; @metadata  ; end

#optionsObject

Common options.



78
# File 'lib/reap/project.rb', line 78

def options  ; @options  ; end

#package(options = nil) ⇒ Object

General pack command.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/reap/project/package.rb', line 54

def package(options=nil)
  packopts = configure_options(options, 'package')

  formats = packopts['formats'] || ['gem', 'tgz']
  formats = [formats].flatten

  prepare(options)

  puts unless dryrun?

  formats.each do |format|
    send("package_#{format}", options)
    #puts unless dryrun?
  end
end

#package_gem(options = nil) ⇒ Object

Routes to #gem_package.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/reap/project/package.rb', line 72

def package_gem(options=nil)
  begin
    require 'rubygems/specification'
    Gem::manage_gems
  rescue LoadError
    #raise LoadError, "RubyGems is not installed?"
  end

  options = configure_options(options, 'package-gem', 'package')
  extension = '.gem'

  platform  = options.delete('platform') || 'none'

  platforms = []
  platforms << nil if platform != 'only'
  platforms << 'current' if platform != 'none'

  platforms.each do |pl|
    options['platform'] = pl
    fname,  = package_prepare_stage(extension, options)
    next unless fname # if already built
    stage = File.join(package_store, fname)
    if dryrun?
      status "gem build #{stage}"
    else
      file = nil
      cd(stage) do
        #status "vi #{metadata.name}.gemspec"
        builder = ::Gem::Builder.new(gemspec())
        status "gem build #{stage}"
        unless dryrun?
          file = builder.build
          file = File.expand_path(file)
        end
      end
      # transfer gem package to package store
      mkdir_p(package_store)
      destination = File.join(package_store, File.basename(file))
      mv(file, package_store) unless File.expand_path(file) == File.expand_path(destination)
      puts
    end
  end
end

#package_storeObject

Returns the package storage directory (now constant ‘pkg’).



13
14
15
# File 'lib/reap/project/package.rb', line 13

def package_store
  PACKAGE_STORE
end

#package_tgz(options = nil) ⇒ Object

Create a Tar’d Gzip package.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/reap/project/package.rb', line 126

def package_tgz(options=nil)
  options = configure_options(options, 'package-tgz', 'package')

  platform  = options.delete('platform') || 'none'
  extension = '.tgz'

  platforms = []
  platforms << nil if platform != 'only'
  platforms << current_platform if platform != 'none'

  platforms.each do |pl|
    options['platform'] = pl
    fname,  = package_prepare_stage(extension, options)
    next unless fname  # if already built
    if dryrun?
      status "tar -cxf #{fname}.tgz"
    else
      file = nil
      cd(package_store) do
        file = compress(:tgz, fname)
      end
      transfer(file, package_store)
      report_package_built(file)
    end
  end
end

#package_zip(options = nil) ⇒ Object

Create Zip package.



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
# File 'lib/reap/project/package.rb', line 155

def package_zip(options=nil)
  options = configure_options(options, 'package-zip', 'package')

  platform  = options.delete('platform') || 'none'
  extension = '.zip'

  platforms = []
  platforms << nil if platform != 'only'
  platforms << current_platform if platform != 'none'

  platforms.each do |pl|
    options['platform'] = pl
    fname,  = package_prepare_stage(extension, options)
    next unless fname  # if already built
    if dryrun?
      status "zip -r #{fname}.zip ."
    else
      file = nil
      cd(package_store) do
        file = compress(:zip, fname)
      end
      transfer(file, package_store)
      report_package_built(file)
    end
  end
end

#prepare(options) ⇒ Object

Prepare for packaging (clean, distclean, version stamp).

TODO: When we add support for binary packages distclean

should not be done for them.


43
44
45
46
47
48
49
50
# File 'lib/reap/project/package.rb', line 43

def prepare(options)
  @prepared ||= (
    clean
    make_distclean if compiles?
    stamp(options)
    true
  )
end

#publish(options = nil) ⇒ Object

Publish website to rubyforge.

This task publishes the source dir (deafult ‘doc’) to a rubyforge website.

Uses RSync to upload files to webserver.

TODO: Add FTP/SFTP support.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/reap/project/publish.rb', line 14

def publish(options=nil)
  options = configure_options(options, 'publish', 'rubyforge')

  project  = options['project']  || .project
  webdir   = options['webdir']
  source   = options['source']
  username = options['username'] || ENV['RUBYFORGE_USERNAME']
  clear    = options['clear']
  protect  = options['protect']
  exclude  = options['exclude']

  source   ||= "doc"
  username ||= ENV['RUBYFORGE_USERNAME']

  if clear
    protect   = protect().to_a
    exclude   = exclude().to_a
  else
    protect   = %w{usage statcvs statsvn robot.txt wiki} + [protect].flatten
    exclude   = %w{.svn} + [exclude].flatten
  end

  abort "No project name." unless project
  abort "No username." unless username

  if webdir and webdir != '.'
    destination = File.join(project, webdir)
  else
    destination = project
  end

  dir = source.chomp('/') + '/'
  url = "#{username}@rubyforge.org:/var/www/gforge-projects/#{destination}"

  op = ['-rLvz', '--delete-after']  # maybe -p ?

  # Using commandline filter options didn't seem
  # to work, so I opted for creating an .rsync_filter file for
  # all cases.

  unless protect.empty? && exclude.empty?
    rsync_file = File.join(source,'.rsync-filter')
    unless file?(rsync_file)
      File.open(rsync_file, 'w') do |f|
        exclude.each{|e| f << "- #{e}\n"}
        protect.each{|e| f << "P #{e}\n"}
      end
    end
    op << "--filter='dir-merge #{rsync_file}'"
  end

  args = op + [dir, url]

  sh "rsync #{args.to_params}"
end

#rdoc(options = nil) ⇒ Object

Generate rdocs.

Generate Rdoc documentation. Settings are the same as the rdoc command’s option, with two exceptions: inline for inline-source and output for op.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/reap/project/rdoc.rb', line 20

def rdoc(options=nil)
  options = configure_options(options, 'doc-rdoc', 'rdoc', 'doc')
  #options = DEFAULT['rdoc'].merge(options)

  options['title'] ||= .title

  targets = options.delete('targets') || {'' => options}
  output  = options['output']
  adfile  = options['adfile']

  adfile = [adfile].flatten.find do |f|
    File.exist?(f)
  end

  targets.each do |subdir, target|
    target = options.merge(target)

    target_solo = target['solo']
    target_main = Dir.glob(target['main'].to_s, File::FNM_CASEFOLD).first

    #target_main   = File.expand_path(target_main) if target_main
    #target_output = File.expand_path(File.join(output, subdir))
    target_output = File.join(output, subdir)

    cmdopts = {}
    #cmdopts['op']            = target_output
    cmdopts['main']          = target_main if target_main
    cmdopts['template']      = target['template'] || ENV['RDOC_TEMPLATE'] || 'html'
    #cmdopts['merge']         = target['merge']
    cmdopts['inline-source'] = target['inline']
    cmdopts['exclude']       = list_option(target['exclude'])

    files = list_option(target['include'])
    files = files.collect{ |g| Dir[g] }.flatten  # Need this to remove unwanted toplevel files.
    files = files - ['Rakefile', 'Rakefile.rb']  # b/c rdoc's exlcude options doesn't work well.
    files = files - [manifest_file].compact

    #folder = target['chdir'] || '.'

    #puts "cd #{folder}" if dryrun?  # TODO: Shouldn't chdir do this automatically?
    #chdir(folder) do
      if target_solo
        input_files = files.collect{ |i| multiglob_r(i) }.flatten.reject{ |f| File.directory?(f) }
        input_files.each do |input_file|
          out = File.join(target_output, File.basename(input_file).chomp(File.extname(input_file)))
          rdoc_target(out, input_file, cmdopts)
          rdoc_insert_ads(out, adfile)
        end
      else
        input_files = files.collect{ |i| dir?(i) ? File.join(i,'**','*') : i }
        rdoc_target(target_output, input_files, cmdopts)
        rdoc_insert_ads(target_output, adfile)
      end
    #end
  end
end

#release(options = {}) ⇒ Object

Release packages (to rubyforge). This generates the packages, and then distributes them to the file server.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/reap/project/release.rb', line 11

def release(options={})
  package(options)


  release_options = configure_options(options, 'release')

  store   = 'pkg'
  version = .version

  release_options['version'] = version
  release_options['store']   = store

  changelog = release_options['changelog'] #|| DEFAULT['release']['changelog'] || DEFAULT['rubyforge']['changelog']
  notelog   = release_options['notelog']   #|| DEFAULT['release']['notelog']   || DEFAULT['rubyforge']['notelog']

  changelog = Dir.glob(changelog.to_s, File::FNM_CASEFOLD).first
  notelog   = Dir.glob(notelog.to_s, File::FNM_CASEFOLD).first

  release_options['changelog'] = changelog if changelog && File.exist?(changelog)
  release_options['notelog']   = notelog   if notelog && File.exist?(notelog)

  files   = release_options['files'] || []

  if files.empty?
    files = Dir[File.join(store, '*')].select do |file|
      /#{version}[.]/ =~ file
    end
    release_options['files'] = files
    #files = Dir.glob(File.join(store,"#{name}-#{version}*"))
  end


  actions = []
  select  = options['hosts']

  hosts(select).each do |host|
    if host.respond_to?(:release)
      # Not going to do dryrun in Rubyforge class b/c it still requires logging in.
      if dryrun?
        puts "release: #{} #{host.basename.downcase}"
      else
        if host.release_confirm?(release_options)
          actions << lambda{ host.release(release_options) }
        end
      end
    end
  end

  actions.each{ |a| a.call }
end

#ridoc(options = nil) ⇒ Object

generate local ri docs

Generate RI documentation. This utilizes rdoc to produce the appropriate files.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/reap/project/rdoc.rb', line 131

def ridoc(options=nil)
  options = configure_options(options, 'doc-ri', 'ri')
  #options = DEFAULT['ri'].merge(options)

  cmdopts = {}
  cmdopts['op']            = options['output']
  cmdopts['exclude']       = options['exclude']

  output = options['output']
  files  = options['include'] || .loadpath #['lib', '[A-Z]*']

  input = files #.collect do |i|
  #  dir?(i) ? File.join(i,'**','*') : i
  #end

  if out_of_date?(output, *input) or force?
    rm_r(output) if exist?(output) and safe?(output)  # remove old ridocs

    #input = input.collect{ |i| glob(i) }.flatten
    vector = [input, cmdopts]
    sh "rdoc --ri -M -a #{vector.to_console}"
  else
    puts "RI Docs are current."
  end
end

#rollout(options = {}) ⇒ Object

A complete rollout. This will prepare (clean, stamp and package), then document, publish and release, tag and announce. It will do under direction. You can use the –force option to bypass this and have evey action taken automatically.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/reap/project/release.rb', line 67

def rollout(options={})
  if force?
    doc, pub, ann, tag = true, true, true, true
  else
    doc = ask("Generate doumentation?", "yN").downcase =~ /^(y|yes)$/i
    pub = ask("Publish website?      ", "yN") =~ /^(y|yes)$/i
    tag = scm? ? (ask("Tag current version?  ", "yN") =~ /^(y|yes)$/i) : false      
    ann = ask("Announce release?     ", "yN") =~ /^(y|yes)$/i
    puts
  end

  document(options) if doc
  publish(options)  if pub

  #package(options)
  release(options)
  scm_tag(options)  if tag
  announce(options) if ann
end

#runmodesObject

Run modes.



74
# File 'lib/reap/project.rb', line 74

def runmodes ; @runmodes ; end

#scaffold(options) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/reap/project/scaffold.rb', line 7

def scaffold(options)
  requests = options['arguments']

  if requests
    requests.each do |f|
      case f
      #when /^(meta\/)?project(info)?(.yaml|.yml)?$/i
      #  scaffold_projectfile(f)
      when /^rake(file)?$/i
        scaffold_rakefile(f)
      when /^setup[.]rb$/
        scaffold_setup_rb(f)
      when /^(task|script)s?\//i
        scaffold_task(f)
      when /^(task|script)(s)?$/
        scaffold_tasks(f)
      else
        raise "Unknown scaffolding."
      end
    end
  else
    scaffold_skeleton(options=nil)
  end
end

#scaffold_rakefile(fname) ⇒ Object

Add tasks in Rakefile form to project.



44
45
46
47
# File 'lib/reap/project/scaffold.rb', line 44

def scaffold_rakefile(fname)
  from = File.join(data_dir, 'buildset', 'rake', 'Rakefile')
  cp(from, fname) unless File.exist?(fname)
end

#scaffold_setup_rb(fname) ⇒ Object



51
52
53
54
# File 'lib/reap/project/scaffold.rb', line 51

def scaffold_setup_rb(fname)
  from = File.join(data_dir, 'buidset', 'rake', 'setup.rb')
  cp(from, fname) unless File.exist?(fname)
end

#scaffold_skeleton(options = nil) ⇒ Object

Create a project skeleton.

TODO: Improve scaffolding. Make more intelligent.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
127
128
129
130
131
132
# File 'lib/reap/project/scaffold.rb', line 74

def scaffold_skeleton(options=nil)
  options = (options || {}).rekey(:to_s)

  files = glob('**/*') - glob('meta/**/*') - ['.reap', 'meta']

  unless files.empty?
    ans = ask("Directory isn't empty. Are you sure you want to add scaffolding?", 'yN')
    case ans.downcase
    when 'y', 'yes'
    else
      abort "Scaffolding aborted."
    end
  end

  #      if options['svn']
  #        if glob('**/*').empty?
  #          mkdir_p('trunk')
  #          mkdir_p('branches')
  #          mkdir_p('tags')
  #          chdir('trunk')
  #        else
  #          abort "Can't create a svn repo unless directory is empty."
  #        end
  #      end

  paths = nil
  dir   = File.join(data_dir, 'base')
  chdir(dir){ paths = Dir['**/*'] }

  dirs  = paths.select{ |f| File.directory?(File.join(dir, f)) }
  files = (paths - dirs).reject{ |f| /[.]svn/ =~ f }

  dirs.each do |dname|
    if File.exist?(dname) and !File.directory?(dname)
      abort "Directory to be created clashes with a prexistent file -- #{dname}"
    end
  end

  dirs.each do |dname|
    mkdir_p(dname) unless File.exist?(dname)
  end

  files.each do |fname|
    next if File.exist?(fname)
    file = File.join(dir, fname)
    if File.extname(file) == '.erb'
      erb = ERB.new(File.read(file))
      txt = erb.result(.get_binding)
      File.open(fname.chomp('.erb'), 'w'){ |f| f << txt }
    else
      cp(file, fname)
    end
  end

  # A little extra love.

  dir = File.join('lib',.name)
  mkdir_p(dir) unless File.exist?(dir)
end

#scaffold_task(fname) ⇒ Object

Add a user tasks to the project.



58
59
60
61
# File 'lib/reap/project/scaffold.rb', line 58

def scaffold_task(fname)
  from = File.join(data_dir, 'buildset', 'tasks', 'task', File.basename(fname))
  cp(from, fname) unless File.exist?(fname)
end

#scaffold_tasks(fname) ⇒ Object

Add all user tasks to the project.



65
66
67
68
# File 'lib/reap/project/scaffold.rb', line 65

def scaffold_tasks(fname)
  dir = File.join(data_dir, 'buildset', 'tasks', 'task')
  cp_r(dir, fname)
end

#scm?Boolean

Determine if project is under version control.

TODO: scm? may need to be made more robsut.

Returns:

  • (Boolean)


11
12
13
# File 'lib/reap/project/scm.rb', line 11

def scm?
  Systems::System.detect
end

#scm_branch(options = nil) ⇒ Object

Branch current version of project. This method routes to the appropriate method for the project’s source control manager.

message    Optional commit message. This is intended
           for commandline usage. (Use -m for shorthand).

TODO: How should metadata.repository come into play here?



73
74
75
76
# File 'lib/reap/project/scm.rb', line 73

def scm_branch(options=nil)
  options = configure_options(options, 'scm-branch', 'scm')
  scm.branch(options)
end

#scm_log(options = {}) ⇒ Object

Generate ChangeLog. This method routes to the appropriate method for the project’s source control manager.

change     File path to store rdoc formated changelog. Default is 'log/changelog.txt'.
xmlchange  File path to store XML formated changelog. Default is 'doc/log/changelog.xml'.

Set either to false to supress creation.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/reap/project/scm.rb', line 24

def scm_log(options={})
  #create_txtlog = (options['txtlog'] != false)
  #create_xmllog = (options['xmllog'] != false)

  xmlout = options['xmloutput'] || 'site/log'  # TODO: How to set site/?

  #if create_txtlog
    txtlog = apply_naming_policy('changelog', 'rdoc')
    txtlog = File.join('log', txtlog)
  #end

  #if create_xmllog
    xmllog = apply_naming_policy('changelog', 'xml')
    xmllog = File.join(xmlout, xmllog)
  #end

  #txtlog = File.join('lib', txtlog) unless txtlog.include?('/')
  #xmllog = File.join(xmldir, xmllog) unless xmllog.include?('/')

  txtlog ||= options['txtlog']
  xmllog ||= options['xmllog']

  scm.log(txtlog)
  scm.log_xml(xmllog) if xmllog
end

#scm_tag(options = nil) ⇒ Object

Tag current versoin of project. This method routes to the appropriate method for the project’s source control manager.

message       Optional commit message. This is intended for commandline
              usage. (Use -m for shorthand).

TODO: How should metadata.repository come into play here?



59
60
61
62
# File 'lib/reap/project/scm.rb', line 59

def scm_tag(options=nil)
  options = configure_options(options, 'scm-tag', 'scm')
  scm.tag(options)
end

#settingsObject

Task user settings.



70
# File 'lib/reap/project.rb', line 70

def settings ; @settings ; end

#site_installObject

Install via project’s install/setup script.

TODO: Remove special reap options from command line.



9
10
11
12
13
14
15
16
# File 'lib/reap/project/site.rb', line 9

def site_install
  script = glob("setup.rb,install.rb,task/setup,task/install").first
  if script
    sh "#{script} #{ARGV.join(' ')}"
  else
    abort "Project needs an install/setup script."
  end
end

#site_uninstallObject

TODO: Create uninstall task.



20
21
22
# File 'lib/reap/project/site.rb', line 20

def site_uninstall
  abort "Not yet implemented."
end

#spec(options = nil) ⇒ Object

Run all specs with basic output.

Options:

specs     File glob(s) of spec files. Defaults to ['spec/**/*_spec.rb', 'spec/**/spec_*.rb'].
loadpath  Paths to add $LOAD_PATH. Defaults to ['lib'].
live      Ignore loadpath, use installed libraries instead. Default is false.
require   Lib(s) to require before excuting specifications.
warning   Whether to show warnings or not. Default is false.
command   Spec command to use. Defaults to 'spec'.
format    Format of RSpec output.
rubyopt   Additional options to pass to the ruby command.
specopt   Additional commandline options for spec command.

– RCOV suppot?

ruby [ruby_opts] -Ilib -S rcov [rcov_opts] bin/spec -- examples [spec_opts]

++



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/reap/project/spec.rb', line 29

def spec(options=nil)
  options = configure_options(options, 'spec')

  #specs   = options['specs']    || DEFAULT['spec']['specs']
  #reqs    = options['require']  || DEFAULT['spec']['require']
  #warning = options['warning']  || DEFAULT['spec']['false']
  #command = options['command']  || DEFAULT['spec']['command']

  specs    = options['specs']
  warning  = options['warning']
  command  = options['command']  || 'spec'
  loadpath = options['loadpath'] || .loadpath
  format   = options['format']
  rubyopt  = options['rubyopt']
  specopt  = options['specopt']
  live     = options['live']

  specs    = list_option(specs)
  loadpath = list_option(loadpath)
  requires = list_option(requires)

  files = multiglob(*specs)

  if files.empty?
    puts "No specifications."
  else
    #RakeFileUtils.verbose(verbose) do
      # ruby [ruby_opts] -Ilib bin/spec examples [spec_opts]
      cmd = "ruby"
      cmd << " -w" if warning
      cmd << %[ -I"#{loadpath.join(':')}"] unless loadpath.empty?
      cmd << %[ -r"#{requires.join(':')}"] unless requires.empty?
      cmd << rubyopt #.join(" ")
      cmd << " "
      #rb_opts << "-S rcov" if rcov
      #cmd << rcov_option_list
      #cmd << %[ -o "#{rcov_dir}" ] if rcov
      cmd << command
      cmd << " "
      #cmd << "-- " if rcov
      cmd << files.join(' ')
      cmd << " "
      cmd << specopt #.join(' ')
      cmd << " --format #{format}" if format

      puts cmd if verbose?
      unless system(cmd)
        STDERR.puts failure_message if failure_message
        raise("Command #{cmd} failed") if fail_on_error
      end
    #end
  end
end

#spec_doc(options = nil) ⇒ Object

Run all specs with text output



85
86
87
88
89
# File 'lib/reap/project/spec.rb', line 85

def spec_doc(options=nil)
  options ||= {}
  options['format'] = 'specdoc'
  spec(options)
end

#stamp(options = {}) ⇒ Object

Update VERSION stamp file.

This file is either called VERSION, or meta/version (case-insensitive and with optional .txt extension).

The format of the files is:

x.y.z status (date)

For exmaple:

1.2.4 alpha (2008-10-10)

On the command line:

--major    will bump the major number
--minor    will bump the minor number
--tiny     will bump the tiny  number
--teeny    will bump the teeny number

One can alternately specify the entire version:

--version=x.y.z

As well as status:

--status=(alpha, beta, rc1, rc2, ...)

TODO: Should we also update a lib/version.rb file? TODO: Considerding createing a standard status marker (a=alpha, b=beta, r=release candidate)

So a version would read, eg. 1.2.4a, or with status number, eg. 1.2.4r1).


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/reap/project/version.rb', line 37

def stamp(options={})
  version = options['version']
  status  = options['status']

  bumps = [ options['major'], options['minor'], options['tiny'], options['teeny'] ]

  bump  = bumps.any?{|x|x}

  abort "Specify bumps or version, not both." if bump and version

  #options = configure_options(options, 'stamp')

  version = version || .version || '0.0.0'
  status  = status  || .status  || '0.0.0'

  if bump
    points = version.to_s.split(/[.]/).collect do |x|
      x.to_i
    end

    if options['major']
      points[0] += 1
      points[1..-1] = *([0] * points[1..-1].size)
    elsif options['minor']
      points[1] += 1
      points[2..-1] = *([0] * points[2..-1].size)
    elsif options['tiny']
      points[2] += 1
      points[3..-1] = *([0] * points[3..-1].size)
    elsif options['teeny']
      points[3] += 1
    end

    version = points.join('.') #.chomp('.0')
  else
    abort "Invalid version -- #{version}" unless String===version && /^[0-9]/ =~ version
  end

  meta = File.directory?('meta')

  file = glob('{,meta/}version{,.txt}', File::FNM_CASEFOLD).first
  file = (meta ? 'meta/version' : 'VERSION') unless file

  text = "#{version} #{status} (#{Time.now.strftime('%Y-%m-%d')})"

  if File.exist?(file)
    old_text = File.read(file).strip
    old_version, old_status, old_date = *old_text.split(/\s/)
    if old_version == "#{version}" && old_status == status
      puts old_text
      return
    end
  end

  if dryrun?
    puts "echo '#{text}' > #{file}"
  else
    write(file, text)
    puts text
    puts "#{file} updated."

    .version  = version
    .status   = status
    .released = Time.now
  end

  # TODO: Stamp .roll if roll file exists.
  # should we read current .roll file and use as defaults?
  if File.exist?('.roll')
    str = []
    str << "name    = #{.name}"
    str << "version = #{.version}"
    str << "status  = #{.status}"
    str << "date    = #{.date}"
    str << "default = #{.default}"
    str << "libpath = #{.libpath}"
    # File.open('.roll','w'){ |f| f << str.join("\n") }
  end
end

#stats(options = nil) ⇒ Object

Simple code count analysis.

Scan source code counting files, lines of code and comments and presents a report of it’s findings.

loadpath   Path to include in analysis. The default
           is the project's loadpath.

exclude    File globs to exclude from analysis. Default
           is 'ext' b/c this does not yet support C analysis.

TODO: Add C support for ext/.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/reap/project/stats.rb', line 22

def stats(options=nil)
  options = configure_options(options, 'stats')

  loadpath = options['loadpath'] || .loadpath
  exclude  = options['exclude']  || ['ext']

  loadpath = list_option(loadpath)
  exclude  = list_option(exclude)

  files = multiglob_r(*loadpath) - multiglob_r(*exclude)

  #() #.inject([]){ |memo, find| memo.concat(glob(find)); memo }
  #Dir.multiglob_with_default(DEFAULT_STATS_FILES)

  fc, l, c, r, t, s = *line_count(*files)

  fct, lt, ct, rt, tt, st = *([0]*6)
  if File.directory?('test')
    fct, lt, ct, rt, tt, st = *line_count('test/**/*')
    t = lt if lt > 0
  end

  rat = lambda do |d,n|
    if d > n and n != 0
      "%.1f" % [ d.to_f / n ]
    elsif n > d and d != 0
      "-" #"%.1f:1" % [ n.to_f / d ]
    elsif d == 0 or n == 0
      "-"
    else
      "1.0"
    end
  end

  per = lambda do |n,d|
    if d != 0
      (((n.to_f / d)*100).to_i).to_s + "%"
    else
      "-"
    end
  end

  max = l.to_s.size + 4

  puts
  #puts "FILES:"
  #puts "  source: #{fc}"
  #puts "  test  : #{fct}"
  #puts "  total : #{fc+fct}"
  #puts
  #puts "LINES:"
  #puts "  code  : %#{max}s   %4s" % [ c.to_s, per[c,l] ]
  #puts "  docs  : %#{max}s   %4s" % [ r.to_s, per[r,l] ]
  #puts "  space : %#{max}s   %4s" % [ s.to_s, per[s,l] ]
  #puts "  test  : %#{max}s   %4s" % [ t.to_s, per[t,l] ]
  #puts "  total : %#{max}s   %4s" % [ l.to_s, per[l,l] ]
  #puts
  #puts "Ratio to 1 :"
  #puts "  code to test : #{rat[c,t]} #{per[c,t]}"

  head = ["Total", "Code", "-%-", "Docs", "-%-", "Blank", "-%-", "Files"]
  prod = [l.to_s, c.to_s, per[c,l], r.to_s, per[r,l], s.to_s, per[s,l], fc]
  test = [lt.to_s, ct.to_s, per[ct,l], rt.to_s, per[rt,l], st.to_s, per[st,l], fct]
  totl = [(l+lt), (c+ct), per[c+ct,l+lt], (r+rt), per[r+rt,l+lt], (s+st), per[s+st,l+lt], (fc+fct)]

  puts "TYPE    %#{max}s %#{max}s %4s %#{max}s %4s %#{max}s %4s %#{max}s" % head
  puts "Source  %#{max}s %#{max}s %4s %#{max}s %4s %#{max}s %4s %#{max}s" % prod
  puts "Test    %#{max}s %#{max}s %4s %#{max}s %4s %#{max}s %4s %#{max}s" % test
  puts "Total   %#{max}s %#{max}s %4s %#{max}s %4s %#{max}s %4s %#{max}s" % totl
  puts
  puts "RATIO     Code    Docs    Blank   Test   Total"
  puts "Code   %7s %7s %7s %7s %7s" % [ rat[c,c], rat[c,r], rat[c,s], rat[c,t], rat[c,l] ]
  puts "Docs   %7s %7s %7s %7s %7s" % [ rat[r,c], rat[r,r], rat[r,s], rat[r,t], rat[r,l] ]
  puts "Blank  %7s %7s %7s %7s %7s" % [ rat[s,c], rat[s,r], rat[s,s], rat[s,t], rat[s,l] ]
  puts "Test   %7s %7s %7s %7s %7s" % [ rat[t,c], rat[t,r], rat[t,s], rat[t,t], rat[t,l] ]
  puts "Total  %7s %7s %7s %7s %7s" % [ rat[l,c], rat[l,r], rat[l,s], rat[l,t], rat[l,l] ]
  puts
end

#test_cross(options = nil) ⇒ Object

Run cross comparison testing.

This tool runs unit tests in pairs to make sure there is cross library compatibility. Each pari is run in a separate interpretor to prevent script clash. This makes for a more robust test facility and prevents potential conflicts between test scripts.

tests     Test files (eg. test/tc_**/*.rb) [test/**/*]
loadpath  Directories to include in load path.
require   List of files to require prior to running tests.
live      Deactive use of local libs and test against install.


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
# File 'lib/reap/project/test.rb', line 197

def test_cross(options=nil)
  options = test_configuration(options)

  tests    = options['tests']
  loadpath = options['loadpath']
  requires = options['requires']
  live     = options['live']
  exclude  = options['exclude']
  log      = options['log'] != false

  files = multiglob_r(*tests) - multiglob_r(exclude)

  return puts("No tests.") if files.empty?

  files = files.select{ |f| File.extname(f) == '.rb' and File.file?(f) }
  width = files.collect{ |f| f.size }.max
  pairs = files.inject([]){ |m, f| files.collect{ |g| m << [f,g] }; m }

  make if compiles?

  cmd   = %[ruby -I#{loadpath.join(':')} -e"load('./%s'); load('%s')"]
  dis   = "%-#{width}s %-#{width}s"

  testruns = pairs.collect do |pair|
    { 'file'    => pair,
      'command' => cmd % pair,
      'display' => dis % pair
    }
  end

  report = test_loop_runner(testruns)

  puts report

  if log && !dryrun?
    logfile = File.join('log', apply_naming_policy('testlog', 'txt'))
    File.open(logfile, 'a') do |f| 
      f << "= Cross Test @ #{Time.now}\n"
      f << report
      f << "\n"
    end
  end
end

#test_load(options = nil) ⇒ Object

Load each test independently to ensure there are no require dependency issues. This is actually a bit redundant as test-solo will also cover these results. So we may deprecate this in the future. This does not generate a test log entry.



92
93
94
95
96
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
127
128
129
130
131
# File 'lib/reap/project/test.rb', line 92

def test_load(options=nil)
  options = test_configuration(options)

  tests    = options['tests']
  loadpath = options['loadpath']
  requires = options['requires']
  live     = options['live']
  exclude  = options['exclude']

  files = multiglob_r(*tests) - multiglob_r(*exclude)

  return puts("No tests.") if files.empty?

  max   = files.collect{ |f| f.size }.max
  list  = []

  files.each do |f|
    next unless File.file?(f)
    if r = system("ruby -I#{loadpath.join(':')} #{f} > /dev/null 2>&1")
      puts "%-#{max}s  [PASS]" % [f]  #if verbose?
    else
      puts "%-#{max}s  [FAIL]" % [f]  #if verbose?
      list << f
    end
  end

  puts "  #{list.size} Load Failures"

  if verbose?
    unless list.empty?
      puts "\n-- Load Failures --\n"
      list.each do |f|
        print "* "
        system "ruby -I#{loadpath} #{f} 2>&1"
        #puts
      end
      puts
    end
  end
end

#test_solo(options = nil) ⇒ Object

Run unit-tests. Each test is run in a separate interpretor to prevent script clash. This makes for a more robust test facility and prevents potential conflicts between test scripts.

tests     Test files (eg. test/tc_**/*.rb) [test/**/*]
loadpath  Directories to include in load path [lib].
require   List of files to require prior to running tests.
live      Deactive use of local libs and test against install.


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
# File 'lib/reap/project/test.rb', line 142

def test_solo(options=nil)
  options = test_configuration(options)

  tests    = options['tests']
  loadpath = options['loadpath']
  requires = options['requires']
  live     = options['live']
  exclude  = options['exclude']
  log      = options['log'] != false

  files = multiglob_r(*tests) - multiglob_r(*exclude)

  return puts("No tests.") if files.empty?

  files = files.select{ |f| File.extname(f) == '.rb' and File.file?(f) }
  width = files.collect{ |f| f.size }.max

  make if compiles?

  cmd   = %[ruby -I#{loadpath.join(':')} %s]
  dis   = "%-#{width}s"

  testruns = files.collect do |file|
    { 'files'   => file,
      'command' => cmd % file,
      'display' => dis % file
    }
  end

  report = test_loop_runner(testruns)

  puts report

  if log && !dryrun?
    logfile = File.join('log', apply_naming_policy('testlog', 'txt'))
    File.open(logfile, 'a') do |f|
      f << "= Solo Test @ #{Time.now}\n"
      f << report
      f << "\n"
    end
  end
end

#test_unit(options = {}) ⇒ Object

Run unit tests. Unlike test-solo and test-cross this loads all tests and runs them together in a single process.

Note that this shells out to the testrb program.

TODO: Generate a test log entry?



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/reap/project/test.rb', line 39

def test_unit(options={})
  options = test_configuration(options)

  tests    = options['tests']
  loadpath = options['loadpath']
  requires = options['requires']
  live     = options['live']
  exclude  = options['exclude']
  #log      = options['log'] != false
  #logfile  = File.join('log', apply_naming_policy('test', 'log'))

  # what about arguments for selecting specific tests?
  tests = options['arguments'] if options['arguments']

  #unless live
  #  loadpath.each do |lp|
  #    $LOAD_PATH.unshift(File.expand_path(lp))
  #  end
  #end

  if File.exist?('test/suite.rb')
    files = 'test/suite.rb'
  else
    files = multiglob_r(*tests)
  end

  if files.empty?
    $stderr.puts "No tests."
    return
  end

  filelist = files.select{|file| !File.directory?(file) }.join(' ')

  if live
    command  = %[testrb #{filelist} 2>&1]
  else
    command  = %[testrb -I#{loadpath.join(':')} #{filelist} 2>&1]
  end

  sh command

  #if log && !dryrun?
  #  command = %[testrb -I#{loadpath} #{filelist} > #{logfile} 2>&1]  # /dev/null 2>&1
  #  system command
  #  puts "Updated #{logfile}"
  #end
end

#trace=(x) ⇒ Object



89
# File 'lib/reap/project.rb', line 89

def trace=(x)   ; runmodes.trace   = x ; end

#trace?Boolean

Returns:

  • (Boolean)


83
# File 'lib/reap/project.rb', line 83

def trace?      ; runmodes.trace?   ; end

#verbose=(x) ⇒ Object



91
# File 'lib/reap/project.rb', line 91

def verbose=(x) ; runmodes.verbose = x ; end

#verbose?Boolean

Returns:

  • (Boolean)


85
# File 'lib/reap/project.rb', line 85

def verbose?    ; runmodes.verbose? ; end