diff options
| author | Cédric Boutillier <boutil@debian.org> | 2014-10-07 00:37:59 +0200 |
|---|---|---|
| committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2014-10-07 04:21:55 +0000 |
| commit | 3c32335a4f58c21058fecc712a58bd1df5cfaa49 (patch) | |
| tree | c17366b4a5335abb02aa5806beb3b077848817d4 | |
| parent | ddec784f07c3841418fc1e97400f226b05105068 (diff) | |
| parent | 1b0cfc6b7c912c632521bc7c21f61d02076da590 (diff) | |
2.0.6-1 (patches applied)applied/2.0.6-1applied/ubuntu/zesty-develapplied/ubuntu/zestyapplied/ubuntu/yakkety-develapplied/ubuntu/yakketyapplied/ubuntu/xenial-develapplied/ubuntu/xenialapplied/ubuntu/wily-develapplied/ubuntu/wilyapplied/ubuntu/vivid-proposedapplied/ubuntu/vivid-develapplied/ubuntu/vividapplied/ubuntu/groovy-develapplied/ubuntu/groovyapplied/ubuntu/focal-develapplied/ubuntu/focalapplied/ubuntu/eoan-develapplied/ubuntu/eoanapplied/ubuntu/disco-develapplied/ubuntu/discoapplied/ubuntu/cosmic-develapplied/ubuntu/cosmicapplied/ubuntu/bionic-develapplied/ubuntu/bionicapplied/ubuntu/artful-develapplied/ubuntu/artfulapplied/debian/stretchapplied/debian/jessieapplied/debian/buster
Imported using git-ubuntu import.
| -rw-r--r-- | README | 55 | ||||
| -rw-r--r-- | README.md | 59 | ||||
| -rw-r--r-- | Rakefile | 218 | ||||
| -rw-r--r-- | TODO | 10 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | checksums.yaml.gz | bin | 0 -> 269 bytes | |||
| -rw-r--r-- | debian/changelog | 14 | ||||
| -rw-r--r-- | debian/control | 9 | ||||
| -rw-r--r-- | debian/copyright | 2 | ||||
| -rw-r--r-- | debian/ruby-cmdparse.docs | 7 | ||||
| -rw-r--r-- | debian/ruby-tests.rb | 8 | ||||
| -rw-r--r-- | doc/about.page (renamed from doc/src/about.page) | 20 | ||||
| -rw-r--r-- | doc/config.yaml | 2 | ||||
| -rw-r--r-- | doc/default.css (renamed from doc/src/default.css) | 3 | ||||
| -rw-r--r-- | doc/default.template (renamed from doc/src/default.template) | 11 | ||||
| -rw-r--r-- | doc/download.page | 34 | ||||
| -rw-r--r-- | doc/index.page | 107 | ||||
| -rw-r--r-- | doc/logo.png (renamed from doc/src/logo.png) | bin | 12456 -> 12456 bytes | |||
| -rw-r--r-- | doc/plugin/extract.rb | 29 | ||||
| -rw-r--r-- | doc/src/download.page | 34 | ||||
| -rw-r--r-- | doc/src/features.page | 11 | ||||
| -rw-r--r-- | doc/src/index.page | 80 | ||||
| -rw-r--r-- | doc/src/meta.info | 5 | ||||
| -rw-r--r-- | doc/src/tutorial.page | 177 | ||||
| -rw-r--r-- | doc/tutorial.page | 191 | ||||
| -rw-r--r-- | doc/virtual | 5 | ||||
| -rw-r--r-- | lib/cmdparse.rb | 4 | ||||
| -rw-r--r-- | lib/cmdparse/wrappers/optparse.rb | 2 | ||||
| -rw-r--r-- | metadata.yml | 58 | ||||
| -rw-r--r-- | net.rb | 38 | ||||
| -rw-r--r-- | setup.rb | 1468 | ||||
| -rw-r--r-- | test/tc_commandhash.rb | 96 |
32 files changed, 1533 insertions, 1226 deletions
@@ -1,55 +0,0 @@ -cmdparse - an advanced command line parser using optparse which has support for commands - -Copyright (C) 2004-2012 Thomas Leitner - -= Description - -Some new programs use a "command style" command line. Examples for such programs are the "svn" -program from Subversion and the "gem" program from Rubygems. The standard Ruby distribution has no -library to create programs that use such a command line interface. - -This library, cmdparse, can be used to create such a command line interface. Internally it uses -optparse or any other option parser library to parse options and it provides a nice API for -specifying commands. - -= License - -GNU LGPLv3 - see COPYING.LESSER for the LGPL and COPYING for the GPL - -= Dependencies - - none - -= Installation - -The preferred way of installing cmdparse is via RubyGems: - $ gem install cmdparse - -If you do not have RubyGems installed, but Rake, you can use the following command: - $ rake install - -If you have neither RubyGems nor Rake, use these commands: - $ ruby setup.rb config - $ ruby setup.rb setup - $ ruby setup.rb install - -= Documentation - -You can build the documentation by invoking - $ rake doc - -This builds the API and the additional documentation. The additional documentation needs webgen ->=0.3.5 (webgen.rubyforge.org) for building. - - -= Example - -There is an example of how to use cmdparse in the +net.rb+ file. - - -= Contact - -Author: Thomas Leitner -* Web: http://cmdparse.rubyforge.org -* e-Mail: t_leitner@gmx.at -* GPG Key-Id: 0xD942E7F6 diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d46451 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +**cmdparse** - an advanced command line parser using optparse which has support for commands + +Copyright (C) 2004-2014 Thomas Leitner + +## Description + +Some programs use a "command style" command line. Examples for such programs are the "svn" program +from Subversion and the "gem" program from Rubygems. The standard Ruby distribution has no library +to create programs that use such a command line interface. + +This library, cmdparse, can be used to create such a command line interface. Internally it uses +optparse or any other option parser library to parse options and it provides a nice API for +specifying commands. + +## License + +GNU LGPLv3 - see COPYING.LESSER for the LGPL and COPYING for the GPL + +## Dependencies + +none + +## Installation + +The preferred way of installing cmdparse is via RubyGems: + + $ gem install cmdparse + +If you do not have RubyGems installed, but Rake, you can use the following command: + + $ rake install + +If you have neither RubyGems nor Rake, use these commands: + + $ ruby setup.rb config + $ ruby setup.rb setup + $ ruby setup.rb install + +## Documentation + +You can build the documentation by invoking + + $ rake doc + +This builds the API and the additional documentation. The additional documentation needs webgen +>=1.0.0 (webgen.gettalong.org) for building. + + +## Example + +There is an example of how to use cmdparse in the `net.rb` file. + + +## Contact + +Author: Thomas Leitner + +* Web: <http://cmdparse.gettalong.org> +* e-Mail: <t_leitner@gmx.at> @@ -2,7 +2,7 @@ # #-- # cmdparse: advanced command line parser supporting commands -# Copyright (C) 2004-2010 Thomas Leitner +# Copyright (C) 2004-2014 Thomas Leitner # # This file is part of cmdparse. # @@ -19,18 +19,22 @@ # #++ - begin require 'rubygems' - require 'rake/gempackagetask' + require 'rubygems/package_task' rescue Exception nil end +begin + require 'webgen/page' +rescue LoadError +end + require 'rake/clean' require 'rake/packagetask' -require 'rake/rdoctask' require 'rake/testtask' +require 'rdoc/task' # General actions ############################################################## @@ -38,167 +42,133 @@ $:.unshift 'lib' require 'cmdparse' PKG_NAME = "cmdparse" -PKG_VERSION = CmdParse::VERSION.join( '.' ) +PKG_VERSION = CmdParse::VERSION.join('.') PKG_FULLNAME = PKG_NAME + "-" + PKG_VERSION -SRC_RB = FileList['lib/**/*.rb'] +# End user tasks ################################################################ # The default task is run if rake is given no explicit arguments. -desc "Default Task" +desc "Default Task (does testing)" task :default => :test - -# End user tasks ################################################################ - -desc "Prepares for installation" -task :prepare do +desc "Installs the package #{PKG_NAME} using setup.rb" +task :install do ruby "setup.rb config" ruby "setup.rb setup" -end - - -desc "Installs the package #{PKG_NAME}" -task :install => [:prepare] -task :install do ruby "setup.rb install" end - task :clean do ruby "setup.rb clean" end -task :test do - ruby "-Ilib test/tc_commandhash.rb" +Rake::TestTask.new do |test| + test.test_files = FileList['test/tc_*.rb'] end -CLOBBER << "doc/output" -desc "Builds the documentation" -task :doc => [:rdoc] do - chdir "doc" do +if defined?(Webgen) + CLOBBER << "htmldoc" + CLOBBER << "webgen-tmp" + desc "Builds the documentation" + task :htmldoc do sh "webgen" end end -rd = Rake::RDocTask.new do |rdoc| - rdoc.rdoc_dir = 'doc/output/rdoc' - rdoc.title = PKG_NAME - rdoc.options << '--line-numbers' << '--inline-source' << '-m CmdParse::CommandParser' - rdoc.rdoc_files.include( 'lib/**/*.rb' ) +if defined? RDoc::Task + RDoc::Task.new do |rdoc| + rdoc.rdoc_dir = 'htmldoc/rdoc' + rdoc.title = PKG_NAME + rdoc.main = 'CmdParse::CommandParser' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('lib') + end end +if defined?(Webgen) && defined?(RDoc::Task) + desc "Build the whole user documentation" + task :doc => [:rdoc, :htmldoc] +end # Developer tasks ############################################################## +namespace :dev do + + PKG_FILES = FileList.new( [ + 'setup.rb', + 'COPYING', + 'COPYING.LESSER', + 'README.md', + 'Rakefile', + 'net.rb', + 'VERSION', + 'lib/**/*.rb', + 'doc/**/*', + 'test/*' + ]) -PKG_FILES = FileList.new( [ - 'setup.rb', - 'TODO', - 'COPYING', - 'COPYING.LESSER', - 'README', - 'Rakefile', - 'net.rb', - 'VERSION', - 'lib/**/*.rb', - 'doc/**/*' - ]) do |fl| - fl.exclude( /\bsvn\b/ ) - fl.exclude( 'doc/output' ) -end + CLOBBER << "VERSION" + file 'VERSION' do + puts "Generating VERSION file" + File.open('VERSION', 'w+') {|file| file.write(PKG_VERSION + "\n")} + end -if !defined? Gem - puts "Package Target requires RubyGEMs" -else - spec = Gem::Specification.new do |s| + Rake::PackageTask.new('cmdparse', PKG_VERSION) do |pkg| + pkg.need_tar = true + pkg.need_zip = true + pkg.package_files = PKG_FILES + end - #### Basic information + if defined? Gem + spec = Gem::Specification.new do |s| - s.name = PKG_NAME - s.version = PKG_VERSION - s.summary = "Advanced command line parser supporting commands" - s.description = <<-EOF + #### Basic information + s.name = PKG_NAME + s.version = PKG_VERSION + s.summary = "Advanced command line parser supporting commands" + s.description = <<-EOF cmdparse provides classes for parsing commands on the command line; command line options are parsed using optparse or any other option parser implementation. Programs that use such command line interfaces are, for example, subversion's 'svn' or Rubygem's 'gem' program. - EOF - - #### Dependencies, requirements and files - - s.files = PKG_FILES.to_a - - s.require_path = 'lib' - s.autorequire = 'cmdparse' - - #### Documentation - - s.has_rdoc = true - s.extra_rdoc_files = rd.rdoc_files.reject do |fn| fn =~ /\.rb$/ end.to_a - s.rdoc_options = ['--line-numbers', '-m', 'CmdParse::CommandParser'] - - #### Author and project details - - s.author = "Thomas Leitner" - s.email = "t_leitner@gmx.at" - s.homepage = "http://cmdparse.rubyforge.org" - s.rubyforge_project = "cmdparse" - end - - task :package => [:generateFiles] - task :generateFiles do |t| - File.open('VERSION', 'w+') do |file| file.write( PKG_VERSION + "\n" ) end - end + EOF + s.license = 'LGPLv3' + + #### Dependencies, requirements and files + s.files = PKG_FILES.to_a + s.require_path = 'lib' + s.autorequire = 'cmdparse' + + #### Documentation + s.has_rdoc = true + s.rdoc_options = ['--line-numbers', '--main', 'CmdParse::CommandParser'] + + #### Author and project details + s.author = "Thomas Leitner" + s.email = "t_leitner@gmx.at" + s.homepage = "http://cmdparse.gettalong.org" + end - CLOBBER << "VERSION" + Gem::PackageTask.new(spec) do |pkg| + pkg.need_zip = true + pkg.need_tar = true + end - Rake::GemPackageTask.new( spec ) do |pkg| - pkg.need_zip = true - pkg.need_tar = true end -end - -desc "Upload documentation to homepage" -task :uploaddoc => [:doc] do - Dir.chdir('doc/output') - sh "scp -r * gettalong@rubyforge.org:/var/www/gforge-projects/cmdparse/" -end - - -# Misc tasks ################################################################### - - -def count_lines( filename ) - lines = 0 - codelines = 0 - open( filename ) do |f| - f.each do |line| - lines += 1 - next if line =~ /^\s*$/ - next if line =~ /^\s*#/ - codelines += 1 + if defined?(Gem) + desc "Upload the release to Rubygems" + task :publish_files => [:package] do + sh "gem push pkg/cmdparse-#{PKG_VERSION}.gem" + puts 'done' end end - [lines, codelines] -end + if defined?(Webgen) && defined?(Gem) && defined?(Rake::RDocTask) + desc "Release cmdparse version " + PKG_VERSION + task :release => [:clobber, :package, :publish_files] + end -def show_line( msg, lines, loc ) - printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg end - -desc "Show statistics" -task :statistics do - total_lines = 0 - total_code = 0 - show_line( "File Name", "Lines", "LOC" ) - SRC_RB.each do |fn| - lines, codelines = count_lines fn - show_line( fn, lines, codelines ) - total_lines += lines - total_code += codelines - end - show_line( "Total", total_lines, total_code ) -end +task :clobber => ['dev:clobber'] @@ -1,10 +0,0 @@ -* see mail from Markus Werner concerning class methods for defining methods as sub commands - ----- DONE ---- - -* define banner in a common way (like option parser), maybe use the one from option parser -* raise NoCommandGivenError when no command was given -* ability to specify default command (used when no command was given) -* add order,order!,... methods to mimic optparse -* optionally capture error messages and provide friendlier output -* if command has subcommands, use order! method, if has arguments use permute! (=POSIXLY_CORRECT); same with getoptlong
\ No newline at end of file @@ -1 +1 @@ -2.0.5 +2.0.6 diff --git a/checksums.yaml.gz b/checksums.yaml.gz Binary files differnew file mode 100644 index 0000000..02f9f17 --- /dev/null +++ b/checksums.yaml.gz diff --git a/debian/changelog b/debian/changelog index cef511b..19dbe7c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +ruby-cmdparse (2.0.6-1) unstable; urgency=medium + + * Imported Upstream version 2.0.6 + * Replace basic test with the upstream test suite + * debian/control: + - update homepage + - bump Standards-Version to 3.9.6 (no changes needed) + - set Testsuite field to autopkgtest-pkg-ruby + - build-depend on ruby-minitest + * Update years in upstream entry in the copyright file + * Install README.md as documentation + + -- Cédric Boutillier <boutil@debian.org> Tue, 07 Oct 2014 00:37:59 +0200 + ruby-cmdparse (2.0.5-2) unstable; urgency=low [ Cédric Boutillier ] diff --git a/debian/control b/debian/control index c56bae2..6ba5752 100644 --- a/debian/control +++ b/debian/control @@ -3,11 +3,12 @@ Section: ruby Priority: optional Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers@lists.alioth.debian.org> Uploaders: Antonio Terceiro <terceiro@debian.org>, Arnaud Cornet <acornet@debian.org>, Paul van Tilburg <paulvt@debian.org>, Cédric Boutillier <boutil@debian.org> -Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.3.0~) -Standards-Version: 3.9.5 +Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.3.0~), ruby-minitest +Standards-Version: 3.9.6 Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/ruby-cmdparse.git Vcs-Browser: http://anonscm.debian.org/gitweb?p=pkg-ruby-extras/ruby-cmdparse.git;a=summary -Homepage: http://cmdparse.rubyforge.org +Homepage: http://cmdparse.gettalong.org +Testsuite: autopkgtest-pkg-ruby XS-Ruby-Versions: all Package: ruby-cmdparse @@ -17,4 +18,4 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter Description: Advanced command line parsing Ruby module supporting sub-commands Flexible Ruby library simplifying creation of shell commands using sub-commands, ie a la apt-get. - It Uses optparse, part of the Ruby standard library. + It uses optparse, part of the Ruby standard library. diff --git a/debian/copyright b/debian/copyright index d5c112c..366cc60 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: cmdparse Source: http://cmdparse.rubyforge.org/ Files: * -Copyright: Copyright (C) 2004-2012 Thomas Leitner +Copyright: Copyright (C) 2004-2014 Thomas Leitner License: LGPL-3+ Files: setup.rb diff --git a/debian/ruby-cmdparse.docs b/debian/ruby-cmdparse.docs index d0b0785..b43bf86 100644 --- a/debian/ruby-cmdparse.docs +++ b/debian/ruby-cmdparse.docs @@ -1,6 +1 @@ -# FIXME: doc/ dir found in source. Consider installing the docs. -# Examples: -# doc/manual.html -# doc/site/* -# FIXME: READMEs found -# README +README.md diff --git a/debian/ruby-tests.rb b/debian/ruby-tests.rb index bcf4faf..dd1d8d1 100644 --- a/debian/ruby-tests.rb +++ b/debian/ruby-tests.rb @@ -1,7 +1 @@ -# a VERY BASIC test case, just to be sure that the code is installed. -require 'test/unit' -class CmdParseTest < Test::Unit::TestCase - def test_require - require 'cmdparse' - end -end +require './test/tc_commandhash' diff --git a/doc/src/about.page b/doc/about.page index a8c6fd4..1ba0434 100644 --- a/doc/src/about.page +++ b/doc/about.page @@ -1,9 +1,9 @@ --- title: About -inMenu: true -orderInfo: 2 +in_menu: true +sort_info: 2 --- -h2. About @cmdparse@ +## About `cmdparse` Some new programs use a "command style" command line. Examples for such programs are the "svn" program from Subversion and the "gem" program from Rubygems. The standard Ruby distribution has no @@ -15,14 +15,10 @@ specifying commands and subcommands. A typical command line for a program which uses commands looks like this: -<pre> - $ net --verbose ipaddr add 192.168.0.1 193.150.0.1 -</pre> + $ net --verbose ipaddr add 192.168.0.1 193.150.0.1 +## Author -h2. Author - -* *Thomas Leitner* -* Web: "http://cmdparse.rubyforge.org":http://cmdparse.rubyforge.org -* e-Mail: "t_leitner@gmx.at":mailto:t_leitner@gmx.at -* GPG Key-Id: 0xD942E7F6 +* **Thomas Leitner** +* Web: <http://cmdparse.gettalong.org> +* e-Mail: <mailto:t_leitner@gmx.at> diff --git a/doc/config.yaml b/doc/config.yaml deleted file mode 100644 index b1b80e5..0000000 --- a/doc/config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -PageHandler: - validator: xmllint diff --git a/doc/src/default.css b/doc/default.css index 1fdd03c..c0d5869 100644 --- a/doc/src/default.css +++ b/doc/default.css @@ -145,7 +145,8 @@ border: 1px solid black; } - table.cmdparse-example td { + table.cmdparse-example td, pre { border: 1px solid #408040; background-color: white; + padding: 5px; } diff --git a/doc/src/default.template b/doc/default.template index 306d8ef..e7d98a3 100644 --- a/doc/src/default.template +++ b/doc/default.template @@ -20,23 +20,20 @@ </div> <div id="headerbar" class="bar"> - <span class="left">Navbar: {breadcrumbTrail: }</span> - <span class="right">Language: {langbar: }</span> + <span class="left">Navbar: {breadcrumb_trail: }</span> <div style="clear:both"></div> </div> <div id="menu"> - {menu:} + {menu: {options: {mi: {in_menu: true}, sort: true}}} </div> <div id="body"> - {content:} + <webgen:block name="content" /> </div> <div id="footer" class="bar"> - <a href="http://validator.w3.org/check?uri=referer"><img src="{resource: w3c-valid-xhtml11}" alt="Valid XHTML 1.1!" height="31" width="88" /></a> - <a href="http://jigsaw.w3.org/css-validator/check/referer"><img src="{resource: w3c-valid-css}" alt="Valid CSS!" height="31" width="88" /></a> - <a href="http://webgen.rubyforge.org"><img src="{resource: webgen-generated}" alt="Generated by webgen" height="31" width="88"/></a> on <b>{date: }</b> + Generated by <a href="http://webgen.gettalong.org">webgen</a> on <b>{date: }</b> </div> </div> </body> diff --git a/doc/download.page b/doc/download.page new file mode 100644 index 0000000..f489127 --- /dev/null +++ b/doc/download.page @@ -0,0 +1,34 @@ +--- +title: Download & Installation +in_menu: true +sort_info: 4 +--- +## Download + +The newest version of cmdparse can be downloaded from [Rubygems] or the [cmdparse Github +page][github]! + +[Rubygems]: http://rubygems.org/gems/cmdparse +[github]: http://github.com/gettalong/cmdparse + + +## Dependencies + +This library has *no* dependencies. + + +## Installation + +The preferred way of installing cmdparse is via RubyGems: + + $ gem install cmdparse + +If you do not have RubyGems installed, but Rake, you can use the following command: + + $ rake install + +If you have neither RubyGems nor Rake, use these commands: + + $ ruby setup.rb config + $ ruby setup.rb setup + $ ruby setup.rb install diff --git a/doc/index.page b/doc/index.page new file mode 100644 index 0000000..9322738 --- /dev/null +++ b/doc/index.page @@ -0,0 +1,107 @@ +--- +title: Homepage +in_menu: true +routed_title: cmdparse +sort_info: 1 +--- +## Welcome + +... to the homepage of ***cmdparse***, an advanced command line parser supporting commands. + +## Feature List + +* Commands can have subcommands +* Default commands for showing help and the version of the program +* Option parsing library of choice can be used (after writing wrapper), default is `optparse` +* Easy to use + + +## News + +**2014-04-05 cmdparse 2.0.6 released!!!** + +There were no codewise changes but the used infrastructure and tools have been updated to newer +versions. Also the license information has been added to the gem specification and the tests are now +included in the distribution. + + +**2012-06-09 cmdparse 2.0.5 released!!!** + +Changes: + +* Fixed backwards incompatible change + + +**2012-06-07 cmdparse 2.0.4 released!!!** + +Changes: + +* Only some minor changes regarding the help output for commands which is nicer now. + + +**2006-06-17 cmdparse 2.0.2 released!!!** + +Changes: + +* Included two patches from Assaph Mehr: + * partial command matching can now be used (see the [tutorial page](tutorial.html)) + * now a banner for the help and version commands can be specified + + +**2006-04-05 cmdparse 2.0.1 released!!!** + +Changes: + +* Just a bug fix release for those using cmdparse with Rubygems. By issuing the command + `require_gem 'cmdparse'` the cmdparse library gets automagically loaded. +* Minor documentation updates + + +**2005-08-16 cmdparse 2.0.0 released!!!** + +This version is not compatible to previous versions of cmdparse as there have been major changes in +the API. However, updating your code to use the new API is very easy! + +Changes: + +* Commands can now have subcommands which can have subcommands which can have subcommands... +* No need to implement a whole new class for simple commands anymore +* Default option parser library is `optparse`, however, any option parser library can be used after writing a small wrapper + + +**2005-07-05 cmdparse 1.0.5 released!!!** + +Changes: + +* added possibility to parse global options, command and local options separately + + +**2005-06-16 cmdparse 1.0.4 released!!!** + +Changes: + +* fix for older ruby versions +* fixed bug where exception was not caught + + +**2005-06-09 cmdparse 1.0.3 released!!!** + +Changes: + +* added optional graceful exception handling + + +**2005-04-21 cmdparse 1.0.2 released!!!** + +Changes: + +* splitted parsing of the arguments and executing the command into two methods + + +**2005-04-13 cmdparse 1.0.1 released!!!** + +Changes: + +* Improved HelpCommand: the global options -h and --help take an optional command name now +* Possibility to define a default command which is used when no command was specified +* A `NoCommandGivenError` is thrown when no command on the CLI and no default command was specified diff --git a/doc/src/logo.png b/doc/logo.png Binary files differindex 298626e..298626e 100644 --- a/doc/src/logo.png +++ b/doc/logo.png diff --git a/doc/plugin/extract.rb b/doc/plugin/extract.rb deleted file mode 100644 index 6353b2a..0000000 --- a/doc/plugin/extract.rb +++ /dev/null @@ -1,29 +0,0 @@ -module CmdparsePlugins - - class ExtractTag < Tags::DefaultTag - - summary "Extracts lines from a file" - depends_on 'Tags' - add_param 'file', nil, 'the file from which to read the lines' - add_param 'lines', nil, 'the lines which should be read (Range)' - set_mandatory 'file' - set_mandatory 'lines' - - def initialize - super - @processOutput = false - register_tag( 'extract' ) - end - - def process_tag( tag, node, refNode ) - data = File.readlines( get_param( 'file' ) ).unshift( 'empty null line' )[get_param( 'lines' )] - out = '<table class="cmdparse-example"><tr>' - out << '<td><pre>' << get_param( 'lines' ).collect {|n| "#{n}<br />" }.to_s << '</pre></td>' - out << '<td><pre>' << data.collect {|line| "#{CGI::escapeHTML(line)}" }.to_s << '</pre></td>' - out << '</tr></table>' - out - end - - end - -end diff --git a/doc/src/download.page b/doc/src/download.page deleted file mode 100644 index 5a08494..0000000 --- a/doc/src/download.page +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Download & Installation -inMenu: true -orderInfo: 4 ---- -h2. Download - -The newest version of cmdparse can be downloaded from Rubyforge. - -Homepage: "cmdparse.rubyforge.org":http://cmdparse.rubyforge.org <br /> -Download: "rubyforge.org/frs/?group_id=396":http://rubyforge.org/frs/?group_id=396 - -h2. Dependencies - -none - -h2. Installation - -The preferred way of installing cmdparse is via RubyGems: -<pre> - $ gem install cmdparse -</pre> - -If you do not have RubyGems installed, but Rake, you can use the following command: -<pre> - $ rake install -</pre> - -If you have neither RubyGems nor Rake, use these commands: -<pre> - $ ruby setup.rb config - $ ruby setup.rb setup - $ ruby setup.rb install -</pre> diff --git a/doc/src/features.page b/doc/src/features.page deleted file mode 100644 index 5fcb929..0000000 --- a/doc/src/features.page +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Features -inMenu: true -orderInfo: 3 ---- -h2. Feature list - -* Commands can have subcommands -* Default commands for showing help and the version of the program -* Option parsing library of choice can be used (after writing wrapper), default is @optparse@ -* Easy to use diff --git a/doc/src/index.page b/doc/src/index.page deleted file mode 100644 index f7e46c6..0000000 --- a/doc/src/index.page +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: HomePage -inMenu: true -directoryName: cmdparse -menuOrder: 1 ---- -h2. Welcome - -... to the homepage of _*cmdparse*_, an advanced command line parser supporting commands. - -Have a look around the site! - -h2. News - -<b>2006-06-17 cmdparse 2.0.2 released!!!</b> - -Changes: - -* Included two patches from Assaph Mehr: - * partial command matching can now be used (see <a href="{relocatable: tutorial.page}">tutorial page</a>) - * now a banner for the help and version commands can be specified - -<b>2006-04-05 cmdparse 2.0.1 released!!!</b> - -Changes: - -* Just a bug fix release for those using cmdparse with Rubygems. By issuing the command - @require_gem 'cmdparse'@ the cmdparse library gets automagically loaded. -* Minor documentation updates - -<b>2005-08-16 cmdparse 2.0.0 released!!!</b> - -This version is not compatible to previous versions of cmdparse as there have been major changes in -the API. However, updating your code to use the new API is very easy! - -Changes: - -* Commands can now have subcommands which can have subcommands which can have subcommands... -* No need to implement a whole new class for simple commands anymore -* Default option parser library is @optparse@, however, any option parser library can be used after writing a small wrapper - -<b>2005-07-05 cmdparse 1.0.5 released!!!</b> - -Changes: - -* added possibility to parse global options, command and local options separately - - -<b>2005-06-16 cmdparse 1.0.4 released!!!</b> - -Changes: - -* fix for older ruby versions -* fixed bug where exception was not caught - - -<b>2005-06-09 cmdparse 1.0.3 released!!!</b> - -Changes: - -* added optional graceful exception handling - - -<b>2005-04-21 cmdparse 1.0.2 released!!!</b> - -Changes: - -* splitted parsing of the arguments and executing the command into two methods - -<b>2005-04-13 cmdparse 1.0.1 released!!!</b> - -Changes: - -* Improved HelpCommand: the global options -h and --help take an optional command name now -* Possibility to define a default command which is used when no command was specified -* A @NoCommandGivenError@ is thrown when no command on the CLI and no default command was specified - -h2. And so - -... have fun! diff --git a/doc/src/meta.info b/doc/src/meta.info deleted file mode 100644 index 88ff88a..0000000 --- a/doc/src/meta.info +++ /dev/null @@ -1,5 +0,0 @@ -api.html: - dest: rdoc/index.html - title: API Reference - orderInfo: 7 - inMenu: true diff --git a/doc/src/tutorial.page b/doc/src/tutorial.page deleted file mode 100644 index 6591e44..0000000 --- a/doc/src/tutorial.page +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: Tutorial -inMenu: true -orderInfo: 6 ---- -h2. Quickstart - -Here are some short code fragments which show how to use cmdparse. A complete example app can be -found later in the "tutorial section":#tutorial. - -Defining commands using classes: -<pre> -class TestCmd < CmdParse::Command - def initialize - super('test', true) - self.add_command(TestSubCmd.new) - end -end - -class TestSubCmd < CmdParse::Command - def initialize - super('sub',false) - end - - def execute (args) - puts "Hallo #{args}" - end -end - -cmd = CmdParse::CommandParser.new( true ) -cmd.add_command(TestCmd.new) -</pre> - -Defining command using the basic @CmdParse::Command@ class: -<pre> -cmd = CmdParse::CommandParser.new( true ) -testcmd = CmdParse::Command.new( 'test', true ) -testcmd.short_desc = "Short desc" -cmd.add_command( testcmd ) - -sub = CmdParse::Command.new( 'sub', false ) -sub.short_desc = "Add an IP address" -sub.set_execution_block do |args| - puts "Hallo #{args}" -end -testcmd.add_command( sub ) -</pre> - -h2(#tutorial). Tutorial - -<b>The complete code for this example can be found in the file @net.rb@ of the @cmdparse@ -package!</b> - -This tutorial produces a small @net@ application which can add, delete and list IP adresses and show -'network statistics'. The shown code fragments do not include the whole program. So, instead of -writing all the code yourself, just look at the code fragments first and then use the include -@net.rb@ file for running the program. - -h3. Require statements - -Create a new new file and write the necessary @require@ statements. - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 5..5}}</notextile> - -h3. The @CommandParser@ class - -Next we will define our basic @CommandParser@ by defining the name of the program, its version and -the global options. The first boolean argument to the constructor of the @CommandParser@ class -defines whether exceptions should be handled gracefully, i.e. by showing an appropriate error -message and the help screen. The second boolean argument defines whether the top level commands -should use partial command matching instead of full command matching. If partial command matching is -used, then the shortest unambiguous part of a command name can be used instead of always specifing -the full command name. - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 30..36}}</notextile> - -The options are defined using an option parser wrapper. Currently, the only option parser library -supported is @optparse@ from the Ruby Standard Library. If you want to use another option parser -library, you need to write a wrapper for it so that @cmdparse@ can use it. - -Now we only have to tell the program to use our newly defined class to process the command line -arguments. - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 86..86}}</notextile> - -The @parse@ method of our @CommandParser@ instance parses the given array of options (or @ARGV@ if -no argument is specified). All the command line options are parsed and the given command executed. - -The program can be executed now but won't be useful as we did not specify any commands. - -h3. Defining commands - -So, as we have defined our @CommandParser@ object, we need to add some commands to it. First, we -will add two predefined commands, namely the @help@ and the @version@ command. - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 37..38}}</notextile> - -That was easy! Now you can execute the program and specify the commands @help@ or @version@. -However, we want the program to do something "useful". Therefore we define a new command. - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 41..44}}</notextile> - -This command is defined by using the default @Command@ class. First an instance is created assigning -a name to the command and defining whether this command takes subcommands and uses partial command -matching. Next we add a short description so that the @help@ command can produce something useful. -And at last, we add this command to our @CommandParser@ instance. - -We specified that our @ipaddr@ command takes subcommands. So we have to define them, too: - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 46..78}}</notextile> - -We add three subcommands to the @ipaddr@ command: @add@, @del@ and @list@. - -The @add@ command is similar to the @ipaddr@ command. However, as the @add@ command does not take -other commands, we have to define an execution block. - -The @del@ command is similar to the @add@ command. As we want to be able to delete all IP addresses -by issuing only one command, we add an option for this. - -By providing @true@ as second argument when we add the @list@ command to the @ipaddr@ command, we -specifiy that this command should be the default command which gets invoked when no command name is -specified on the command line. Only one command can be specified as default command! - -Till now we only used the basic @Command@ class to specify commands. However, new commands can also -be created by subclassing the @Command@ class, as shown with this last command: - -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 9..28}}</notextile> -<notextile>{extract: {file: ../net.rb, lines: !ruby/range 39..39}}</notextile> - -h3. Running the program - -That's all! You can run the program now and have a look at the output which differs depending on -which arguments you choose. - -So, a typical invocation of this program looks like this: - -<pre> - $ ruby net.rb --verbose ipaddr add 192.168.0.1 193.150.0.1 -</pre> - -* <notextile><tt>--verbose</tt></notextile> is a global option -* @ipaddr@ is the first command name (which has no options) -* @add@ is the second command name (which also has no options) -* <tt>192.168.0.1 193.150.0.1</tt> are additional arguments - -You should notice that if you type -<pre> - $ ruby net.rb -</pre> - -you get an error because you did not specify any command. - -However, when you type -<pre> - $ ruby net.rb ipaddr -</pre> - -you do not get an error! - -Why? As the @ipaddr@ command takes subcommands there should be an additional command name (e.g. -@list@) on the command line. However, as the @list@ command is the default command for @ipaddr@ you -do not need to type it. - -*By the way:* You get the same output if you type -<pre> - $ ruby net.rb ip -</pre> - -Why? As partial command matching is used for the top level commands, the shortest unambiguous name -for a command can be used. As there is no other command starting with @ip@ (or even with the letter -@i@), it is sufficient to write the above to select the @ipaddr@ command. - -*Notice:* The options of a command which does not take subcommands do not need to be at the front; -they can be anywhere, like this -<pre> - $ ruby test.rb --verbose mycommand file1 file2 --recursive file3 -</pre> diff --git a/doc/tutorial.page b/doc/tutorial.page new file mode 100644 index 0000000..e9b000f --- /dev/null +++ b/doc/tutorial.page @@ -0,0 +1,191 @@ +--- +title: Tutorial +in_menu: true +sort_info: 6 +--- +## Quickstart + +Here are some short code fragments which show how to use cmdparse. A complete example app can be +found later in the [tutorial section](#tutorial). + +Defining commands using classes: + +~~~ ruby +class TestCmd < CmdParse::Command + def initialize + super('test', true) + self.add_command(TestSubCmd.new) + end +end + +class TestSubCmd < CmdParse::Command + def initialize + super('sub',false) + end + + def execute (args) + puts "Hallo #{args}" + end +end + +cmd = CmdParse::CommandParser.new( true ) +cmd.add_command(TestCmd.new) +~~~ + +Defining command using the basic `CmdParse::Command` class: + +~~~ ruby +cmd = CmdParse::CommandParser.new( true ) +testcmd = CmdParse::Command.new( 'test', true ) +testcmd.short_desc = "Short desc" +cmd.add_command( testcmd ) + +sub = CmdParse::Command.new( 'sub', false ) +sub.short_desc = "Add an IP address" +sub.set_execution_block do |args| + puts "Hallo #{args}" +end +testcmd.add_command( sub ) +~~~ + + +## Tutorial + +The complete code for this example can be found in the file `net.rb` of the `cmdparse` package! + +This tutorial produces a small `net` application which can add, delete and list IP adresses and show +'network statistics'. The shown code fragments do not include the whole program. So, instead of +writing all the code yourself, just look at the code fragments first and then use the include +`net.rb` file for running the program. + +### Require statements + +Create a new new file and write the necessary `require` statements. + +~~~ ruby +{extract: {lines: !ruby/range 5..5}} +~~~ + +### The `CommandParser` class + +Next we will define our basic `CommandParser` by defining the name of the program, its version and +the global options. + +The first boolean argument to the constructor of the `CommandParser` class defines whether +exceptions should be handled gracefully, i.e. by showing an appropriate error message and the help +screen. The second boolean argument defines whether the top level commands should use partial +command matching instead of full command matching. If partial command matching is used, then the +shortest unambiguous part of a command name can be used instead of always specifing the full command +name. + +~~~ ruby +{extract: {lines: !ruby/range 30..36}} +~~~ + +The options are defined using an option parser wrapper. Currently, the only option parser library +supported is `optparse` from the Ruby Standard Library. If you want to use another option parser +library, you need to write a wrapper for it so that `cmdparse` can use it. + +Now we only have to tell the program to use our newly defined class to process the command line +arguments. + +~~~ ruby +{extract: {lines: !ruby/range 86..86}} +~~~ + +The `parse` method of our `CommandParser` instance parses the given array of options (or `ARGV` if +no argument is specified). All the command line options are parsed and the given command executed. + +The program can be executed now but won't be useful as we did not specify any commands. + + +### Defining commands + +So, as we have defined our `CommandParser` object, we need to add some commands to it. First, we +will add two predefined commands, namely the `help` and the `version` command. + +~~~ ruby +{extract: {lines: !ruby/range 37..38}} +~~~ + +That was easy! Now you can execute the program and specify the commands `help` or `version`. +However, we want the program to do something "useful". Therefore we define a new command. + +~~~ ruby +{extract: {lines: !ruby/range 41..44}} +~~~ + +This command is defined by using the default `Command` class. First an instance is created assigning +a name to the command and defining whether this command takes subcommands and uses partial command +matching. Next we add a short description so that the `help` command can produce something useful. +And at last, we add this command to our `CommandParser` instance. + +We specified that our `ipaddr` command takes subcommands. So we have to define them, too: + +~~~ ruby +{extract: {lines: !ruby/range 46..78}} +~~~ + +We add three subcommands to the `ipaddr` command: `add`, `del` and `list`. + +* The `add` command is similar to the `ipaddr` command. However, as the `add` command does not take + other commands, we have to define an execution block. + +* The `del` command is similar to the `add` command. As we want to be able to delete all IP + addresses by issuing only one command, we add an option for this. + +* By providing `true` as second argument when we add the `list` command to the `ipaddr` command, we + specifiy that this command should be the default command which gets invoked when no command name + is specified on the command line. Only one command can be specified as default command! + +Till now we only used the basic `Command` class to specify commands. However, new commands can also +be created by subclassing the `Command` class, as shown with this last command: + +~~~ ruby +{extract: {lines: !ruby/range 9..28}} +{extract: {lines: !ruby/range 39..39}} +~~~ + + +### Running the program + +That's all! You can run the program now and have a look at the output which differs depending on +which arguments you choose. + +So, a typical invocation of this program looks like this: + + $ ruby net.rb --verbose ipaddr add 192.168.0.1 193.150.0.1 + +* `--verbose` is a global option +* `ipaddr` is the first command name (which has no options) +* `add` is the second command name (which also has no options) +* `192.168.0.1 193.150.0.1` are additional arguments + +You should notice that if you type + + $ ruby net.rb + +you get an error because you did not specify any command. + +However, when you type + + $ ruby net.rb ipaddr + +you do not get an error! + +Why? As the `ipaddr` command takes subcommands there should be an additional command name (e.g. +`list`) on the command line. However, as the `list` command is the default command for `ipaddr` you +do not need to type it. + +*By the way:* You get the same output if you type + + $ ruby net.rb ip + +Why? As partial command matching is used for the top level commands, the shortest unambiguous name +for a command can be used. As there is no other command starting with `ip` (or even with the letter +`i`), it is sufficient to write the above to select the `ipaddr` command. + +*Notice:* The options of a command which does not take subcommands do not need to be at the front; +they can be anywhere, like this + + $ ruby test.rb --verbose mycommand file1 file2 --recursive file3 diff --git a/doc/virtual b/doc/virtual new file mode 100644 index 0000000..ad36e85 --- /dev/null +++ b/doc/virtual @@ -0,0 +1,5 @@ +api.html: + dest_path: rdoc/index.html + title: API Reference + sort_info: 7 + in_menu: true diff --git a/lib/cmdparse.rb b/lib/cmdparse.rb index b23602a..e997a13 100644 --- a/lib/cmdparse.rb +++ b/lib/cmdparse.rb @@ -1,7 +1,7 @@ # #-- # cmdparse: advanced command line parser supporting commands -# Copyright (C) 2004-2012 Thomas Leitner +# Copyright (C) 2004-2014 Thomas Leitner # # This file is part of cmdparse. # @@ -23,7 +23,7 @@ module CmdParse # The version of this cmdparse implemention - VERSION = [2, 0, 5] + VERSION = [2, 0, 6] # Base class for all cmdparse errors. diff --git a/lib/cmdparse/wrappers/optparse.rb b/lib/cmdparse/wrappers/optparse.rb index 74c6711..27441bd 100644 --- a/lib/cmdparse/wrappers/optparse.rb +++ b/lib/cmdparse/wrappers/optparse.rb @@ -1,7 +1,7 @@ # #-- # cmdparse: advanced command line parser supporting commands -# Copyright (C) 2004-2012 Thomas Leitner +# Copyright (C) 2004-2014 Thomas Leitner # # This file is part of cmdparse. # diff --git a/metadata.yml b/metadata.yml index e5a8476..9d46b98 100644 --- a/metadata.yml +++ b/metadata.yml @@ -1,71 +1,67 @@ --- !ruby/object:Gem::Specification name: cmdparse version: !ruby/object:Gem::Version - version: 2.0.5 - prerelease: + version: 2.0.6 platform: ruby authors: - Thomas Leitner autorequire: cmdparse bindir: bin cert_chain: [] -date: 2012-06-09 00:00:00.000000000 Z +date: 2014-04-05 00:00:00.000000000 Z dependencies: [] -description: ! " cmdparse provides classes for parsing commands on the command - line; command line options\n are parsed using optparse or any other option - parser implementation. Programs that use\n such command line interfaces are, - for example, subversion's 'svn' or Rubygem's 'gem' program.\n" +description: |2 + cmdparse provides classes for parsing commands on the command line; command line options + are parsed using optparse or any other option parser implementation. Programs that use + such command line interfaces are, for example, subversion's 'svn' or Rubygem's 'gem' program. email: t_leitner@gmx.at executables: [] extensions: [] extra_rdoc_files: [] files: - setup.rb -- TODO - COPYING - COPYING.LESSER -- README +- README.md - Rakefile - net.rb - VERSION -- lib/cmdparse/wrappers/optparse.rb - lib/cmdparse.rb -- doc/plugin/extract.rb -- doc/config.yaml -- doc/src/features.page -- doc/src/logo.png -- doc/src/tutorial.page -- doc/src/default.css -- doc/src/index.page -- doc/src/meta.info -- doc/src/download.page -- doc/src/default.template -- doc/src/about.page -homepage: http://cmdparse.rubyforge.org -licenses: [] +- lib/cmdparse/wrappers/optparse.rb +- doc/about.page +- doc/default.css +- doc/default.template +- doc/download.page +- doc/index.page +- doc/logo.png +- doc/tutorial.page +- doc/virtual +- test/tc_commandhash.rb +homepage: http://cmdparse.gettalong.org +licenses: +- LGPLv3 +metadata: {} post_install_message: rdoc_options: - --line-numbers -- -m +- --main - CmdParse::CommandParser require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement - none: false requirements: - - - ! '>=' + - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] -rubyforge_project: cmdparse -rubygems_version: 1.8.24 +rubyforge_project: +rubygems_version: 2.0.3 signing_key: -specification_version: 3 +specification_version: 4 summary: Advanced command line parser supporting commands test_files: [] @@ -9,12 +9,12 @@ require 'yaml' class NetStatCommand < CmdParse::Command def initialize - super( 'stat', false ) + super('stat', false) self.short_desc = "Show network statistics" self.description = "This command shows very useful network statistics - eye catching!!!" end - def execute( args ) + def execute(args) puts "Showing network statistics" if $verbose puts puts "Yeah, I will do something now..." @@ -27,62 +27,62 @@ class NetStatCommand < CmdParse::Command end -cmd = CmdParse::CommandParser.new( true, true ) +cmd = CmdParse::CommandParser.new(true, true) cmd.program_name = "net" cmd.program_version = [0, 1, 1] cmd.options = CmdParse::OptionParserWrapper.new do |opt| opt.separator "Global options:" opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true } end -cmd.add_command( CmdParse::HelpCommand.new ) -cmd.add_command( CmdParse::VersionCommand.new ) -cmd.add_command( NetStatCommand.new ) +cmd.add_command(CmdParse::HelpCommand.new) +cmd.add_command(CmdParse::VersionCommand.new) +cmd.add_command(NetStatCommand.new) # ipaddr -ipaddr = CmdParse::Command.new( 'ipaddr', true, true ) +ipaddr = CmdParse::Command.new('ipaddr', true, true) ipaddr.short_desc = "Manage IP addresses" -cmd.add_command( ipaddr ) +cmd.add_command(ipaddr) # ipaddr add -add = CmdParse::Command.new( 'add', false ) +add = CmdParse::Command.new('add', false) add.short_desc = "Add an IP address" add.set_execution_block do |args| puts "Adding ip addresses: #{args.join(', ')}" if $verbose $ipaddrs += args end -ipaddr.add_command( add ) +ipaddr.add_command(add) # ipaddr del -del = CmdParse::Command.new( 'del', false ) +del = CmdParse::Command.new('del', false) del.short_desc = "Delete an IP address" del.options = CmdParse::OptionParserWrapper.new do |opt| - opt.on( '-a', '--all', 'Delete all IP addresses' ) { $deleteAll = true } + opt.on('-a', '--all', 'Delete all IP addresses') { $deleteAll = true } end del.set_execution_block do |args| if $deleteAll $ipaddrs = [] else puts "Deleting ip addresses: #{args.join(', ')}" if $verbose - args.each {|ip| $ipaddrs.delete( ip ) } + args.each {|ip| $ipaddrs.delete(ip) } end end -ipaddr.add_command( del ) +ipaddr.add_command(del) # ipaddr list -list = CmdParse::Command.new( 'list', false ) +list = CmdParse::Command.new('list', false) list.short_desc = "Lists all IP addresses" list.set_execution_block do |args| puts "Listing ip addresses:" if $verbose puts $ipaddrs.to_yaml end -ipaddr.add_command( list, true ) +ipaddr.add_command(list, true) -if File.exists?( 'dumpnet' ) - $ipaddrs = Marshal.load( File.read( 'dumpnet' ) ) +if File.exists?('dumpnet') + $ipaddrs = Marshal.load(File.read('dumpnet')) else $ipaddrs = [] end cmd.parse -File.open( 'dumpnet', 'w+' ) {|f| f.write( Marshal.dump( $ipaddrs ) ) } +File.open('dumpnet', 'w+') {|f| f.write(Marshal.dump($ipaddrs)) } @@ -1,70 +1,20 @@ # # setup.rb # -# Copyright (c) 2000-2004 Minero Aoki +# Copyright (c) 2000-2005 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of -# the GNU Lesser General Public License version 2.1. +# the GNU LGPL, Lesser General Public License version 2.1. # -# -# For backward compatibility -# - -unless Enumerable.method_defined?(:map) +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end -unless Enumerable.method_defined?(:detect) - module Enumerable - alias detect find - end -end - -unless Enumerable.method_defined?(:select) - module Enumerable - alias select find_all - end -end - -unless Enumerable.method_defined?(:reject) - module Enumerable - def reject - result = [] - each do |i| - result.push i unless yield(i) - end - result - end - end -end - -unless Enumerable.method_defined?(:inject) - module Enumerable - def inject(result) - each do |i| - result = yield(result, i) - end - result - end - end -end - -unless Enumerable.method_defined?(:any?) - module Enumerable - def any? - each do |i| - return true if yield(i) - end - false - end - end -end - -unless File.respond_to?(:read) +unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read @@ -72,9 +22,13 @@ unless File.respond_to?(:read) end end -# -# Application independent utilities -# +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end def File.binread(fname) open(fname, 'rb') {|f| @@ -82,311 +36,512 @@ def File.binread(fname) } end -# for corrupted windows stat(2) +# for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end -# -# Config -# - -if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg } - ARGV.delete(arg) - require arg.split(/=/, 2)[1] - $".push 'rbconfig.rb' -else - require 'rbconfig' -end - -def multipackage_install? - FileTest.directory?(File.dirname($0) + '/packages') -end - class ConfigTable - c = ::Config::CONFIG + include Enumerable - rubypath = c['bindir'] + '/' + c['ruby_install_name'] + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end - major = c['MAJOR'].to_i - minor = c['MINOR'].to_i - teeny = c['TEENY'].to_i - version = "#{major}.#{minor}" + attr_accessor :install_prefix + attr_accessor :config_opt - # ruby ver. >= 1.4.4? - newpath_p = ((major >= 2) or - ((major == 1) and - ((minor >= 5) or - ((minor == 4) and (teeny >= 4))))) - - subprefix = lambda {|path| - path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix') - } + attr_writer :verbose - if c['rubylibdir'] - # V < 1.6.3 - stdruby = subprefix.call(c['rubylibdir']) - siteruby = subprefix.call(c['sitedir']) - versite = subprefix.call(c['sitelibdir']) - sodir = subprefix.call(c['sitearchdir']) - elsif newpath_p - # 1.4.4 <= V <= 1.6.3 - stdruby = "$prefix/lib/ruby/#{version}" - siteruby = subprefix.call(c['sitedir']) - versite = siteruby + '/' + version - sodir = "$site-ruby/#{c['arch']}" - else - # V < 1.4.4 - stdruby = "$prefix/lib/ruby/#{version}" - siteruby = "$prefix/lib/ruby/#{version}/site_ruby" - versite = siteruby - sodir = "$site-ruby/#{c['arch']}" - end - - if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } - makeprog = arg.sub(/'/, '').split(/=/, 2)[1] - else - makeprog = 'make' - end - - common_descripters = [ - [ 'prefix', [ c['prefix'], - 'path', - 'path prefix of target environment' ] ], - [ 'std-ruby', [ stdruby, - 'path', - 'the directory for standard ruby libraries' ] ], - [ 'site-ruby-common', [ siteruby, - 'path', - 'the directory for version-independent non-standard ruby libraries' ] ], - [ 'site-ruby', [ versite, - 'path', - 'the directory for non-standard ruby libraries' ] ], - [ 'bin-dir', [ '$prefix/bin', - 'path', - 'the directory for commands' ] ], - [ 'rb-dir', [ '$site-ruby', - 'path', - 'the directory for ruby scripts' ] ], - [ 'so-dir', [ sodir, - 'path', - 'the directory for ruby extentions' ] ], - [ 'data-dir', [ '$prefix/share', - 'path', - 'the directory for shared data' ] ], - [ 'ruby-path', [ rubypath, - 'path', - 'path to set to #! line' ] ], - [ 'ruby-prog', [ rubypath, - 'name', - 'the ruby program using for installation' ] ], - [ 'make-prog', [ makeprog, - 'name', - 'the make program to compile ruby extentions' ] ], - [ 'without-ext', [ 'no', - 'yes/no', - 'does not compile/install ruby extentions' ] ] - ] - multipackage_descripters = [ - [ 'with', [ '', - 'name,name...', - 'package names that you want to install', - 'ALL' ] ], - [ 'without', [ '', - 'name,name...', - 'package names that you do not want to install', - 'NONE' ] ] - ] - if multipackage_install? - DESCRIPTER = common_descripters + multipackage_descripters - else - DESCRIPTER = common_descripters + def verbose? + @verbose end - SAVE_FILE = 'config.save' + attr_writer :no_harm - def ConfigTable.each_name(&block) - keys().each(&block) + def no_harm? + @no_harm end - def ConfigTable.keys - DESCRIPTER.map {|name, *dummy| name } + def [](key) + lookup(key).resolve(self) end - def ConfigTable.each_definition(&block) - DESCRIPTER.each(&block) + def []=(key, val) + lookup(key).set val end - def ConfigTable.get_entry(name) - name, ent = DESCRIPTER.assoc(name) - ent + def names + @items.map {|i| i.name } end - def ConfigTable.get_entry!(name) - get_entry(name) or raise ArgumentError, "no such config: #{name}" + def each(&block) + @items.each(&block) end - def ConfigTable.add_entry(name, vals) - ConfigTable::DESCRIPTER.push [name,vals] + def key?(name) + @table.key?(name) end - def ConfigTable.remove_entry(name) - get_entry(name) or raise ArgumentError, "no such config: #{name}" - DESCRIPTER.delete_if {|n, arr| n == name } + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" end - def ConfigTable.config_key?(name) - get_entry(name) ? true : false + def add(item) + @items.push item + @table[item.name] = item end - def ConfigTable.bool_config?(name) - ent = get_entry(name) or return false - ent[1] == 'yes/no' + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item end - def ConfigTable.value_config?(name) - ent = get_entry(name) or return false - ent[1] != 'yes/no' + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end end - def ConfigTable.path_config?(name) - ent = get_entry(name) or return false - ent[1] == 'path' + def savefile + '.config' end - - class << self - alias newobj new + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end end - def ConfigTable.new - c = newobj() - c.initialize_from_table - c + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } end - def ConfigTable.load - c = newobj() - c.initialize_from_file - c + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end end - def initialize_from_table - @table = {} - DESCRIPTER.each do |k, (default, vname, desc, default2)| - @table[k] = default + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + major = c['MAJOR'].to_i + minor = c['MINOR'].to_i + teeny = c['TEENY'].to_i + version = "#{major}.#{minor}" + + # ruby ver. >= 1.4.4? + newpath_p = ((major >= 2) or + ((major == 1) and + ((minor >= 5) or + ((minor == 4) and (teeny >= 4))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" end - end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } - def initialize_from_file - raise InstallError, "#{File.basename $0} config first"\ - unless File.file?(SAVE_FILE) - @table = {} - File.foreach(SAVE_FILE) do |line| - k, v = line.split(/=/, 2) - @table[k] = v.strip + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' end - end - def save - File.open(SAVE_FILE, 'w') {|f| - @table.each do |k, v| - f.printf "%s=%s\n", k, v if v - end - } + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end end - def []=(k, v) - raise InstallError, "unknown config option #{k}"\ - unless ConfigTable.config_key?(k) - @table[k] = v + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + @items.freeze + @table.freeze + @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end - - def [](key) - return nil unless @table[key] - @table[key].gsub(%r<\$([^/]+)>) { self[$1] } + + def parse_opt(opt) + m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] end - def set_raw(key, val) - @table[key] = val + def dllext + @rbconfig['DLEXT'] end - def get_raw(key) - @table[key] + def value_config?(name) + lookup(name).value? end -end + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + attr_reader :name + attr_reader :description -module MetaConfigAPI + attr_accessor :default + alias help_default default - def eval_file_ifexist(fname) - instance_eval File.read(fname), fname, 1 if File.file?(fname) - end + def help_opt + "--#{@name}=#{@template}" + end - def config_names - ConfigTable.keys - end + def value? + true + end - def config?(name) - ConfigTable.config_key?(name) - end + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end - def bool_config?(name) - ConfigTable.bool_config?(name) + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end end - def value_config?(name) - ConfigTable.value_config?(name) + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end end - def path_config?(name) - ConfigTable.path_config?(name) + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end end - def add_config(name, argname, default, desc) - ConfigTable.add_entry name,[default,argname,desc] + class ProgramItem < Item + def config_type + 'program' + end end - def add_path_config(name, default, desc) - add_config name, 'path', default, desc + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end end - def add_bool_config(name, default, desc) - add_config name, 'yes/no', default ? 'yes' : 'no', desc + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end end - def set_config_default(name, default) - if bool_config?(name) - ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no') - else - ConfigTable.get_entry!(name)[0] = default + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val end end - def remove_config(name) - ent = ConfigTable.get_entry(name) - ConfigTable.remove_entry name - ent + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end end -end +end # class ConfigTable -# -# File Operations -# +# This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) - dirname = prefix + dirname if prefix + dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? - # does not check '/'... it's too abnormal case - dirs = dirname.split(%r<(?=/)>) + # Does not check '/', it's too abnormal. + dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] @@ -397,54 +552,78 @@ module FileOperations end end - def rm_f(fname) - $stderr.puts "rm -f #{fname}" if verbose? + def rm_f(path) + $stderr.puts "rm -f #{path}" if verbose? return if no_harm? - - if File.exist?(fname) or File.symlink?(fname) - File.chmod 0777, fname - File.unlink fname - end + force_remove_file path end - def rm_rf(dn) - $stderr.puts "rm -rf #{dn}" if verbose? + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end - Dir.chdir dn - Dir.foreach('.') do |fn| - next if fn == '.' - next if fn == '..' - if File.dir?(fn) - verbose_off { - rm_rf fn - } + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath else - verbose_off { - rm_f fn - } + force_remove_file entpath end end - Dir.chdir '..' - Dir.rmdir dn + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end end def move_file(src, dest) - File.unlink dest if File.exist?(dest) + force_remove_file dest begin File.rename src, dest rescue - File.open(dest, 'wb') {|f| f.write File.binread(src) } + File.open(dest, 'wb') {|f| + f.write File.binread(src) + } File.chmod File.stat(src).mode, dest File.unlink src end end + def force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? - realdest = prefix + dest if prefix + realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) @@ -471,68 +650,42 @@ module FileOperations new_content != File.binread(path) end - def command(str) - $stderr.puts str if verbose? - system str or raise RuntimeError, "'system #{str}' failed" + def command(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end - def ruby(str) - command config('ruby-prog') + ' ' + str + def ruby(*args) + command config('rubyprog'), *args end - def make(task = '') - command config('make-prog') + ' ' + task + def make(task = nil) + command(*[config('makeprog'), task].compact) end def extdir?(dir) - File.exist?(dir + '/MANIFEST') + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end - def all_files_in(dirname) - Dir.open(dirname) {|d| - return d.select {|ent| File.file?("#{dirname}/#{ent}") } + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } } end - REJECT_DIRS = %w( - CVS SCCS RCS CVS.adm - ) + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) - def all_dirs_in(dirname) - Dir.open(dirname) {|d| - return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end -# -# Main Installer -# - -class InstallError < StandardError; end - - -module HookUtils - - def run_hook(name) - try_run_hook "#{curr_srcdir()}/#{name}" or - try_run_hook "#{curr_srcdir()}/#{name}.rb" - end - - def try_run_hook(fname) - return false unless File.file?(fname) - begin - instance_eval File.read(fname), fname, 1 - rescue - raise InstallError, "hook #{fname} failed:\n" + $!.message - end - true - end - -end - +# This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @@ -541,6 +694,7 @@ module HookScriptAPI alias config get_config + # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end @@ -549,10 +703,6 @@ module HookScriptAPI # srcdir/objdir (works only in the package directory) # - #abstract srcdir_root - #abstract objdir_root - #abstract relpath - def curr_srcdir "#{srcdir_root()}/#{relpath()}" end @@ -574,7 +724,7 @@ module HookScriptAPI end def srcfile?(path) - File.file? srcfile(path) + File.file?(srcfile(path)) end def srcentries(path = '.') @@ -600,35 +750,53 @@ end class ToplevelInstaller - Version = '3.2.4' - Copyright = 'Copyright (c) 2000-2004 Minero Aoki' + Version = '3.4.1' + Copyright = 'Copyright (c) 2000-2005 Minero Aoki' TASKS = [ + [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke - instance().invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke end - @singleton = nil - - def ToplevelInstaller.instance - @singleton ||= new(File.dirname($0)) - @singleton + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') end - include MetaConfigAPI + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::RbConfig::CONFIG + end - def initialize(ardir_root) - @config = nil - @options = { 'verbose' => true } + def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] end def inspect @@ -637,33 +805,34 @@ class ToplevelInstaller def invoke run_metaconfigs - task = parsearg_global() - @config = load_config(task) - __send__ "parsearg_#{task}" - init_installers - __send__ "exec_#{task}" - end - - def run_metaconfigs - eval_file_ifexist "#{@ardir}/metaconfig" - end - - def load_config(task) - case task - when 'config' - ConfigTable.new - when 'clean', 'distclean' - if File.exist?('config.save') - then ConfigTable.load - else ConfigTable.new - end + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install else - ConfigTable.load + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" end end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end def init_installers - @installer = Installer.new(@config, @options, @ardir, File.expand_path('.')) + @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # @@ -687,96 +856,91 @@ class ToplevelInstaller # def parsearg_global - valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/ - while arg = ARGV.shift case arg when /\A\w+\z/ - raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg - when '-q', '--quiet' - @options['verbose'] = false - - when '--verbose' - @options['verbose'] = true - - when '-h', '--help' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' print_usage $stdout exit 0 - - when '-v', '--version' + when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 - when '--copyright' puts Copyright exit 0 - else - raise InstallError, "unknown global option '#{arg}'" + setup_rb_error "unknown global option '#{arg}'" end end + nil + end - raise InstallError, <<EOS -No task or global option given. -Typical installation procedure is: - $ ruby #{File.basename($0)} config - $ ruby #{File.basename($0)} setup - # ruby #{File.basename($0)} install (may require root privilege) -EOS + def valid_task?(t) + valid_task_re() =~ t end + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end def parsearg_no_options - raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\ - unless ARGV.empty? + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config - re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/ - @options['config-opt'] = [] - + evalopt = [] + set = [] + @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i - @options['config-opt'] = ARGV.dup + @config.config_opt = ARGV.dup break end - m = re.match(i) or raise InstallError, "config: unknown option #{i}" - name, value = m.to_a[1,2] - if value - if ConfigTable.bool_config?(name) - raise InstallError, "config: --#{name} allows only yes/no for argument"\ - unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value - value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no' - end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value else - raise InstallError, "config: --#{name} requires argument"\ - unless ConfigTable.bool_config?(name) - value = 'yes' + evalopt.push [name, value] end - @config[name] = value + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) end end def parsearg_install - @options['no-harm'] = false - @options['install-prefix'] = '' + @config.no_harm = false + @config.install_prefix = '' while a = ARGV.shift case a - when /\A--no-harm\z/ - @options['no-harm'] = true - when /\A--prefix=(.*)\z/ - path = $1 + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' - @options['install-prefix'] = path + @config.install_prefix = path else - raise InstallError, "install: unknown option #{a}" + setup_rb_error "install: unknown option #{a}" end end end @@ -791,39 +955,31 @@ EOS out.puts " ruby #{File.basename $0} <global option>" out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]" - fmt = " %-20s %s\n" + fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' - out.printf fmt, '-h,--help', 'print this message' - out.printf fmt, '-v,--version', 'print version and quit' + out.printf fmt, ' --help', 'print this message' + out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' - out.puts out.puts 'Tasks:' TASKS.each do |name, desc| - out.printf " %-10s %s\n", name, desc + out.printf fmt, name, desc end + fmt = " %-24s %s [%s]\n" out.puts - out.puts 'Options for config:' - ConfigTable.each_definition do |name, (default, arg, desc, default2)| - out.printf " %-20s %s [%s]\n", - '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg), - desc, - default2 || default + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default end - out.printf " %-20s %s [%s]\n", - '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's" - + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts - out.puts 'Options for install:' - out.printf " %-20s %s [%s]\n", - '--no-harm', 'only display what to do if given', 'off' - out.printf " %-20s %s [%s]\n", - '--prefix', 'install path prefix', '$prefix' - + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end @@ -844,13 +1000,13 @@ EOS @installer.exec_install end + def exec_test + @installer.exec_test + end + def exec_show - ConfigTable.each_name do |k| - v = @config.get_raw(k) - if not v or v.empty? - v = '(not specified)' - end - printf "%-10s %s\n", k, v + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? end end @@ -862,36 +1018,45 @@ EOS @installer.exec_distclean end -end +end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller - include HookUtils - include HookScriptAPI include FileOperations - def initialize(ardir) + def initialize(ardir_root, config) super - @packages = all_dirs_in("#{@ardir}/packages") + @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs - eval_file_ifexist "#{@ardir}/metaconfig" + @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| - eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig" + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end + attr_reader :packages + + def packages=(list) + raise 'package list is empty' if list.empty? + list.each do |name| + raise "directory packages/#{name} does not exist"\ + unless File.dir?("#{@ardir}/packages/#{name}") + end + @packages = list + end + def init_installers @installers = {} @packages.each do |pack| - @installers[pack] = Installer.new(@config, @options, + @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end - with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| @@ -903,8 +1068,7 @@ class ToplevelInstallerMulti < ToplevelInstaller def extract_selection(list) a = list.split(/,/) a.each do |name| - raise InstallError, "no such package: #{name}" \ - unless @installers.key?(name) + setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end @@ -917,21 +1081,6 @@ class ToplevelInstallerMulti < ToplevelInstaller end # - # multi-package metaconfig API - # - - attr_reader :packages - - def declare_packages(list) - raise 'package list is empty' if list.empty? - list.each do |name| - raise "directory packages/#{name} does not exist"\ - unless File.dir?("#{@ardir}/packages/#{name}") - end - @packages = list - end - - # # Task Handlers # @@ -954,15 +1103,21 @@ class ToplevelInstallerMulti < ToplevelInstaller run_hook 'post-install' end + def exec_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + def exec_clean - rm_f 'config.save' + rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean - rm_f 'config.save' + rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' @@ -975,7 +1130,7 @@ class ToplevelInstallerMulti < ToplevelInstaller def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| - $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose'] + $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] @@ -983,28 +1138,32 @@ class ToplevelInstallerMulti < ToplevelInstaller end end + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this def verbose? - @options['verbose'] + @config.verbose? end + # module FileOperations requires this def no_harm? - @options['no-harm'] + @config.no_harm? end -end +end # class ToplevelInstallerMulti class Installer - FILETYPES = %w( bin lib ext data ) + FILETYPES = %w( bin lib ext data conf man ) - include HookScriptAPI - include HookUtils include FileOperations + include HookScriptAPI - def initialize(config, opt, srcroot, objroot) + def initialize(config, srcroot, objroot) @config = config - @options = opt @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' @@ -1014,8 +1173,11 @@ class Installer "#<#{self.class} #{File.basename(@srcdir)}>" end + def noop(rel) + end + # - # Hook Script API bases + # Hook Script API base methods # def srcdir_root @@ -1031,23 +1193,25 @@ class Installer end # - # configs/options + # Config Access # - def no_harm? - @options['no-harm'] + # module FileOperations requires this + def verbose? + @config.verbose? end - def verbose? - @options['verbose'] + # module FileOperations requires this + def no_harm? + @config.no_harm? end def verbose_off begin - save, @options['verbose'] = @options['verbose'], false + save, @config.verbose = @config.verbose?, false yield ensure - @options['verbose'] = save + @config.verbose = save end end @@ -1059,22 +1223,19 @@ class Installer exec_task_traverse 'config' end - def config_dir_bin(rel) - end - - def config_dir_lib(rel) - end + alias config_dir_bin noop + alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end - def extconf - opt = @options['config-opt'].join(' ') - command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}" - end + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop - def config_dir_data(rel) + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # @@ -1086,46 +1247,90 @@ class Installer end def setup_dir_bin(rel) - all_files_in(curr_srcdir()).each do |fname| - adjust_shebang "#{curr_srcdir()}/#{fname}" + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" end end - # modify: #!/usr/bin/ruby - # modify: #! /usr/bin/ruby - # modify: #!ruby - # not modify: #!/usr/bin/env ruby - SHEBANG_RE = /\A\#!\s*\S*ruby\S*/ + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop - def adjust_shebang(path) + def update_shebang_line(path) return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin - File.open(path, 'rb') {|r| - File.open(tmpfile, 'wb') {|w| - first = r.gets - return unless SHEBANG_RE =~ first - - $stderr.puts "adjusting shebang: #{File.basename path}" if verbose? - w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path')) - w.write r.read - } - } - move_file tmpfile, File.basename(path) + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end - def setup_dir_lib(rel) - end + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end - def setup_dir_ext(rel) - make if extdir?(curr_srcdir()) - end + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args - def setup_dir_data(rel) + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end end # @@ -1133,67 +1338,82 @@ class Installer # def exec_install + rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) - install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755 + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) - install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644 + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) - install_files ruby_extentions('.'), - "#{config('so-dir')}/#{File.dirname(rel)}", + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) - install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644 + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode) - mkdir_p dest, @options['install-prefix'] + mkdir_p dest, @config.install_prefix list.each do |fname| - install fname, dest, mode, @options['install-prefix'] + install fname, dest, mode, @config.install_prefix end end - def ruby_scripts - collect_filenames_auto().select {|n| /\.rb\z/ =~ n } + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) end - + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + # picked up many entries from cvs-1.11.1/src/ignore.c - reject_patterns = %w( + JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) - mapping = { - '.' => '\.', - '$' => '\$', - '#' => '\#', - '*' => '.*' - } - REJECT_PATTERNS = Regexp.new('\A(?:' + - reject_patterns.map {|pat| - pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] } - }.join('|') + - ')\z') - - def collect_filenames_auto - mapdir((existfiles() - hookfiles()).reject {|fname| - REJECT_PATTERNS =~ fname - }) - end def existfiles - all_files_in(curr_srcdir()) | all_files_in('.') + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles @@ -1202,27 +1422,49 @@ class Installer }.flatten end - def mapdir(filelist) - filelist.map {|fname| - if File.exist?(fname) # objdir - fname - else # srcdir - File.join(curr_srcdir(), fname) - end - } + def glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } end - def ruby_extentions(dir) - _ruby_extentions(dir) or - raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first" + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } end - DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/ + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } - def _ruby_extentions(dir) - Dir.open(dir) {|d| - return d.select {|fname| DLEXT =~ fname } - } + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run end # @@ -1231,53 +1473,51 @@ class Installer def exec_clean exec_task_traverse 'clean' - rm_f 'config.save' + rm_f @config.savefile rm_f 'InstalledFiles' end - def clean_dir_bin(rel) - end - - def clean_dir_lib(rel) - end + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end - def clean_dir_data(rel) - end - # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' - rm_f 'config.save' + rm_f @config.savefile rm_f 'InstalledFiles' end - def distclean_dir_bin(rel) - end - - def distclean_dir_lib(rel) - end + alias distclean_dir_bin noop + alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + # - # lib + # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| - if config('without-ext') == 'yes' and type == 'ext' + if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end @@ -1290,7 +1530,7 @@ class Installer dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') - all_dirs_in(curr_srcdir()).each do |d| + directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" @@ -1312,17 +1552,31 @@ class Installer @currdir = File.dirname(rel) end -end + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + if $0 == __FILE__ begin - if multipackage_install? - ToplevelInstallerMulti.invoke - else - ToplevelInstaller.invoke - end - rescue + ToplevelInstaller.invoke + rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." diff --git a/test/tc_commandhash.rb b/test/tc_commandhash.rb new file mode 100644 index 0000000..0969133 --- /dev/null +++ b/test/tc_commandhash.rb @@ -0,0 +1,96 @@ +require 'minitest/autorun' +require 'cmdparse' + +class CommandHashTest < Minitest::Test + def setup + @cmd = CmdParse::CommandHash.new + end + + def test_basic + assert_equal 0, @cmd.size + assert_nil @cmd['import'] + @cmd['import'] = 1 + assert_equal 1, @cmd['import'] + assert_equal 1, @cmd['i'] + assert_equal 1, @cmd['im'] + assert_equal 1, @cmd['imp'] + assert_equal 1, @cmd['impo'] + assert_equal 1, @cmd['impor'] + assert_nil @cmd['importer'] + + @cmd['implode'] = 2 + assert_equal 2, @cmd.size + + assert_equal 1, @cmd['import'] + assert_equal 2, @cmd['implode'] + assert_nil @cmd['impart'] + + assert_nil @cmd['i'] + assert_nil @cmd['im'] + assert_nil @cmd['imp'] + assert_equal 1, @cmd['impo'] + assert_equal 1, @cmd['impor'] + assert_equal 1, @cmd['import'] + assert_equal 2, @cmd['implo'] + assert_equal 2, @cmd['implod'] + assert_equal 2, @cmd['implode'] + end + + def test_edge_cases + @cmd['import'] = 1 + @cmd['important'] = 2 + + assert_equal 1, @cmd['import'] + assert_equal 2, @cmd['important'] + assert_nil @cmd['i'] + assert_nil @cmd['im'] + assert_nil @cmd['imp'] + assert_nil @cmd['impo'] + assert_nil @cmd['impor'] + assert_equal 2, @cmd['importa'] + assert_equal 2, @cmd['importan'] + + assert_nil @cmd['impart'] + end + + def test_integration + # define and setup the commands + cmd = CmdParse::CommandParser.new(handle_exceptions = false) + cmd.main_command.use_partial_commands( true ) + Object.const_set(:ImportCommand, Class.new(CmdParse::Command) do + def initialize() super('import', false) end + def execute(args) raise 'import' end + def show_help() raise 'import' end + end) + Object.const_set(:ImpolodeCommand, Class.new(CmdParse::Command) do + def initialize() super('implode', false) end + def execute(args) raise 'implode' end + def show_help() raise 'implode' end + end) + cmd.add_command( ImportCommand.new ) + cmd.add_command( ImpolodeCommand.new ) + + # simulate running the program + assert_raises(RuntimeError, 'import') {cmd.parse(['import'])} + assert_raises(RuntimeError, 'implode') {cmd.parse(['implode'])} + assert_raises(CmdParse::InvalidCommandError) {cmd.parse(['impart'])} + + assert_raises(CmdParse::InvalidCommandError) {cmd.parse(['i'])} + assert_raises(CmdParse::InvalidCommandError) {cmd.parse(['im'])} + assert_raises(CmdParse::InvalidCommandError) {cmd.parse(['imp'])} + assert_raises(RuntimeError, 'import') {cmd.parse(['impo'])} + assert_raises(RuntimeError, 'import') {cmd.parse(['impor'])} + assert_raises(RuntimeError, 'implode') {cmd.parse(['impl'])} + assert_raises(RuntimeError, 'implode') {cmd.parse(['implo'])} + assert_raises(RuntimeError, 'implode') {cmd.parse(['implod'])} + + # simulate the help command + cmd.add_command( CmdParse::HelpCommand.new ) + assert_raises(RuntimeError, 'import') {cmd.parse(['help', 'import'])} + assert_raises(RuntimeError, 'implode') {cmd.parse(['help', 'implode'])} + + cmd.main_command.use_partial_commands( false ) + assert_raises(CmdParse::InvalidCommandError, 'import') {cmd.parse(['impo'])} + assert_raises(CmdParse::InvalidCommandError, 'implode') {cmd.parse(['impl'])} + end +end |
