# HG changeset patch # User Mahlon E. Smith # Date 1485992135 28800 # Node ID 3cc813140c8008b281417404cc0864da01cc07dc # Parent 7fc2d1713795ed4d9d6629843f91cc8dcf3f6392 First round of modernizing after a long absence. Much work to be done. diff -r 7fc2d1713795 -r 3cc813140c80 .gems --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gems Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,4 @@ +loggability +mail +rspec + diff -r 7fc2d1713795 -r 3cc813140c80 .pryrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.pryrc Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,14 @@ +#!/usr/bin/ruby + + +$LOAD_PATH.unshift( 'lib' ) + +begin + require 'ezmlm' + +rescue Exception => e + $stderr.puts "Ack! Ezmlm libraries failed to load: #{e.message}\n\t" + + e.backtrace.join( "\n\t" ) +end + + diff -r 7fc2d1713795 -r 3cc813140c80 .rspec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.rspec Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,1 @@ +-c -f d diff -r 7fc2d1713795 -r 3cc813140c80 .ruby-gemset --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.ruby-gemset Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,1 @@ +ezmlm diff -r 7fc2d1713795 -r 3cc813140c80 .ruby-version --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.ruby-version Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,1 @@ +2.4 diff -r 7fc2d1713795 -r 3cc813140c80 LICENSE --- a/LICENSE Fri Sep 05 23:58:48 2008 +0000 +++ b/LICENSE Wed Feb 01 15:35:35 2017 -0800 @@ -1,4 +1,4 @@ -Copyright (c) 2008, LAIKA Information Systems +Copyright (c) 2017, Mahlon E. Smith All rights reserved. Redistribution and use in source and binary forms, with or without diff -r 7fc2d1713795 -r 3cc813140c80 README --- a/README Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -= ThingFish - -== Authors - -* Michael Granger -* Jeremiah Jordan - -== Installation - -=== Requirements - -Ruby-Ezmlm is tested using Ruby 1.8.6: - - * Ruby (>= 1.8.6): http://www.ruby-lang.org/en/downloads/ - -Other versions may work, but are not tested. - -=== Ruby Modules - -There are two ways you can install Ruby-Ezmlm. The easiest is to use RubyGems: - - $ sudo gem install -y ruby-ezmlm - -If you'd rather install from source, you'll need these: - - * Tmail (>= 1.2.3.1): http://tmail.rubyforge.org/ - -Once these are installed: - - $ tar -zxvf ezmlm-0.0.1.tgz - $ cd thingfish-0.0.1 - $ su - - # rake install - -If you want to help out with development, run tests, or generate documentation, -you'll need a few more things: - - * RSpec (>= 1.0.5): http://rspec.rubyforge.org/ - * rcov (>= 0.7.0): http://eigenclass.org/hiki.rb?rcov - -If you have RubyGems installed, you can install these automatically via the -+install_dependencies+ task of the Rakefile: - - $ sudo rake install_dependencies - diff -r 7fc2d1713795 -r 3cc813140c80 README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,61 @@ +# Ruby-Ezmlm + +code +: https://bitbucket.org/mahlon/Ruby-Ezmlm + + +## Authors + +* Michael Granger +* Jeremiah Jordan +* Mahlon E. Smith + + +## Description + +This is a ruby interface for interacting with ezmlm-idx, an email list +manager for use with the Qmail MTA. (The -idx provides an extended +feature set over the initial ezmlm environment.) + +http://untroubled.org/ezmlm/ + + +## Prerequisites + +* Ruby 2.2 or better + + +## Installation + + $ gem install ezmlm + + +## License + +Copyright (c) 2017, Mahlon E. Smith +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the author/s, nor the names of the project's + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -r 7fc2d1713795 -r 3cc813140c80 Rakefile --- a/Rakefile Fri Sep 05 23:58:48 2008 +0000 +++ b/Rakefile Wed Feb 01 15:35:35 2017 -0800 @@ -1,290 +1,118 @@ -#!rake -# -# Ruby-Ezmlm rakefile -# -# Based on various other Rakefiles, especially one by Ben Bleything -# -# Copyright (c) 2008 The FaerieMUD Consortium -# -# Authors: -# * LAIKA Information Systems -# - -BEGIN { - require 'pathname' - basedir = Pathname.new( __FILE__ ).dirname - - libdir = basedir + "lib" - extdir = basedir + "ext" - - $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s ) - $LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s ) -} - +#!/usr/bin/env rake +# vim: set nosta noet ts=4 sw=4: -require 'rbconfig' -require 'rubygems' -require 'rake' -require 'rake/rdoctask' -require 'rake/testtask' -require 'rake/packagetask' -require 'rake/clean' - -$dryrun = false +require 'pathname' -### Config constants -BASEDIR = Pathname.new( __FILE__ ).dirname.relative_path_from( Pathname.getwd ) -BINDIR = BASEDIR + 'bin' -LIBDIR = BASEDIR + 'lib' -EXTDIR = BASEDIR + 'ext' -DOCSDIR = BASEDIR + 'docs' -PKGDIR = BASEDIR + 'pkg' - -PROJECT_NAME = 'Ruby-Ezmlm' -PKG_NAME = PROJECT_NAME.downcase -PKG_SUMMARY = 'A programmatic interface to ezmlm-idx lists' -VERSION_FILE = LIBDIR + 'ezmlm.rb' -PKG_VERSION = VERSION_FILE.read[ /VERSION = '(\d+\.\d+\.\d+)'/, 1 ] -PKG_FILE_NAME = "#{PKG_NAME.downcase}-#{PKG_VERSION}" -GEM_FILE_NAME = "#{PKG_FILE_NAME}.gem" +PROJECT = 'ezmlm' +BASEDIR = Pathname.new( __FILE__ ).expand_path.dirname.relative_path_from( Pathname.getwd ) +LIBDIR = BASEDIR + 'lib' -ARTIFACTS_DIR = Pathname.new( ENV['CC_BUILD_ARTIFACTS'] || 'artifacts' ) - -TEXT_FILES = %w( Rakefile ChangeLog README LICENSE ).collect {|filename| BASEDIR + filename } -BIN_FILES = Pathname.glob( BINDIR + '*' ).delete_if {|item| item =~ /\.svn/ } -LIB_FILES = Pathname.glob( LIBDIR + '**/*.rb' ).delete_if {|item| item =~ /\.svn/ } -EXT_FILES = Pathname.glob( EXTDIR + '**/*.{c,h,rb}' ).delete_if {|item| item =~ /\.svn/ } - -SPECDIR = BASEDIR + 'spec' -SPECLIBDIR = SPECDIR + 'lib' -SPEC_FILES = Pathname.glob( SPECDIR + '**/*_spec.rb' ).delete_if {|item| item =~ /\.svn/ } + - Pathname.glob( SPECLIBDIR + '**/*.rb' ).delete_if {|item| item =~ /\.svn/ } - -TESTDIR = BASEDIR + 'tests' -TEST_FILES = Pathname.glob( TESTDIR + '**/*.tests.rb' ).delete_if {|item| item =~ /\.svn/ } - -RAKE_TASKDIR = BASEDIR + 'rake' -RAKE_TASKLIBS = Pathname.glob( RAKE_TASKDIR + '*.rb' ) - -LOCAL_RAKEFILE = BASEDIR + 'Rakefile.local' - -EXTRA_PKGFILES = [] +if Rake.application.options.trace + $trace = true + $stderr.puts '$trace is enabled' +end -RELEASE_FILES = TEXT_FILES + - SPEC_FILES + - TEST_FILES + - BIN_FILES + - LIB_FILES + - EXT_FILES + - RAKE_TASKLIBS + - EXTRA_PKGFILES - -RELEASE_FILES << LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist? +# parse the current library version +$version = ( LIBDIR + "#{PROJECT}.rb" ).read.split(/\n/). + select{|line| line =~ /VERSION =/}.first.match(/([\d|.]+)/)[1] -COVERAGE_MINIMUM = ENV['COVERAGE_MINIMUM'] ? Float( ENV['COVERAGE_MINIMUM'] ) : 85.0 -RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' -RCOV_OPTS = [ - '--exclude', RCOV_EXCLUDES, - '--xrefs', - '--save', - '--callsites', - #'--aggregate', 'coverage.data' # <- doesn't work as of 0.8.1.2.0 - ] - - -# Subversion constants -- directory names for releases and tags -SVN_TRUNK_DIR = 'trunk' -SVN_RELEASES_DIR = 'releases' -SVN_BRANCHES_DIR = 'branches' -SVN_TAGS_DIR = 'tags' - -SVN_DOTDIR = BASEDIR + '.svn' -SVN_ENTRIES = SVN_DOTDIR + 'entries' +task :default => [ :spec, :docs, :package ] -### Load some task libraries that need to be loaded early -require RAKE_TASKDIR + 'helpers.rb' -require RAKE_TASKDIR + 'svn.rb' -require RAKE_TASKDIR + 'verifytask.rb' - -# Define some constants that depend on the 'svn' tasklib -PKG_BUILD = get_svn_rev( BASEDIR ) || 0 -SNAPSHOT_PKG_NAME = "#{PKG_FILE_NAME}.#{PKG_BUILD}" -SNAPSHOT_GEM_NAME = "#{SNAPSHOT_PKG_NAME}.gem" - -# Documentation constants -RDOCDIR = DOCSDIR + 'api' -RDOC_OPTIONS = [ - '-w', '4', - '-SHN', - '-i', '.', - '-m', 'README', - '-W', 'http://opensource.laika.com/wiki/ruby-ezmlm/browser/trunk/' - ] - -# Release constants -SMTP_HOST = 'mail.faeriemud.org' -SMTP_PORT = 465 # SMTP + SSL - -# Project constants -PROJECT_HOST = 'deveiate.org' -PROJECT_PUBDIR = "/usr/local/www/public/code" -PROJECT_DOCDIR = "#{PROJECT_PUBDIR}/#{PKG_NAME}" -PROJECT_SCPPUBURL = "#{PROJECT_HOST}:#{PROJECT_PUBDIR}" -PROJECT_SCPDOCURL = "#{PROJECT_HOST}:#{PROJECT_DOCDIR}" - -# Rubyforge stuff -RUBYFORGE_GROUP = 'laika' -RUBYFORGE_PROJECT = 'ezmlm' - -# Gem dependencies: gemname => version -DEPENDENCIES = { - 'tmail' => '>=1.2.3.1', -} +######################################################################## +### P A C K A G I N G +######################################################################## -# Developer Gem dependencies: gemname => version -DEVELOPMENT_DEPENDENCIES = { - 'amatch' => '>= 0.2.3', - 'rake' => '>= 0.8.1', - 'rcodetools' => '>= 0.7.0.0', - 'rcov' => '>= 0', - 'RedCloth' => '>= 4.0.3', - 'rspec' => '>= 0', - 'rubyforge' => '>= 0', - 'termios' => '>= 0', - 'text-format' => '>= 1.0.0', - 'tmail' => '>= 1.2.3.1', - 'ultraviolet' => '>= 0.10.2', - 'libxml-ruby' => '>= 0.8.3', -} - -# Non-gem requirements: packagename => version -REQUIREMENTS = { - 'ezmlm-idx' => '>=0', -} - -# RubyGem specification -GEMSPEC = Gem::Specification.new do |gem| - gem.name = PKG_NAME.downcase - gem.version = PKG_VERSION +require 'rubygems' +require 'rubygems/package_task' +spec = Gem::Specification.new do |s| + s.email = 'mahlon@martini.nu' + s.homepage = 'https://bitbucket.org/mahlon/Ruby-Ezmlm' + s.authors = [ + 'Mahlon E. Smith ', + 'Michael Granger ', + 'Jeremiah Jordan ' + ] + s.platform = Gem::Platform::RUBY + s.summary = "Interact with Ezmlm-IDX mailing lists." + s.name = PROJECT + s.version = $version + s.license = 'BSD-3-Clause' + s.has_rdoc = true + s.require_path = 'lib' + s.bindir = 'bin' + s.files = File.read( __FILE__ ).split( /^__END__/, 2 ).last.split + # s.executables = %w[] + s.description = <<-EOF +This is a ruby interface for interacting with ezmlm-idx, an email list +manager for use with the Qmail MTA. (The -idx provides an extended +feature set over the initial ezmlm environment.) + EOF + s.required_ruby_version = '>= 2' - gem.summary = PKG_SUMMARY - gem.description = <<-EOD - Ruby-Ezmlm provides a programmatic interface to ezmlm-idx lists, their archives, and the command - line utilities that interact with them. The library is intended to provide two sets of - functionality: the management and setup of lists, and programmatic access to the message archive. - EOD - - gem.authors = 'LAIKA Information Systems' - gem.email = 'opensource@laika.com' - gem.homepage = 'http://opensource.laika.com/wiki/ruby-ezmlm' - gem.rubyforge_project = RUBYFORGE_PROJECT - - gem.has_rdoc = true - gem.rdoc_options = RDOC_OPTIONS - - gem.bindir = BINDIR.relative_path_from(BASEDIR).to_s - - - gem.files = RELEASE_FILES. - collect {|f| f.relative_path_from(BASEDIR).to_s } - gem.test_files = SPEC_FILES. - collect {|f| f.relative_path_from(BASEDIR).to_s } - - DEPENDENCIES.each do |name, version| - version = '>= 0' if version.length.zero? - gem.add_runtime_dependency( name, version ) - end - - DEVELOPMENT_DEPENDENCIES.each do |name, version| - version = '>= 0' if version.length.zero? - gem.add_development_dependency( name, version ) - end - - REQUIREMENTS.each do |name, version| - gem.requirements << [ name, version ].compact.join(' ') - end + s.add_dependency 'loggability', "~> 0.13" + s.add_dependency 'mail', "~> 2.6" end -# Manual-generation config -MANUALDIR = DOCSDIR + 'manual' - -$trace = Rake.application.options.trace ? true : false -$dryrun = Rake.application.options.dryrun ? true : false - - -# Load any remaining task libraries -RAKE_TASKLIBS.each do |tasklib| - next if tasklib =~ %r{/(helpers|svn|verifytask)\.rb$} - begin - require tasklib - rescue ScriptError => err - fail "Task library '%s' failed to load: %s: %s" % - [ tasklib, err.class.name, err.message ] - trace "Backtrace: \n " + err.backtrace.join( "\n " ) - rescue => err - log "Task library '%s' failed to load: %s: %s. Some tasks may not be available." % - [ tasklib, err.class.name, err.message ] - trace "Backtrace: \n " + err.backtrace.join( "\n " ) - end -end - -# Load any project-specific rules defined in 'Rakefile.local' if it exists -import LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist? - - -##################################################################### -### T A S K S -##################################################################### - -### Default task -task :default => [:clean, :local, :spec, :rdoc, :package] - -### Task the local Rakefile can append to -- no-op by default -task :local - - -### Task: clean -CLEAN.include 'coverage' -CLOBBER.include 'artifacts', 'coverage.info', PKGDIR - -# Target to hinge on ChangeLog updates -file SVN_ENTRIES - -### Task: changelog -file 'ChangeLog' => SVN_ENTRIES.to_s do |task| - log "Updating #{task.name}" - - changelog = make_svn_changelog() - File.open( task.name, 'w' ) do |fh| - fh.print( changelog ) - end +Gem::PackageTask.new( spec ) do |pkg| + pkg.need_zip = true + pkg.need_tar = true end -### Task: cruise (Cruisecontrol task) -desc "Cruisecontrol build" -task :cruise => [:clean, :spec, :package] do |task| - raise "Artifacts dir not set." if ARTIFACTS_DIR.to_s.empty? - artifact_dir = ARTIFACTS_DIR.cleanpath - artifact_dir.mkpath - - coverage = BASEDIR + 'coverage' - if coverage.exist? && coverage.directory? - $stderr.puts "Copying coverage stats..." - FileUtils.cp_r( 'coverage', artifact_dir ) +######################################################################## +### D O C U M E N T A T I O N +######################################################################## + +begin + require 'rdoc/task' + + desc 'Generate rdoc documentation' + RDoc::Task.new do |rdoc| + rdoc.name = :docs + rdoc.rdoc_dir = 'docs' + rdoc.main = "README.rdoc" + # rdoc.options = [ '-f', 'fivefish' ] + rdoc.rdoc_files = [ 'lib', *FileList['*.rdoc'] ] end - - $stderr.puts "Copying packages..." - FileUtils.cp_r( FileList['pkg/*'].to_a, artifact_dir ) + + RDoc::Task.new do |rdoc| + rdoc.name = :doc_coverage + rdoc.options = [ '-C1' ] + end + +rescue LoadError + $stderr.puts "Omitting 'docs' tasks, rdoc doesn't seem to be installed." end -desc "Update the build system to the latest version" -task :update_build do - log "Updating the build system" - sh 'svn', 'up', RAKE_TASKDIR - log "Updating the Rakefile" - sh 'rake', '-f', RAKE_TASKDIR + 'Metarakefile' +######################################################################## +### T E S T I N G +######################################################################## + +begin + require 'rspec/core/rake_task' + task :test => :spec + + desc "Run specs" + RSpec::Core::RakeTask.new do |t| + t.pattern = "spec/**/*_spec.rb" + end + + desc "Build a coverage report" + task :coverage do + ENV[ 'COVERAGE' ] = "yep" + Rake::Task[ :spec ].invoke + end + +rescue LoadError + $stderr.puts "Omitting testing tasks, rspec doesn't seem to be installed." end + + +######################################################################## +### M A N I F E S T +######################################################################## +__END__ + diff -r 7fc2d1713795 -r 3cc813140c80 bin/ezmlm-listd --- a/bin/ezmlm-listd Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -#!/usr/bin/env ruby -# -# A startup script for an instance of Ezmlm::ListDaemon. -# $Id$ -# -# Authors: -# * Michael Granger -# * Jeremiah Jordan -# - -BEGIN { - require 'pathname' - basedir = Pathname.new( __FILE__ ).dirname.parent - libdir = basedir + 'lib' - - $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s ) -} - -begin - require 'rubygems' - require 'optparse' - require 'ezmlm/listdaemon' -rescue LoadError - unless Object.const_defined?( :Gem ) - require 'rubygems' - retry - end - raise -end - - -progname = Pathname.new( $0 ).basename -opts = Ezmlm::ListDaemon.default_options - -oparser = OptionParser.new do |oparser| - oparser.accept( Pathname ) {|path| Pathname.new(path) } - - oparser.banner = "Usage: #{progname.basename} OPTIONS LISTSDIRECTORY" - oparser.separator 'Version ' + Ezmlm::VERSION - - oparser.separator '' - oparser.separator 'Options:' - oparser.on( '--bind ADDRESS', '-b', String, - "Specify the address to bind to. Defaults to '#{opts.bind_addr}'" ) do |bindaddr| - opts.bind_addr = bindaddr - end - - oparser.on( '--port PORTNUMBER', '-p', Integer, - "Specify the port to connect to. Defaults to '#{opts.bind_port}" ) do |portnumber| - opts.bind_port = portnumber - end - - - oparser.separator '' - oparser.separator 'Other Options:' - oparser.on( '--debug', '-d', FalseClass, "Turn on debugging" ) do - $DEBUG = true - $stderr.puts "Debugging enabled." - opts.debugmode = true - end - - oparser.on_tail( '--help', '-h', FalseClass, "Display help for the given command." ) do - $stderr.puts( oparser ) - exit!( 0 ) - end - - oparser.on_tail( '--version', '-V', "Print Ezmlm library version on STDOUT and quit." ) do - $stdout.puts Ezmlm::VERSION - exit!( 0 ) - end -end - - -# Parse command-line flags and display the help if there isn't exactly one argument -remaining_args = oparser.parse( ARGV ) -if remaining_args.nitems != 1 - $stderr.puts( oparser ) - exit( 64 ) # EX_USAGE -end - -listsdir = remaining_args.shift -daemon = Ezmlm::ListDaemon.new( listsdir, opts ) -daemon.start.join diff -r 7fc2d1713795 -r 3cc813140c80 experiments/announcepost.rb --- a/experiments/announcepost.rb Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -require 'drb' -DRb.start_service -lists = DRbObject.new( nil, 'druby://localhost:32315' ) -l = lists.get_list( 'announce' ) -l.last_post - diff -r 7fc2d1713795 -r 3cc813140c80 lib/ezmlm.rb --- a/lib/ezmlm.rb Fri Sep 05 23:58:48 2008 +0000 +++ b/lib/ezmlm.rb Wed Feb 01 15:35:35 2017 -0800 @@ -1,4 +1,5 @@ #!/usr/bin/ruby +# vim: set nosta noet ts=4 sw=4: # # A Ruby programmatic interface to the ezmlm-idx mailing list system # @@ -6,13 +7,6 @@ # # $Id$ # -# == Authors -# -# * Michael Granger -# * Jeremiah Jordan -# -# :include: LICENSE -# #--- # # Please see the file LICENSE in the base directory for licensing details. @@ -24,19 +18,10 @@ ### Toplevel namespace module module Ezmlm - # SVN Revision - SVNRev = %q$Rev$ - - # SVN Id - SVNId = %q$Id$ - # Package version - VERSION = '0.0.1' - + VERSION = '0.1.0' require 'ezmlm/list' - require 'ezmlm/listdaemon' - ############### module_function @@ -44,23 +29,23 @@ ### Find all directories that look like an Ezmlm list directory under the specified +listsdir+ ### and return Pathname objects for each. + ### def find_directories( listsdir ) listsdir = Pathname.new( listsdir ) return Pathname.glob( listsdir + '*' ).select do |entry| entry.directory? && ( entry + 'mailinglist' ).exist? end end - + ### Iterate over each directory that looks like an Ezmlm list in the specified +listsdir+ and ### yield it as an Ezmlm::List object. + ### def each_list( listsdir ) find_directories( listsdir ).each do |entry| yield( Ezmlm::List.new(entry) ) end end - end # module Ezmlm -# vim: set nosta noet ts=4 sw=4: diff -r 7fc2d1713795 -r 3cc813140c80 lib/ezmlm/list.rb --- a/lib/ezmlm/list.rb Fri Sep 05 23:58:48 2008 +0000 +++ b/lib/ezmlm/list.rb Wed Feb 01 15:35:35 2017 -0800 @@ -1,39 +1,29 @@ #!/usr/bin/ruby +# vim: set nosta noet ts=4 sw=4: # -# A Ruby interface to a single Ezmlm-idx mailing list directory +# A Ruby interface to a single Ezmlm-idx mailing list directory. # # == Version # # $Id$ # -# == Authors -# -# * Michael Granger -# * Jeremiah Jordan -# -# :include: LICENSE -# #--- -# -# Please see the file LICENSE in the base directory for licensing details. -# require 'pathname' require 'ezmlm' -require 'tmail' +require 'mail' ### A Ruby interface to an ezmlm-idx mailing list directory +### class Ezmlm::List - + ### Create a new Ezmlm::List object for the specified +listdir+, which should be ### an ezmlm-idx mailing list directory. + ### def initialize( listdir ) - listdir = Pathname.new( listdir ) if !listdir.is_a?( Pathname ) + listdir = Pathname.new( listdir ) unless listdir.is_a?( Pathname ) @listdir = listdir - - # Cached lookups - @config = nil end @@ -49,20 +39,20 @@ def name return self.config[ 'L' ] end - + ### Return the configured host of the list def host return self.config[ 'H' ] end - + ### Return the configured address of the list (in list@host form) def address return "%s@%s" % [ self.name, self.host ] end alias_method :fullname, :address - + ### Return the number of messages in the list archive def message_count @@ -91,23 +81,23 @@ unless @config configfile = self.listdir + 'config' raise "List config file %p does not exist" % [ configfile ] unless configfile.exist? - + @config = configfile.read.scan( /^(\S):([^\n]*)$/m ).inject({}) do |h,pair| key,val = *pair h[key] = val h end end - + return @config end - + ### Return the email address of the list's owner. def owner self.config['5'] end - + ### Fetch an Array of the email addresses for all of the list's subscribers. def subscribers @@ -132,10 +122,10 @@ ### of a closed list. def subscription_moderators return [] unless self.closed? - + modsubfile = self.listdir + 'modsub' remotefile = self.listdir + 'remote' - + subdir = nil if modsubfile.exist? && modsubfile.read(1) == '/' subdir = Pathname.new( modsubfile.read.chomp ) @@ -144,29 +134,29 @@ else subdir = self.listdir + 'mod/subscribers' end - + return self.read_subscriber_dir( subdir ) end - - + + ### Returns an Array of email addresses of people responsible for moderating posts ### sent to the list. def message_moderators return [] unless self.moderated? - + modpostfile = self.listdir + 'modpost' subdir = nil - + if modpostfile.exist? && modpostfile.read(1) == '/' subdir = Pathname.new( modpostfile.read.chomp ) else subdir = self.listdir + 'mod/subscribers' end - + return self.read_subscriber_dir( subdir ) end - - + + ### Return a TMail::Mail object loaded from the last post to the list. Returns ### +nil+ if there are no archived posts. def last_post @@ -189,25 +179,23 @@ last_post = TMail::Mail.load( last_post_path.to_s ) end - - + + ######### protected ######### - ### Read the hashed subscriber email addresses from the specified +directory+ and return them in + ### Read the hashed subscriber email addresses from the specified +directory+ and return them in ### an Array. def read_subscriber_dir( directory ) rval = [] Pathname.glob( directory + '*' ) do |hashfile| rval.push( hashfile.read.scan(/T([^\0]+)\0/) ) end - + return rval.flatten end - - -end -# vim: set nosta noet ts=4 sw=4: +end # class Ezmlm::List + diff -r 7fc2d1713795 -r 3cc813140c80 lib/ezmlm/listdaemon.rb --- a/lib/ezmlm/listdaemon.rb Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -#!/usr/bin/ruby -# -# A DRb interface to one or more ezmlm-idx mailing lists. -# -# == Version -# -# $Id$ -# -# == Authors -# -# * Michael Granger -# * Jeremiah Jordan -# -# :include: LICENSE -# -#--- -# -# Please see the file LICENSE in the base directory for licensing details. -# - -require 'pathname' -require 'ezmlm' -require 'ezmlm/list' -require 'drb' -require 'ostruct' - - -### A DRb interface to one or more ezmlm-idx mailing lists -class Ezmlm::ListDaemon - - # The default port to listen on - DEFAULT_PORT = 32315 - - # The default address to bind to - DEFAULT_ADDRESS = '127.0.0.1' - - - ### The interface that is presented to DRb - class Service - include Enumerable - - ### Create a new service endpoint for the specified +listsdir+, which is a directory - ### which contains ezmlm-idx list directories. - def initialize( listsdir ) - listsdir = Pathname.new( listsdir ) - @listsdir = listsdir - end - - - ###### - public - ###### - - # The directory which contains the list directories that should be served. - attr_reader :listsdir - - - ### Create a new Ezmlm::List object for the list directory with the specified +name+. - def get_list( name ) - name = validate_listdir_name( name ) - return Ezmlm::List.new( self.listsdir + name ) - end - - - ### Iterate over each current list in the Service's listsdir, yielding an Ezmlm::List object - ### for each one. - def each_list( &block ) # :yields: list_object - Ezmlm.each_list( self.listsdir, &block ) - end - alias_method :each, :each_list - - - ####### - private - ####### - - VALID_LISTNAME_PATTERN = /^[a-z0-9.-]+$/i - - ### Ensure that the given +name+ is a valid list name, raising an exception if not. Returns - ### an untainted copy of +name+. - def validate_listdir_name( name ) - unless match = VALID_LISTNAME_PATTERN.match( name ) - raise ArgumentError, "invalid list name %p" % [ name ] - end - - return match[0].untaint - end - - end # class Service - - - - ### Return an OpenStruct that contains the default options - def self::default_options - opts = OpenStruct.new - - opts.bind_addr = DEFAULT_ADDRESS - opts.bind_port = DEFAULT_PORT - opts.debugmode = false - opts.helpmode = false - opts.foreground = false - - return opts - end - - - ################################################################# - ### I N S T A N C E M E T H O D S - ################################################################# - - ### Create a new Ezmlm::ListDaemon that will serve objects for the list directories - ### contained in +listsdir+. The +options+ argument, if given, is an object (such as the one - ### returned from ::default_options) that contains values for the following methods: - ### - ### bind_addr:: - ### The address to bind to. Defaults to DEFAULT_ADDRESS. - ### bind_port:: - ### The port to listen on. Defaults to DEFAULT_PORT. - ### debugmode:: - ### Whether to run in debugging mode, which causes the daemon to run in the foreground - ### and send any output to STDERR. Defaults to +false+. - ### foreground:: - ### Don't go into the background. - def initialize( listsdir, options=nil ) - @service = Service.new( listsdir ) - @options = options || self.class.default_options - end - - - ###### - public - ###### - - # The daemon's configuration options - attr_reader :options - - # The Ezmlm::ListDaemon::Service object that serves as the DRb interface - attr_reader :service - - - ### Daemonize unless configured otherwise, start the DRb service and return the listening - ### Thread object - def start - uri = "druby://%s:%d" % [ self.options.bind_addr, self.options.bind_port ] - DRb.start_service( uri, @service ) - - return DRb.thread - end - - -end # class Ezmlm::ListDaemon - -# vim: set nosta noet ts=4 sw=4: diff -r 7fc2d1713795 -r 3cc813140c80 project.yml --- a/project.yml Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -rubyforge_project: ezmlm -project_requirements: - ezmlm-idx: ">=0" -project_description: |- - Ruby-Ezmlm provides a programmatic interface to ezmlm-idx lists, their archives, and the command - line utilities that interact with them. The library is intended to provide two sets of - functionality: the management and setup of lists, and programmatic access to the message archive. -rubyforge_group: laika -author_name: LAIKA Information Systems -project_homepage: http://opensource.laika.com/wiki/ruby-ezmlm -project_dependencies: - tmail: ">=1.2.3.1" -project_summary: A programmatic interface to ezmlm-idx lists -project_name: Ruby-Ezmlm -version_file: ezmlm.rb -additional_pkgfiles: [] - -dev_dependencies: {} - -author_email: opensource@laika.com diff -r 7fc2d1713795 -r 3cc813140c80 spec/ezmlm/list_spec.rb --- a/spec/ezmlm/list_spec.rb Fri Sep 05 23:58:48 2008 +0000 +++ b/spec/ezmlm/list_spec.rb Wed Feb 01 15:35:35 2017 -0800 @@ -3,30 +3,14 @@ BEGIN { require 'pathname' basedir = Pathname.new( __FILE__ ).dirname.parent.parent - libdir = basedir + "lib" - $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir ) } - -begin - require 'tmail' - require 'spec/runner' - require 'spec/lib/helpers' - require 'ezmlm/list' -rescue LoadError - unless Object.const_defined?( :Gem ) - require 'rubygems' - retry - end - raise -end - +require_relative '../spec_helpers' +require 'ezmlm' describe Ezmlm::List do - include Ezmlm::SpecHelpers - # Testing constants TEST_LISTDIR = Pathname.new( 'list' ) @@ -34,7 +18,7 @@ TEST_LIST_HOST = 'lists.syrup.info' TEST_OWNER = 'listowner@rumpus-the-whale.info' TEST_CUSTOM_MODERATORS_DIR = '/foo/bar/clowns' - + TEST_SUBSCRIBERS = %w[ pete.chaffee@toadsmackers.com dolphinzombie@alahalohamorra.com @@ -44,7 +28,7 @@ TEST_MODERATORS = %w[ dolphinzombie@alahalohamorra.com ] - + TEST_CONFIG = <<-"EOF".gsub( /^\t+/, '' ) F:-aBCDeFGHijKlMnOpQrStUVWXYZ X: @@ -62,7 +46,7 @@ 8: 9: EOF - + it "can create a list" it "can add a new subscriber" @@ -70,359 +54,359 @@ it "can edit the list's text files" - ### + ### ### List manager functions - ### + ### describe "list manager functions" do - + before( :each ) do @listpath = TEST_LISTDIR.dup @list = Ezmlm::List.new( @listpath ) end - - + + it "can return the configured list name" do - @list.stub!( :config ).and_return({ 'L' => :the_list_name }) - @list.name.should == :the_list_name + allow(@list).to receive( :config ).and_return({ 'L' => :the_list_name }) + expect(@list.name).to eq(:the_list_name) end - - + + it "can return the configured list host" do - @list.stub!( :config ).and_return({ 'H' => :the_list_host }) - @list.host.should == :the_list_host + allow(@list).to receive( :config ).and_return({ 'H' => :the_list_host }) + expect(@list.host).to eq(:the_list_host) end - - + + it "can return the configured list address" do - @list.stub!( :config ).and_return({ 'L' => TEST_LIST_NAME, 'H' => TEST_LIST_HOST }) - @list.address.should == "%s@%s" % [ TEST_LIST_NAME, TEST_LIST_HOST ] + allow(@list).to receive( :config ).and_return({ 'L' => TEST_LIST_NAME, 'H' => TEST_LIST_HOST }) + expect(@list.address).to eq("%s@%s" % [ TEST_LIST_NAME, TEST_LIST_HOST ]) end - - + + CONFIG_KEYS = %w[ F X D T L H C 0 3 4 5 6 7 8 9 ] it "can fetch the list config as a Hash" do - config_path = mock( "Mock config path" ) - @listpath.should_receive( :+ ).with( 'config' ).and_return( config_path ) - config_path.should_receive( :exist? ).and_return( true ) - config_path.should_receive( :read ).and_return( TEST_CONFIG ) - - @list.config.should be_an_instance_of( Hash ) - @list.config.should have( CONFIG_KEYS.length ).members - @list.config.keys.should include( *CONFIG_KEYS ) + config_path = double( "Mock config path" ) + expect(@listpath).to receive( :+ ).with( 'config' ).and_return( config_path ) + expect(config_path).to receive( :exist? ).and_return( true ) + expect(config_path).to receive( :read ).and_return( TEST_CONFIG ) + + expect(@list.config).to be_an_instance_of( Hash ) + expect(@list.config.size).to eq(CONFIG_KEYS.length) + expect(@list.config.keys).to include( *CONFIG_KEYS ) end - + it "raises an error if the list config file doesn't exist" do - config_path = mock( "Mock config path" ) - @listpath.should_receive( :+ ).with( 'config' ).and_return( config_path ) - config_path.should_receive( :exist? ).and_return( false ) + config_path = double( "Mock config path" ) + expect(@listpath).to receive( :+ ).with( 'config' ).and_return( config_path ) + expect(config_path).to receive( :exist? ).and_return( false ) - lambda { + expect { @list.config - }.should raise_error( RuntimeError, /does not exist/ ) + }.to raise_error( RuntimeError, /does not exist/ ) end - + it "can return a list of subscribers' email addresses" do subscribers_dir = TEST_LISTDIR + 'subscribers' - - expectation = Pathname.should_receive( :glob ).with( subscribers_dir + '*' ) + + expectation = expect(Pathname).to receive( :glob ).with( subscribers_dir + '*' ) TEST_SUBSCRIBERS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end - + subscribers = @list.subscribers - - subscribers.should have(TEST_SUBSCRIBERS.length).members - subscribers.should include( *TEST_SUBSCRIBERS ) + + expect(subscribers.size).to eq(TEST_SUBSCRIBERS.length) + expect(subscribers).to include( *TEST_SUBSCRIBERS ) end ### Subscriber moderation it "knows that subscription moderation is enabled if the dir/modsub file exists" do - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).and_return( true ) - - @list.should be_closed() + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).and_return( true ) + + expect(@list).to be_closed() end - + it "knows that subscription moderation is enabled if the dir/remote file exists" do - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).and_return( false ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).and_return( false ) - remote_path_obj = mock( "Mock 'remote' path object" ) - @listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) - remote_path_obj.should_receive( :exist? ).and_return( true ) - - @list.should be_closed() + remote_path_obj = double( "Mock 'remote' path object" ) + expect(@listpath).to receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) + expect(remote_path_obj).to receive( :exist? ).and_return( true ) + + expect(@list).to be_closed() end - - + + it "knows that subscription moderation is disabled if neither the dir/modsub nor " + "dir/remote files exist" do - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).and_return( false ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).and_return( false ) - remote_path_obj = mock( "Mock 'remote' path object" ) - @listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) - remote_path_obj.should_receive( :exist? ).and_return( false ) + remote_path_obj = double( "Mock 'remote' path object" ) + expect(@listpath).to receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) + expect(remote_path_obj).to receive( :exist? ).and_return( false ) - @list.should_not be_closed() + expect(@list).not_to be_closed() end it "returns an empty array of subscription moderators for an open list" do - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).and_return( false ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).and_return( false ) - remote_path_obj = mock( "Mock 'remote' path object" ) - @listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) - remote_path_obj.should_receive( :exist? ).and_return( false ) + remote_path_obj = double( "Mock 'remote' path object" ) + expect(@listpath).to receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) + expect(remote_path_obj).to receive( :exist? ).and_return( false ) - @list.subscription_moderators.should be_empty() + expect(@list.subscription_moderators).to be_empty() end - + it "can return a list of subscription moderators' email addresses" do # Test the moderation config files for existence - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).twice.and_return( true ) - remote_path_obj = mock( "Mock 'remote' path object" ) - @listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) - remote_path_obj.should_receive( :exist? ).once.and_return( true ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).twice.and_return( true ) + remote_path_obj = double( "Mock 'remote' path object" ) + expect(@listpath).to receive( :+ ).with( 'remote' ).and_return( remote_path_obj ) + expect(remote_path_obj).to receive( :exist? ).once.and_return( true ) # Try to read directory names from both config files - modsub_path_obj.should_receive( :read ).with( 1 ).and_return( nil ) - remote_path_obj.should_receive( :read ).with( 1 ).and_return( nil ) - + expect(modsub_path_obj).to receive( :read ).with( 1 ).and_return( nil ) + expect(remote_path_obj).to receive( :read ).with( 1 ).and_return( nil ) + # Read subscribers from the default directory - subscribers_dir = mock( "Mock moderator subscribers directory" ) - @listpath.should_receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir ) - subscribers_dir.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) - expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir ) + subscribers_dir = double( "Mock moderator subscribers directory" ) + expect(@listpath).to receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir ) + expect(subscribers_dir).to receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) + expectation = expect(Pathname).to receive( :glob ).with( :mod_sub_dir ) TEST_MODERATORS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end mods = @list.subscription_moderators - mods.should have(TEST_MODERATORS.length).members - mods.should include( *TEST_MODERATORS ) + expect(mods.size).to eq(TEST_MODERATORS.length) + expect(mods).to include( *TEST_MODERATORS ) end - - + + it "can return a list of subscription moderators' email addresses when the moderators " + "directory has been customized" do # Test the moderation config files for existence - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).twice.and_return( true ) - @listpath.should_receive( :+ ).with( 'remote' ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).twice.and_return( true ) + expect(@listpath).to receive( :+ ).with( 'remote' ) # Try to read directory names from both config files - modsub_path_obj.should_receive( :read ).with( 1 ).and_return( '/' ) - modsub_path_obj.should_receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) + expect(modsub_path_obj).to receive( :read ).with( 1 ).and_return( '/' ) + expect(modsub_path_obj).to receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) - custom_mod_path = mock( "Mock path object for customized moderator dir" ) - Pathname.should_receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) + custom_mod_path = double( "Mock path object for customized moderator dir" ) + expect(Pathname).to receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) # Read subscribers from the default file - custom_mod_path.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) - expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir ) + expect(custom_mod_path).to receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) + expectation = expect(Pathname).to receive( :glob ).with( :mod_sub_dir ) TEST_MODERATORS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end mods = @list.subscription_moderators - mods.should have(TEST_MODERATORS.length).members - mods.should include( *TEST_MODERATORS ) + expect(mods.size).to eq(TEST_MODERATORS.length) + expect(mods).to include( *TEST_MODERATORS ) end - + it "can get a list of modererators when remote subscription moderation is enabled" + " and the modsub configuration is empty" do # Test the moderation config files for existence - modsub_path_obj = mock( "Mock 'modsub' path object" ) - @listpath.should_receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) - modsub_path_obj.should_receive( :exist? ).twice.and_return( false ) - remote_path_obj = mock( "Mock 'remote' path object" ) - @listpath.should_receive( :+ ).with( 'remote' ).twice.and_return( remote_path_obj ) - remote_path_obj.should_receive( :exist? ).twice.and_return( true ) + modsub_path_obj = double( "Mock 'modsub' path object" ) + expect(@listpath).to receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj ) + expect(modsub_path_obj).to receive( :exist? ).twice.and_return( false ) + remote_path_obj = double( "Mock 'remote' path object" ) + expect(@listpath).to receive( :+ ).with( 'remote' ).twice.and_return( remote_path_obj ) + expect(remote_path_obj).to receive( :exist? ).twice.and_return( true ) # Try to read directory names from both config files - remote_path_obj.should_receive( :read ).with( 1 ).and_return( '/' ) - remote_path_obj.should_receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) + expect(remote_path_obj).to receive( :read ).with( 1 ).and_return( '/' ) + expect(remote_path_obj).to receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) - custom_mod_path = mock( "Mock path object for customized moderator dir" ) - Pathname.should_receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) + custom_mod_path = double( "Mock path object for customized moderator dir" ) + expect(Pathname).to receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) # Read subscribers from the default file - custom_mod_path.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) - expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir ) + expect(custom_mod_path).to receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) + expectation = expect(Pathname).to receive( :glob ).with( :mod_sub_dir ) TEST_MODERATORS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end mods = @list.subscription_moderators - mods.should have(TEST_MODERATORS.length).members - mods.should include( *TEST_MODERATORS ) + expect(mods.size).to eq(TEST_MODERATORS.length) + expect(mods).to include( *TEST_MODERATORS ) end ### Message moderation - + it "knows that subscription moderation is enabled if the dir/modpost file exists" do - modpost_path_obj = mock( "Mock 'modpost' path object" ) - @listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) - modpost_path_obj.should_receive( :exist? ).and_return( true ) - - @list.should be_moderated() + modpost_path_obj = double( "Mock 'modpost' path object" ) + expect(@listpath).to receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) + expect(modpost_path_obj).to receive( :exist? ).and_return( true ) + + expect(@list).to be_moderated() end - + it "knows that subscription moderation is disabled if the dir/modpost file doesn't exist" do - modpost_path_obj = mock( "Mock 'modpost' path object" ) - @listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) - modpost_path_obj.should_receive( :exist? ).and_return( false ) + modpost_path_obj = double( "Mock 'modpost' path object" ) + expect(@listpath).to receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) + expect(modpost_path_obj).to receive( :exist? ).and_return( false ) - @list.should_not be_moderated() + expect(@list).not_to be_moderated() end it "returns an empty array of message moderators for an open list" do - modpost_path_obj = mock( "Mock 'modpost' path object" ) - @listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) - modpost_path_obj.should_receive( :exist? ).and_return( false ) + modpost_path_obj = double( "Mock 'modpost' path object" ) + expect(@listpath).to receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj ) + expect(modpost_path_obj).to receive( :exist? ).and_return( false ) - @list.message_moderators.should be_empty() + expect(@list.message_moderators).to be_empty() end - - + + it "can return a list of message moderators' email addresses" do # Test the moderation config file for existence - modpost_path_obj = mock( "Mock 'modpost' path object" ) - @listpath.should_receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj ) - modpost_path_obj.should_receive( :exist? ).twice.and_return( true ) + modpost_path_obj = double( "Mock 'modpost' path object" ) + expect(@listpath).to receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj ) + expect(modpost_path_obj).to receive( :exist? ).twice.and_return( true ) # Try to read directory names from the config file - modpost_path_obj.should_receive( :read ).with( 1 ).and_return( nil ) - + expect(modpost_path_obj).to receive( :read ).with( 1 ).and_return( nil ) + # Read subscribers from the default directory - subscribers_dir = mock( "Mock moderator subscribers directory" ) - @listpath.should_receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir ) - subscribers_dir.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) - expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir ) + subscribers_dir = double( "Mock moderator subscribers directory" ) + expect(@listpath).to receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir ) + expect(subscribers_dir).to receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) + expectation = expect(Pathname).to receive( :glob ).with( :mod_sub_dir ) TEST_MODERATORS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end mods = @list.message_moderators - mods.should have(TEST_MODERATORS.length).members - mods.should include( *TEST_MODERATORS ) + expect(mods.size).to eq(TEST_MODERATORS.length) + expect(mods).to include( *TEST_MODERATORS ) end - - + + it "can return a list of message moderators' email addresses when the moderators " + "directory has been customized" do # Test the moderation config files for existence - modpost_path_obj = mock( "Mock 'modpost' path object" ) - @listpath.should_receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj ) - modpost_path_obj.should_receive( :exist? ).twice.and_return( true ) + modpost_path_obj = double( "Mock 'modpost' path object" ) + expect(@listpath).to receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj ) + expect(modpost_path_obj).to receive( :exist? ).twice.and_return( true ) # Try to read directory names from both config files - modpost_path_obj.should_receive( :read ).with( 1 ).and_return( '/' ) - modpost_path_obj.should_receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) + expect(modpost_path_obj).to receive( :read ).with( 1 ).and_return( '/' ) + expect(modpost_path_obj).to receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR ) - custom_mod_path = mock( "Mock path object for customized moderator dir" ) - Pathname.should_receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) + custom_mod_path = double( "Mock path object for customized moderator dir" ) + expect(Pathname).to receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path ) # Read subscribers from the default file - custom_mod_path.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) - expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir ) + expect(custom_mod_path).to receive( :+ ).with( '*' ).and_return( :mod_sub_dir ) + expectation = expect(Pathname).to receive( :glob ).with( :mod_sub_dir ) TEST_MODERATORS.each do |email| - mock_subfile = mock( "Mock subscribers file for '#{email}'" ) - mock_subfile.should_receive( :read ).and_return( "T#{email}\0" ) + mock_subfile = double( "Mock subscribers file for '#{email}'" ) + expect(mock_subfile).to receive( :read ).and_return( "T#{email}\0" ) expectation.and_yield( mock_subfile ) end mods = @list.message_moderators - mods.should have(TEST_MODERATORS.length).members - mods.should include( *TEST_MODERATORS ) + expect(mods.size).to eq(TEST_MODERATORS.length) + expect(mods).to include( *TEST_MODERATORS ) end ### List owner - + it "returns nil when the list doesn't have an owner in its config" do - @list.stub!( :config ).and_return({ '5' => nil }) - @list.owner.should == nil + allow(@list).to receive( :config ).and_return({ '5' => nil }) + expect(@list.owner).to eq(nil) end - - + + it "can return the email address of the list owner" do - @list.stub!( :config ).and_return({ '5' => TEST_OWNER }) - @list.owner.should == TEST_OWNER + allow(@list).to receive( :config ).and_return({ '5' => TEST_OWNER }) + expect(@list.owner).to eq(TEST_OWNER) end end - - - ### + + + ### ### Archive functions - ### + ### describe "archive functions" do - + before( :each ) do @listpath = TEST_LISTDIR.dup @list = Ezmlm::List.new( @listpath ) end - - + + it "can return the count of archived posts" do - numpath_obj = mock( "num file path object" ) - @listpath.should_receive( :+ ).with( 'num' ).and_return( numpath_obj ) - - numpath_obj.should_receive( :exist? ).and_return( true ) - numpath_obj.should_receive( :read ).and_return( "1723:123123123" ) - - @list.message_count.should == 1723 + numpath_obj = double( "num file path object" ) + expect(@listpath).to receive( :+ ).with( 'num' ).and_return( numpath_obj ) + + expect(numpath_obj).to receive( :exist? ).and_return( true ) + expect(numpath_obj).to receive( :read ).and_return( "1723:123123123" ) + + expect(@list.message_count).to eq(1723) end - + it "can return the count of archived posts to a list that hasn't been posted to" do - numpath_obj = mock( "num file path object" ) - @listpath.should_receive( :+ ).with( 'num' ).and_return( numpath_obj ) - - numpath_obj.should_receive( :exist? ).and_return( false ) - - @list.message_count.should == 0 + numpath_obj = double( "num file path object" ) + expect(@listpath).to receive( :+ ).with( 'num' ).and_return( numpath_obj ) + + expect(numpath_obj).to receive( :exist? ).and_return( false ) + + expect(@list.message_count).to eq(0) end - + - + TEST_ARCHIVE_DIR = TEST_LISTDIR + 'archive' TEST_ARCHIVE_SUBDIRS = %w[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ] TEST_POST_FILES = %w[ 00 01 02 03 04 05 06 07 08 09 10 11 12 13 ] @@ -435,121 +419,121 @@ TEST_ARCHIVE_DIR + TEST_ARCHIVE_SUBDIRS.last + pn } end - + it "can return a TMail::Mail object parsed from the last archived post" do # need to find the last message - archive_path_obj = mock( "archive path" ) + archive_path_obj = double( "archive path" ) - @listpath.should_receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) - archive_path_obj.should_receive( :exist? ).and_return( true ) + expect(@listpath).to receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) + expect(archive_path_obj).to receive( :exist? ).and_return( true ) # Find the last numbered directory under the archive dir - archive_path_obj.should_receive( :+ ).with( '[0-9]*' ). + expect(archive_path_obj).to receive( :+ ).with( '[0-9]*' ). and_return( :archive_dir_globpath ) - Pathname.should_receive( :glob ).with( :archive_dir_globpath ). + expect(Pathname).to receive( :glob ).with( :archive_dir_globpath ). and_return( @archive_subdir_paths ) # Find the last numbered file under the last numbered directory we found # above. - @archive_subdir_paths.last.should_receive( :+ ).with( '[0-9]*' ). + expect(@archive_subdir_paths.last).to receive( :+ ).with( '[0-9]*' ). and_return( :archive_post_pathglob ) - Pathname.should_receive( :glob ).with( :archive_post_pathglob ). + expect(Pathname).to receive( :glob ).with( :archive_post_pathglob ). and_return( @archive_post_paths ) - TMail::Mail.should_receive( :load ).with( @archive_post_paths.last.to_s ). + expect(TMail::Mail).to receive( :load ).with( @archive_post_paths.last.to_s ). and_return( :mail_object ) - @list.last_post.should == :mail_object + expect(@list.last_post).to eq(:mail_object) end - - + + it "returns nil for the last post if there is no archive directory for the list" do - archive_path_obj = mock( "archive path" ) + archive_path_obj = double( "archive path" ) - @listpath.should_receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) - archive_path_obj.should_receive( :exist? ).and_return( false ) - @list.last_post.should == nil + expect(@listpath).to receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) + expect(archive_path_obj).to receive( :exist? ).and_return( false ) + expect(@list.last_post).to eq(nil) end - - + + it "returns nil for the last post if there haven't been any posts to the list" do - archive_path_obj = mock( "archive path" ) - mail_object = mock( "Mock TMail object" ) + archive_path_obj = double( "archive path" ) + mail_object = double( "Mock TMail object" ) - @listpath.should_receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) - archive_path_obj.should_receive( :exist? ).and_return( true ) + expect(@listpath).to receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) + expect(archive_path_obj).to receive( :exist? ).and_return( true ) # Find the last numbered directory under the archive dir - archive_path_obj.should_receive( :+ ).with( '[0-9]*' ). + expect(archive_path_obj).to receive( :+ ).with( '[0-9]*' ). and_return( :archive_dir_globpath ) - Pathname.should_receive( :glob ).with( :archive_dir_globpath ).and_return( [] ) + expect(Pathname).to receive( :glob ).with( :archive_dir_globpath ).and_return( [] ) - @list.last_post.should == nil + expect(@list.last_post).to eq(nil) end - - + + it "raises a RuntimeError if the last archive directory doesn't have any messages in it" do - archive_path_obj = mock( "archive path" ) - mail_object = mock( "Mock TMail object" ) + archive_path_obj = double( "archive path" ) + mail_object = double( "Mock TMail object" ) - @listpath.should_receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) - archive_path_obj.should_receive( :exist? ).and_return( true ) + expect(@listpath).to receive( :+ ).with( 'archive' ).and_return( archive_path_obj ) + expect(archive_path_obj).to receive( :exist? ).and_return( true ) # Find the last numbered directory under the archive dir - archive_path_obj.should_receive( :+ ).with( '[0-9]*' ). + expect(archive_path_obj).to receive( :+ ).with( '[0-9]*' ). and_return( :archive_dir_globpath ) - Pathname.should_receive( :glob ).with( :archive_dir_globpath ). + expect(Pathname).to receive( :glob ).with( :archive_dir_globpath ). and_return( @archive_subdir_paths ) - @archive_subdir_paths.last.should_receive( :+ ).with( '[0-9]*' ). + expect(@archive_subdir_paths.last).to receive( :+ ).with( '[0-9]*' ). and_return( :archive_post_pathglob ) - Pathname.should_receive( :glob ).with( :archive_post_pathglob ). + expect(Pathname).to receive( :glob ).with( :archive_post_pathglob ). and_return( [] ) - lambda { + expect { @list.last_post - }.should raise_error( RuntimeError, /unexpectedly empty/i ) + }.to raise_error( RuntimeError, /unexpectedly empty/i ) end - - + + it "can fetch the date of the last archived post" do - mail_object = mock( "Mock TMail object" ) + mail_object = double( "Mock TMail object" ) - @list.should_receive( :last_post ).and_return( mail_object ) - mail_object.should_receive( :date ).and_return( :the_message_date ) + expect(@list).to receive( :last_post ).and_return( mail_object ) + expect(mail_object).to receive( :date ).and_return( :the_message_date ) - @list.last_message_date.should == :the_message_date + expect(@list.last_message_date).to eq(:the_message_date) end - + it "can fetch the date of the last archived post" do - mail_object = mock( "Mock TMail object" ) + mail_object = double( "Mock TMail object" ) - @list.should_receive( :last_post ).and_return( mail_object ) - mail_object.should_receive( :date ).and_return( :the_message_date ) + expect(@list).to receive( :last_post ).and_return( mail_object ) + expect(mail_object).to receive( :date ).and_return( :the_message_date ) - @list.last_message_date.should == :the_message_date + expect(@list.last_message_date).to eq(:the_message_date) end - + it "can fetch the author of the last archived post" do - mail_object = mock( "Mock TMail object" ) + mail_object = double( "Mock TMail object" ) - @list.should_receive( :last_post ).and_return( mail_object ) - mail_object.should_receive( :from ).and_return( :the_message_author ) + expect(@list).to receive( :last_post ).and_return( mail_object ) + expect(mail_object).to receive( :from ).and_return( :the_message_author ) - @list.last_message_author.should == :the_message_author + expect(@list.last_message_author).to eq(:the_message_author) end - + it "can fetch the subject of the last archived post" do - mail_object = mock( "Mock TMail object" ) + mail_object = double( "Mock TMail object" ) - @list.should_receive( :last_post ).and_return( mail_object ) - mail_object.should_receive( :from ).and_return( :the_message_author ) + expect(@list).to receive( :last_post ).and_return( mail_object ) + expect(mail_object).to receive( :from ).and_return( :the_message_author ) - @list.last_message_author.should == :the_message_author + expect(@list.last_message_author).to eq(:the_message_author) end end @@ -557,8 +541,8 @@ it "can fetch the body of an archived post by message id" it "can fetch the header of an archived post by message id" - - it "can return a hash of the subjects of all archived posts to message ids" + + it "can return a hash of the subjects of all archived posts to message ids" it "can return an Array of the subjects of all archived posts" it "can return a hash of the threads of all archived posts to message ids" diff -r 7fc2d1713795 -r 3cc813140c80 spec/ezmlm/listdaemon_spec.rb --- a/spec/ezmlm/listdaemon_spec.rb Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -#!/usr/bin/env ruby - -BEGIN { - require 'pathname' - basedir = Pathname.new( __FILE__ ).dirname.parent.parent - - libdir = basedir + "lib" - - $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir ) -} - - -begin - require 'ostruct' - require 'spec/runner' - require 'spec/lib/helpers' - require 'ezmlm/listdaemon' -rescue LoadError - unless Object.const_defined?( :Gem ) - require 'rubygems' - retry - end - raise -end - - -describe Ezmlm::ListDaemon do - include Ezmlm::SpecHelpers - - - DEFAULT_ADDRESS = Ezmlm::ListDaemon::DEFAULT_ADDRESS - DEFAULT_PORT = Ezmlm::ListDaemon::DEFAULT_PORT - - - it "can return a struct that contains its default options" do - opts = Ezmlm::ListDaemon.default_options - - opts.should be_an_instance_of( OpenStruct ) - opts.bind_addr.should == DEFAULT_ADDRESS - opts.bind_port.should == DEFAULT_PORT - opts.debugmode.should == false - opts.helpmode.should == false - end - - describe "created with defaults" do - - DEFAULT_URL = "druby://%s:%d" % [ DEFAULT_ADDRESS, DEFAULT_PORT ] - - before( :each ) do - @test_list_dir = Pathname.new( 'lists' ) - @daemon = Ezmlm::ListDaemon.new( @test_list_dir ) - end - - - it "can be started and will return a thread" do - mock_drb_thread = mock( "drb thread" ) - - DRb.should_receive( :start_service ).with( DEFAULT_URL, @daemon.service ) - DRb.should_receive( :thread ).and_return( mock_drb_thread ) - - @daemon.start.should == mock_drb_thread - end - end - - - describe "created with an options struct" do - - TEST_ADDRESS = '0.0.0.0' - TEST_PORT = 17771 - TEST_URL = "druby://%s:%d" % [ TEST_ADDRESS, TEST_PORT ] - - before( :each ) do - @test_list_dir = Pathname.new( 'lists' ) - - @opts = Ezmlm::ListDaemon.default_options - @opts.bind_addr = TEST_ADDRESS - @opts.bind_port = TEST_PORT - - @daemon = Ezmlm::ListDaemon.new( @test_list_dir, @opts ) - end - - - it "can be started and will return a thread" do - mock_drb_thread = mock( "drb thread" ) - - DRb.should_receive( :start_service ).with( TEST_URL, @daemon.service ) - DRb.should_receive( :thread ).and_return( mock_drb_thread ) - - @daemon.start.should == mock_drb_thread - end - end - -end - - -describe Ezmlm::ListDaemon::Service do - - before( :each ) do - @dummydir = 'lists' - @service = Ezmlm::ListDaemon::Service.new( @dummydir ) - end - - - it "can return a list object by name if there is a corresponding listdir" do - @service.get_list( 'announce' ).should be_an_instance_of( Ezmlm::List ) - end - - it "raises an exception when asked for a list whose name contains invalid characters" do - lambda { - @service.get_list( 'glarg beegun' ) - }.should raise_error( ArgumentError ) - end - - it "can iterate over listdirs, yielding each as a Ezmlm::List object" do - Ezmlm.should_receive( :each_list ).with( Pathname.new(@dummydir) ).and_yield( :a_list ) - @service.each_list {|l| l.should == :a_list } - end - -end - -# listservice = DRbObject.new( nil, 'druby://lists.laika.com:23431' ) -# announce = listservice.each_list do |list| -# last_posts << list.last_post -# end -# announce = listservice.get_list( 'announce' ) -# -# announce.last_post -# -# diff -r 7fc2d1713795 -r 3cc813140c80 spec/ezmlm_spec.rb --- a/spec/ezmlm_spec.rb Fri Sep 05 23:58:48 2008 +0000 +++ b/spec/ezmlm_spec.rb Wed Feb 01 15:35:35 2017 -0800 @@ -3,66 +3,50 @@ BEGIN { require 'pathname' basedir = Pathname.new( __FILE__ ).dirname.parent - libdir = basedir + "lib" - $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir ) } -begin - require 'spec/runner' - require 'spec/lib/helpers' - require 'ezmlm' -rescue LoadError - unless Object.const_defined?( :Gem ) - require 'rubygems' - retry - end - raise -end - +require_relative 'spec_helpers' +require 'ezmlm' describe Ezmlm do - include Ezmlm::SpecHelpers - - TEST_LISTSDIR = '/tmp/lists' it "can fetch a list of all mailing list subdirectories beneath a given directory" do - file_entry = mock( "plain file" ) - file_entry.should_receive( :directory? ).and_return( false ) + file_entry = double( "plain file" ) + expect( file_entry ).to receive( :directory? ).and_return( false ) - nonexistant_mlentry = stub( "mailinglist path that doesn't exist", :exist? => false ) - nonml_dir_entry = stub( "directory with no mailinglist file", - :directory? => true, :+ => nonexistant_mlentry ) + nonexistant_mlentry = double( "mailinglist path that doesn't exist", :exist? => false ) + nonml_dir_entry = double( "directory with no mailinglist file", + :directory? => true, :+ => nonexistant_mlentry ) - existant_mlentry = stub( "mailinglist path that does exist", :exist? => true ) - ml_dir_entry = stub( "directory with a mailinglist file", :directory? => true, :+ => existant_mlentry ) - - Pathname.should_receive( :glob ).with( an_instance_of(Pathname) ). + existant_mlentry = double( "mailinglist path that does exist", :exist? => true ) + ml_dir_entry = double( "directory with a mailinglist file", :directory? => true, :+ => existant_mlentry ) + + expect( Pathname ).to receive( :glob ).with( an_instance_of(Pathname) ). and_return([ file_entry, nonml_dir_entry, ml_dir_entry ]) dirs = Ezmlm.find_directories( TEST_LISTSDIR ) - - dirs.should have(1).member - dirs.should include( ml_dir_entry ) + + expect( dirs.size ).to eq( 1 ) + expect( dirs ).to include( ml_dir_entry ) end - + it "can iterate over all mailing lists in a specified directory" do - Ezmlm.should_receive( :find_directories ).with( TEST_LISTSDIR ).and_return([ :listdir1, :listdir2 ]) + expect( Ezmlm ).to receive( :find_directories ).with( TEST_LISTSDIR ).and_return([ :listdir1, :listdir2 ]) - Ezmlm::List.should_receive( :new ).with( :listdir1 ).and_return( :listobject1 ) - Ezmlm::List.should_receive( :new ).with( :listdir2 ).and_return( :listobject2 ) - + expect( Ezmlm::List ).to receive( :new ).with( :listdir1 ).and_return( :listobject1 ) + expect( Ezmlm::List ).to receive( :new ).with( :listdir2 ).and_return( :listobject2 ) + lists = [] Ezmlm.each_list( TEST_LISTSDIR ) do |list| lists << list end - - lists.should have(2).members - lists.should include( :listobject1, :listobject2 ) + + expect( lists.size ).to eq(2) + expect( lists ).to include( :listobject1, :listobject2 ) end - end diff -r 7fc2d1713795 -r 3cc813140c80 spec/lib/helpers.rb --- a/spec/lib/helpers.rb Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ -#!/usr/bin/ruby - -begin - require 'ezmlm' -rescue LoadError - unless Object.const_defined?( :Gem ) - require 'rubygems' - retry - end - raise -end - -module Ezmlm::SpecHelpers - - ############### - module_function - ############### - - ### Create a temporary working directory and return - ### a Pathname object for it. - ### - def make_tempdir - dirname = "%s.%d.%0.4f" % [ - 'ezmlm_spec', - Process.pid, - (Time.now.to_f % 3600), - ] - tempdir = Pathname.new( Dir.tmpdir ) + dirname - tempdir.mkpath - - return tempdir - end - - -end - -# Override the badly-formatted output of the RSpec HTML formatter -require 'spec/runner/formatter/html_formatter' - -class Spec::Runner::Formatter::HtmlFormatter - def example_failed( example, counter, failure ) - failure_style = failure.pending_fixed? ? 'pending_fixed' : 'failed' - - unless @header_red - @output.puts " " - @header_red = true - end - - unless @example_group_red - css_class = 'example_group_%d' % [current_example_group_number] - @output.puts " " - @example_group_red = true - end - - move_progress() - - @output.puts "
", - " #{h(example.description)}", - "
" - if failure.exception - backtrace = format_backtrace( failure.exception.backtrace ) - message = failure.exception.message - - @output.puts "
#{h message}
", - "
#{backtrace}
" - end - - if extra = extra_failure_content( failure ) - @output.puts( extra ) - end - - @output.puts "
", - "
" - @output.flush - end - - - alias_method :default_global_styles, :global_styles - - def global_styles - css = default_global_styles() - css << %Q{ - /* Stuff added by #{__FILE__} */ - - /* Overrides */ - #rspec-header { - -webkit-box-shadow: #333 0 2px 5px; - margin-bottom: 1em; - } - - .example_group dt { - -webkit-box-shadow: #333 0 2px 3px; - } - - /* Style for log output */ - dd.log-message { - background: #eee; - padding: 0 2em; - margin: 0.2em 1em; - border-bottom: 1px dotted #999; - border-top: 1px dotted #999; - text-indent: -1em; - } - - /* Parts of the message */ - dd.log-message .log-time { - font-weight: bold; - } - dd.log-message .log-time:after { - content: ": "; - } - dd.log-message .log-level { - font-variant: small-caps; - border: 1px solid #ccc; - padding: 1px 2px; - } - dd.log-message .log-name { - font-size: 1.2em; - color: #1e51b2; - } - dd.log-message .log-name:before { content: "«"; } - dd.log-message .log-name:after { content: "»"; } - - dd.log-message .log-message-text { - padding-left: 4px; - font-family: Monaco, "Andale Mono", "Vera Sans Mono", mono; - } - - - /* Distinguish levels */ - dd.log-message.debug { color: #666; } - dd.log-message.info {} - - dd.log-message.warn, - dd.log-message.error { - background: #ff9; - } - dd.log-message.error .log-level, - dd.log-message.error .log-message-text { - color: #900; - } - dd.log-message.fatal { - background: #900; - color: white; - font-weight: bold; - border: 0; - } - dd.log-message.fatal .log-name { - color: white; - } - } - - return css - end -end - - diff -r 7fc2d1713795 -r 3cc813140c80 spec/spec_helpers.rb --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/spec_helpers.rb Wed Feb 01 15:35:35 2017 -0800 @@ -0,0 +1,45 @@ +#!/usr/bin/ruby + +require 'simplecov' if ENV['COVERAGE'] +require 'rspec' +require 'loggability/spechelpers' + +module SpecHelpers + + TEST_LISTSDIR = ENV['TEST_LISTSDIR'] || '/tmp/lists' + + ############### + module_function + ############### + + ### Create a temporary working directory and return + ### a Pathname object for it. + ### + def make_tempdir + dirname = "%s.%d.%0.4f" % [ + 'ezmlm_spec', + Process.pid, + (Time.now.to_f % 3600), + ] + tempdir = Pathname.new( Dir.tmpdir ) + dirname + tempdir.mkpath + + return tempdir + end +end + + +RSpec.configure do |config| + include SpecHelpers + + config.run_all_when_everything_filtered = true + config.filter_run :focus + config.order = 'random' + config.mock_with( :rspec ) do |mock| + mock.syntax = :expect + end + + config.include( SpecHelpers ) +end + + diff -r 7fc2d1713795 -r 3cc813140c80 utils.rb --- a/utils.rb Fri Sep 05 23:58:48 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,800 +0,0 @@ -# -# Install/distribution utility functions -# $Id$ -# -# Copyright (c) 2001-2008, The FaerieMUD Consortium. -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, this -# list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# -# * Neither the name of LAIKA, nor the names of its contributors may be used to -# endorse or promote products derived from this software without specific prior -# written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -BEGIN { - require 'rbconfig' - require 'uri' - require 'find' - require 'pp' - require 'irb' - - begin - require 'readline' - include Readline - rescue LoadError => e - $stderr.puts "Faking readline..." - def readline( prompt ) - $stderr.print prompt.chomp - return $stdin.gets.chomp - end - end - -} - - -### Command-line utility functions -module UtilityFunctions - include Config - - # The list of regexen that eliminate files from the MANIFEST - ANTIMANIFEST = [ - /makedist\.rb/, - /\bCVS\b/, - /~$/, - /^#/, - %r{docs/html}, - %r{docs/man}, - /\bTEMPLATE\.\w+\.tpl\b/, - /\.cvsignore/, - /\.s?o$/, - ] - - # Set some ANSI escape code constants (Shamelessly stolen from Perl's - # Term::ANSIColor by Russ Allbery and Zenin - AnsiAttributes = { - 'clear' => 0, - 'reset' => 0, - 'bold' => 1, - 'dark' => 2, - 'underline' => 4, - 'underscore' => 4, - 'blink' => 5, - 'reverse' => 7, - 'concealed' => 8, - - 'black' => 30, 'on_black' => 40, - 'red' => 31, 'on_red' => 41, - 'green' => 32, 'on_green' => 42, - 'yellow' => 33, 'on_yellow' => 43, - 'blue' => 34, 'on_blue' => 44, - 'magenta' => 35, 'on_magenta' => 45, - 'cyan' => 36, 'on_cyan' => 46, - 'white' => 37, 'on_white' => 47 - } - - ErasePreviousLine = "\033[A\033[K" - - ManifestHeader = (<<-"EOF").gsub( /^\t+/, '' ) - # - # Distribution Manifest - # Created: #{Time::now.to_s} - # - - EOF - - ############### - module_function - ############### - - # Create a string that contains the ANSI codes specified and return it - def ansi_code( *attributes ) - attributes.flatten! - # $stderr.puts "Returning ansicode for TERM = %p: %p" % - # [ ENV['TERM'], attributes ] - return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM'] - attributes = AnsiAttributes.values_at( *attributes ).compact.join(';') - - # $stderr.puts " attr is: %p" % [attributes] - if attributes.empty? - return '' - else - return "\e[%sm" % attributes - end - end - - - ### Colorize the given +string+ with the specified +attributes+ and return it, handling line-endings, etc. - def colorize( string, *attributes ) - ending = string[/(\s)$/] || '' - string = string.rstrip - return ansi_code( attributes.flatten ) + string + ansi_code( 'reset' ) + ending - end - - - # Test for the presence of the specified library, and output a - # message describing the test using nicename. If nicename - # is nil, the value in library is used to build a default. - def test_for_library( library, nicename=nil, progress=false ) - nicename ||= library - message( "Testing for the #{nicename} library..." ) if progress - if $LOAD_PATH.detect {|dir| - File.exists?(File.join(dir,"#{library}.rb")) || - File.exists?(File.join(dir,"#{library}.#{CONFIG['DLEXT']}")) - } - message( "found.\n" ) if progress - return true - else - message( "not found.\n" ) if progress - return false - end - end - - # Test for the presence of the specified library, and output a - # message describing the problem using nicename. If - # nicename is nil, the value in library is used - # to build a default. If raaUrl and/or downloadUrl are - # specified, they are also use to build a message describing how to find the - # required library. If fatal is true, a missing library - # will cause the program to abort. - def test_for_required_library( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true ) - nicename ||= library - unless test_for_library( library, nicename ) - msgs = [ "You are missing the required #{nicename} library.\n" ] - msgs << "RAA: #{raaUrl}\n" if raaUrl - msgs << "Download: #{downloadUrl}\n" if downloadUrl - if fatal - abort msgs.join('') - else - error_message msgs.join('') - end - end - return true - end - - ### Output msg as a ANSI-colored program/section header (white on - ### blue). - def header( msg ) - msg.chomp! - $stderr.puts ansi_code( 'bold', 'white', 'on_blue' ) + msg + ansi_code( 'reset' ) - $stderr.flush - end - - ### Output msg to STDERR and flush it. - def message( *msgs ) - $stderr.print( msgs.join("\n") ) - $stderr.flush - end - - ### Output +msg+ to STDERR and flush it if $VERBOSE is true. - def verbose_msg( msg ) - msg.chomp! - message( msg + "\n" ) if $VERBOSE - end - - ### Output the specified msg as an ANSI-colored error message - ### (white on red). - def error_msg( msg ) - message ansi_code( 'bold', 'white', 'on_red' ) + msg + ansi_code( 'reset' ) - end - alias :error_message :error_msg - - ### Output the specified msg as an ANSI-colored debugging message - ### (yellow on blue). - def debug_msg( msg ) - return unless $DEBUG - msg.chomp! - $stderr.puts ansi_code( 'yellow' ) + ">>> #{msg}" + ansi_code( 'reset' ) - $stderr.flush - end - - ### Erase the previous line (if supported by your terminal) and output the - ### specified msg instead. - def replace_msg( msg ) - $stderr.puts - $stderr.print ErasePreviousLine - message( msg ) - end - alias :replace_message :replace_msg - - ### Output a divider made up of length hyphen characters. - def divider( length=75 ) - $stderr.puts "\r" + ("-" * length ) - end - alias :writeLine :divider - - - ### Output the specified msg colored in ANSI red and exit with a - ### status of 1. - def abort( msg ) - print ansi_code( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansi_code( 'reset' ) + "\n\n" - Kernel.exit!( 1 ) - end - - - ### Output the specified prompt_string as a prompt (in green) and - ### return the user's input with leading and trailing spaces removed. If a - ### test is provided, the prompt will repeat until the test returns true. - ### An optional failure message can also be passed in. - def prompt( prompt_string, failure_msg="Try again." ) # :yields: response - prompt_string.chomp! - prompt_string << ":" unless /\W$/.match( prompt_string ) - response = nil - - begin - response = readline( ansi_code('bold', 'green') + - "#{prompt_string} " + ansi_code('reset') ) || '' - response.strip! - if block_given? && ! yield( response ) - error_message( failure_msg + "\n\n" ) - response = nil - end - end until response - - return response - end - - - ### Prompt the user with the given prompt_string via #prompt, - ### substituting the given default if the user doesn't input - ### anything. If a test is provided, the prompt will repeat until the test - ### returns true. An optional failure message can also be passed in. - def prompt_with_default( prompt_string, default, failure_msg="Try again." ) - response = nil - - begin - response = prompt( "%s [%s]" % [ prompt_string, default ] ) - response = default if response.empty? - - if block_given? && ! yield( response ) - error_message( failure_msg + "\n\n" ) - response = nil - end - end until response - - return response - end - - - $programs = {} - - ### Search for the program specified by the given progname in the - ### user's PATH, and return the full path to it, or nil if - ### no such program is in the path. - def find_program( progname ) - unless $programs.key?( progname ) - ENV['PATH'].split(File::PATH_SEPARATOR).each {|d| - file = File.join( d, progname ) - if File.executable?( file ) - $programs[ progname ] = file - break - end - } - end - - return $programs[ progname ] - end - - - ### Search for the release version for the project in the specified - ### +directory+. - def extract_version( directory='.' ) - release = nil - - Dir::chdir( directory ) do - if File::directory?( "CVS" ) - verbose_msg( "Project is versioned via CVS. Searching for RELEASE_*_* tags..." ) - - if (( cvs = find_program('cvs') )) - revs = [] - output = %x{cvs log} - output.scan( /RELEASE_(\d+(?:_\d\w+)*)/ ) {|match| - rev = $1.split(/_/).collect {|s| Integer(s) rescue 0} - verbose_msg( "Found %s...\n" % rev.join('.') ) - revs << rev - } - - release = revs.sort.last - end - - elsif File::directory?( '.svn' ) - verbose_msg( "Project is versioned via Subversion" ) - - if (( svn = find_program('svn') )) - output = %x{svn pg project-version}.chomp - unless output.empty? - verbose_msg( "Using 'project-version' property: %p" % output ) - release = output.split( /[._]/ ).collect {|s| Integer(s) rescue 0} - end - end - end - end - - return release - end - - - ### Find the current release version for the project in the specified - ### +directory+ and return its successor. - def extract_next_version( directory='.' ) - version = extract_version( directory ) || [0,0,0] - version.compact! - version[-1] += 1 - - return version - end - - - # Pattern for extracting the name of the project from a Subversion URL - SVNUrlPath = %r{ - .*/ # Skip all but the last bit - ([^/]+) # $1 = project name - / # Followed by / + - (?: - trunk | # 'trunk' - ( - branches | # ...or branches/branch-name - tags # ...or tags/tag-name - )/\w - ) - $ # bound to the end - }ix - - ### Extract the project name (CVS Repository name) for the given +directory+. - def extract_project_name( directory='.' ) - name = nil - - Dir::chdir( directory ) do - - # CVS-controlled - if File::directory?( "CVS" ) - verbose_msg( "Project is versioned via CVS. Using repository name." ) - name = File.open( "CVS/Repository", "r").readline.chomp - name.sub!( %r{.*/}, '' ) - - # Subversion-controlled - elsif File::directory?( '.svn' ) - verbose_msg( "Project is versioned via Subversion" ) - - # If the machine has the svn tool, try to get the project name - if (( svn = find_program( 'svn' ) )) - - # First try an explicit property - output = shell_command( svn, 'pg', 'project-name' ) - if !output.empty? - verbose_msg( "Using 'project-name' property: %p" % output ) - name = output.first.chomp - - # If that doesn't work, try to figure it out from the URL - elsif (( uri = get_svn_uri() )) - name = uri.path.sub( SVNUrlPath ) { $1 } - end - end - end - - # Fall back to guessing based on the directory name - unless name - name = File::basename(File::dirname( File::expand_path(__FILE__) )) - end - end - - return name - end - - - ### Extract the Subversion URL from the specified directory and return it as - ### a URI object. - def get_svn_uri( directory='.' ) - uri = nil - - Dir::chdir( directory ) do - output = %x{svn info} - debug_msg( "Using info: %p" % output ) - - if /^URL: \s* ( .* )/xi.match( output ) - uri = URI::parse( $1 ) - end - end - - return uri - end - - - ### (Re)make a manifest file in the specified +path+. - def make_manifest( path="MANIFEST" ) - if File::exists?( path ) - reply = prompt_with_default( "Replace current '#{path}'? [yN]", "n" ) - return false unless /^y/i.match( reply ) - - verbose_msg "Replacing manifest at '#{path}'" - else - verbose_msg "Creating new manifest at '#{path}'" - end - - files = [] - verbose_msg( "Finding files...\n" ) - Find::find( Dir::pwd ) do |f| - Find::prune if File::directory?( f ) && - /^\./.match( File::basename(f) ) - verbose_msg( " found: #{f}\n" ) - files << f.sub( %r{^#{Dir::pwd}/?}, '' ) - end - files = vet_manifest( files ) - - verbose_msg( "Writing new manifest to #{path}..." ) - File::open( path, File::WRONLY|File::CREAT|File::TRUNC ) do |ofh| - ofh.puts( ManifestHeader ) - ofh.puts( files ) - end - verbose_msg( "done." ) - end - - - ### Read the specified manifestFile, which is a text file - ### describing which files to package up for a distribution. The manifest - ### should consist of one or more lines, each containing one filename or - ### shell glob pattern. - def read_manifest( manifestFile="MANIFEST" ) - verbose_msg "Building manifest..." - raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile - - manifest = IO::readlines( manifestFile ).collect {|line| - line.chomp - }.select {|line| - line !~ /^(\s*(#.*)?)?$/ - } - - filelist = [] - for pat in manifest - verbose_msg "Adding files that match '#{pat}' to the file list" - filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)} - end - - verbose_msg "found #{filelist.length} files.\n" - return filelist - end - - - ### Given a filelist like that returned by #read_manifest, remove - ### the entries therein which match the Regexp objects in the given - ### antimanifest and return the resultant Array. - def vet_manifest( filelist, antimanifest=ANTIMANIFEST ) - origLength = filelist.length - verbose_msg "Vetting manifest..." - - for regex in antimanifest - verbose_msg "\n\tPattern /#{regex.source}/ removed: " + - filelist.find_all {|file| regex.match(file)}.join(', ') - filelist.delete_if {|file| regex.match(file)} - end - - verbose_msg "removed #{origLength - filelist.length} files from the list.\n" - return filelist - end - - - ### Combine a call to #read_manifest with one to #vet_manifest. - def get_vetted_manifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST ) - vet_manifest( read_manifest(manifestFile), antimanifest ) - end - - - ### Given a documentation catalogFile, extract the title, if - ### available, and return it. Otherwise generate a title from the name of - ### the CVS module. - def find_rdoc_title( catalogFile="docs/CATALOG" ) - - # Try extracting it from the CATALOG file from a line that looks like: - # Title: Foo Bar Module - title = find_catalog_keyword( 'title', catalogFile ) - - # If that doesn't work for some reason, use the name of the project. - title = extract_project_name() - - return title - end - - - ### Given a documentation catalogFile, extract the name of the file - ### to use as the initally displayed page. If extraction fails, the - ### +default+ will be used if it exists. Returns +nil+ if there is no main - ### file to be found. - def find_rdoc_main( catalogFile="docs/CATALOG", default="README" ) - - # Try extracting it from the CATALOG file from a line that looks like: - # Main: Foo Bar Module - main = find_catalog_keyword( 'main', catalogFile ) - - # Try to make some educated guesses if that doesn't work - if main.nil? - basedir = File::dirname( __FILE__ ) - basedir = File::dirname( basedir ) if /docs$/ =~ basedir - - if File::exists?( File::join(basedir, default) ) - main = default - end - end - - return main - end - - - ### Given a documentation catalogFile, extract an upload URL for - ### RDoc. - def find_rdoc_upload( catalogFile="docs/CATALOG" ) - find_catalog_keyword( 'upload', catalogFile ) - end - - - ### Given a documentation catalogFile, extract a CVS web frontend - ### URL for RDoc. - def find_rdoc_cvs_url( catalogFile="docs/CATALOG" ) - find_catalog_keyword( 'webcvs', catalogFile ) - end - - - ### Find one or more 'accessor' directives in the catalog if they exist and - ### return an Array of them. - def find_rdoc_accessors( catalogFile="docs/CATALOG" ) - accessors = [] - in_attr_section = false - indent = '' - - if File::exists?( catalogFile ) - verbose_msg "Extracting accessors from CATALOG file (%s).\n" % catalogFile - - # Read lines from the catalog - File::foreach( catalogFile ) do |line| - debug_msg( " Examining line #{line.inspect}..." ) - - # Multi-line accessors - if in_attr_section - if /^#\s+([a-z0-9_]+(?:\s*=\s*.*)?)$/i.match( line ) - debug_msg( " Found accessor: #$1" ) - accessors << $1 - next - end - - debug_msg( " End of accessors section." ) - in_attr_section = false - - # Single-line accessor - elsif /^#\s*Accessors:\s*(\S+)$/i.match( line ) - debug_msg( " Found single accessors line: #$1" ) - vals = $1.split(/,/).collect {|val| val.strip } - accessors.replace( vals ) - - # Multi-line accessor header - elsif /^#\s*Accessors:\s*$/i.match( line ) - debug_msg( " Start of accessors section." ) - in_attr_section = true - end - - end - end - - debug_msg( "Found accessors: %s" % accessors.join(",") ) - return accessors - end - - - ### Given a documentation catalogFile, try extracting the given - ### +keyword+'s value from it. Keywords are lines that look like: - ### # : - ### Returns +nil+ if the catalog file was unreadable or didn't contain the - ### specified +keyword+. - def find_catalog_keyword( keyword, catalogFile="docs/CATALOG" ) - val = nil - - if File::exists? catalogFile - verbose_msg "Extracting '#{keyword}' from CATALOG file (%s).\n" % catalogFile - File::foreach( catalogFile ) do |line| - debug_msg( "Examining line #{line.inspect}..." ) - val = $1.strip and break if /^#\s*#{keyword}:\s*(.*)$/i.match( line ) - end - end - - return val - end - - - ### Given a documentation catalogFile, which is in the same format - ### as that described by #read_manifest, read and expand it, and then return - ### a list of those files which appear to have RDoc documentation in - ### them. If catalogFile is nil or does not exist, the MANIFEST - ### file is used instead. - def find_rdocable_files( catalogFile="docs/CATALOG" ) - startlist = [] - if File.exists? catalogFile - verbose_msg "Using CATALOG file (%s).\n" % catalogFile - startlist = get_vetted_manifest( catalogFile ) - else - verbose_msg "Using default MANIFEST\n" - startlist = get_vetted_manifest() - end - - verbose_msg "Looking for RDoc comments in:\n" - startlist.select {|fn| - verbose_msg " #{fn}: " - found = false - File::open( fn, "r" ) {|fh| - fh.each {|line| - if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*} - found = true - break - end - } - } - - verbose_msg( (found ? "yes" : "no") + "\n" ) - found - } - end - - - ### Open a file and filter each of its lines through the given block a - ### line at a time. The return value of the block is used as the - ### new line, or omitted if the block returns nil or - ### false. - def edit_in_place( file, testMode=false ) # :yields: line - raise "No block specified for editing operation" unless block_given? - - tempName = "#{file}.#{$$}" - File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile| - File::open( file, File::RDONLY ) {|fh| - fh.each {|line| - newline = yield( line ) or next - tempfile.print( newline ) - $stderr.puts "%p -> %p" % [ line, newline ] if - line != newline - } - } - } - - if testMode - File::unlink( tempName ) - else - File::rename( tempName, file ) - end - end - - - ### Execute the specified shell command, read the results, and - ### return them. Like a %x{} that returns an Array instead of a String. - def shell_command( *command ) - raise "Empty command" if command.empty? - - cmdpipe = IO::popen( command.join(' '), 'r' ) - return cmdpipe.readlines - end - - - ### Execute a block with $VERBOSE set to +false+, restoring it to its - ### previous value before returning. - def verbose_off - raise LocalJumpError, "No block given" unless block_given? - - thrcrit = Thread.critical - oldverbose = $VERBOSE - begin - Thread.critical = true - $VERBOSE = false - yield - ensure - $VERBOSE = oldverbose - Thread.critical = false - end - end - - - ### Try the specified code block, printing the given - def try( msg, bind=TOPLEVEL_BINDING ) - result = '' - if msg =~ /^to\s/ - message "Trying #{msg}...\n" - else - message msg + "\n" - end - - begin - rval = nil - if block_given? - rval = yield - else - file, line = caller(1)[0].split(/:/,2) - rval = eval( msg, bind, file, line.to_i ) - end - - PP.pp( rval, result ) - - rescue Exception => err - if err.backtrace - nicetrace = err.backtrace.delete_if {|frame| - /in `(try|eval)'/ =~ frame - }.join("\n\t") - else - nicetrace = "Exception had no backtrace" - end - - result = err.message + "\n\t" + nicetrace - - ensure - divider - message result.chomp + "\n" - divider - $stderr.puts - end - end - - - ### Start an IRB session with the specified binding +b+ as the current scope. - def start_irb_session( b ) - IRB.setup(nil) - - workspace = IRB::WorkSpace.new( b ) - - if IRB.conf[:SCRIPT] - irb = IRB::Irb.new( workspace, IRB.conf[:SCRIPT] ) - else - irb = IRB::Irb.new( workspace ) - end - - IRB.conf[:IRB_RC].call( irb.context ) if IRB.conf[:IRB_RC] - IRB.conf[:MAIN_CONTEXT] = irb.context - - trap("SIGINT") do - irb.signal_handle - end - - catch(:IRB_EXIT) do - irb.eval_input - end - end - -end # module UtilityFunctions - - - -if __FILE__ == $0 - # $DEBUG = true - include UtilityFunctions - - projname = extract_project_name() - header "Project: #{projname}" - - ver = extract_version() || [0,0,1] - puts "Version: %s\n" % ver.join('.') - - if File::directory?( "docs" ) - puts "Rdoc:", - " Title: " + find_rdoc_title(), - " Main: " + find_rdoc_main(), - " Upload: " + find_rdoc_upload(), - " SCCS URL: " + find_rdoc_cvs_url(), - " Accessors: " + find_rdoc_accessors().join(",") - end - - puts "Manifest:", - " " + get_vetted_manifest().join("\n ") -end