#! /usr/bin/ruby
# Test for optparse.  This comes from ((<URL:sample/getopts.rb>)) in
# distribution.

=begin 
= (({optparse}))サンプル
このスクリプトは(({optparse}))のサンプル用に、
(({Ruby}))に標準で入っている"(({sample/getopts.rb}))"を改造したものです。
=end

RCSID = %w$Id: getopts.test,v 0.5 2001/04/10 09:43:24 nobu Exp $[1..-1].each {|s| s.freeze}.freeze
=begin undocumented
== グローバル定数
: (({Version}))
  必須ではありませんが、定義されていると(({--version}))というオプションが使えるようになります。
=end
Version = RCSID[1].split('.').collect {|s| s.to_i}.freeze

=begin 
== ロード
まずロードします
=end

require './optparse'

=begin 
== デフォルト値
オプションのデフォルト値を設定しておきます。

ブロックで現れるローカル変数のスコープはブロックに限られてしまうので、
先に初期化しておく必要があります。

あるいは、グローバル変数かインスタンス変数にすればブロックに限定されないので、
初期化の必要はありませんが、やはりデフォルトの設定はあった方がいいでしょう。
((- Ruby 本でも「(({nil}))でも初期化せよ」と勧められていることですし。-))
=end
debug = version = false
f = g = nil
x, y = 800, 600

=begin 
== スクリプト名
メッセージに表示されるスクリプト名をベース名だけにしておきます。
この辺は趣味の問題でしょう。
=end
$0 = File.basename($0)

=begin 
== オプションブロック
(({ARGV.options}))にブロックを指定すると、
設定されている(({OptionParser}))インスタンスを(必要なら作って)渡してくれます。
返される値はブロックの返した値です。

また、このブロックの中では(({OptionParser::ParseError}))は(({rescue}))されて
エラーメッセージが(({STDERR}))に表示されます。このときは(({nil}))が返されます。
=end
ARGV.options {|q|
=begin 
=== バナー
ヘルプメッセージの最初に表示される文字列です。
これは単にそのまま使われます。
=end
  q.banner = "Usage: #{$0} -d [options] [string ...]\n"

=begin 
=== 引数をとらないオプション
--- -d --debug debug message
    (({-})) で始まる文字列は、短形式のオプションスイッチ、
    (({--})) で始まる文字列は、長形式のオプションスイッチを指定します。
    それ以外は、ヘルプメッセージに表示される説明です。
    長い行を分割する機能はありませんが、複数指定すれば一行ずつ表示されますので長い場合は分けておいてください。
    
    このオプションは引数をとりません。
    この場合 ((|debug|)) には (({true})) がセットされます。
    
    ブロック引数には、ローカル変数でなくとも代入可能なものなら何でも大丈夫なので、
    グローバル変数や外部の変数、インスタンス変数、アクセサなども使えます。
=end
  q.on('-d', '--debug', 'debug message') {|debug|}

=begin 
=== 引数が必須なオプションと引数の自動変換
--- --x
--- --y
    スイッチの指定に '=' が含まれていると、引数が必要なオプションになります。
    また、同時に(({Class}))を指定しておくと、
    引数は自動的に該当するオブジェクト(この場合は(({Integer})))に変換されてブロックに渡されます。
    引数がないときや引数が整数にマッチしないときは例外になります。
    
    変換できるクラスは(デフォルトでは)以下のものがあります。
    * Object	  任意の文字列(デフォルト)
    * String	  空でない文字列
    * Integer	  整数
    * Float	  実数
    * Numeric	  (({Integer}))か(({Float}))
    * TrueClass	  真偽値(指定されていれば(({true})))
    * FalseClass  真偽値(指定されていれば(({false})))
=end
  q.on('--x=X', 'x coordinate', Integer) {|x|}
  q.on('--y=Y', 'y coordinate', Integer) {|y|}

=begin 
=== 引数を省略可能なオプション
--- -f
    引数の指定を (({[]})) で囲っておくと、引数は省略可能とみなされます。
    引数が省略されたときには、(({nil}))が渡されます。
=end
  q.on('-f[=FLAG]', 'just a flag', TrueClass){|f|}

=begin 
=== 引数変換の指定
--- --geometry
    (({on}))に(({Regexp}))((({match}))を持っているオブジェクト) を渡すと、
    引数が一致しなければならないパターンとみなします。
    
    match にはオプションに対して与えられた引数の文字列が渡されます。
    この結果が(({nil}))か(({false}))の場合は不正な引数として例外になります。
    
    ブロックには(({match}))の結果を(({to_a}))したものが渡されます。
    (({Regexp}))を指定してマッチした部分文字列をブロックで使うというのが最も典型的な使い方でしょうが、
    この場合、文字列のまま渡されるので変換が必要になることもあります。
=end
  q.on('--geometry=GEOM', 'geometry XxY', /(\d+)x(\d+)/) {|geom, x, y|
    x, y = x.to_i, y.to_i
  }
=begin alternative
    分解された文字列を再度パースしなおさせる方法もあります。
      q.on('--geometry=GEOM', 'geometry XxY', /(\d+)x(\d+)/) {|geom, x, y|
        q.parse('--x', x, '--y', y)
      }
    または
      q.on('--geometry=GEOM', 'geometry XxY', /(\d+)x(\d+)/) {|geom, x, y|
        ARGV[0, 0] = ['--x', x, '--y', y]
      }
    これによって複数のオプションで指定する引数をまとめて指定できるオプションを作れます。
=end

=begin 
== パース
ここで実際にコマンドライン引数がパースされます。
=end
  q.parse!

=begin 
== オプションのチェック
最小限必要なオプションが指定されているかどうかチェックしています。
ブロックの最後の式なのでこの結果が(({ARGV.options}))の結果になります。

あるいは
オプション以外の引数の数があっていなかったらメッセージを表示して終了するという場合は
  ARGV.empty?
とか
  ARGV.size == 2
とします。
((-同時には指定できないオプションの組合せなどは、各々のハンドラの中でチェックすることもできます。-))
=end
  debug and (x or y)		# option check

} or (puts ARGV.options; exit 1)
=begin 
オプション指定にエラーがあったり、必要なオプションが指定されていなかった場合、
ここでヘルプメッセージを表示して終了しています。

(({ARGV.options}))のブロックの中で(({parse!}))した場合、
エラーメッセージが表示されるだけでスクリプト自体が終了しま((*せん*))。
=end

=begin 
== その他
オプション解析の結果を表示しています。
=end
if debug
  puts "version #{Version.join '.'}"
  puts "x = #{x.inspect}"
  puts "y = #{y.inspect}"
  puts "f = #{f.inspect}"
  puts "g = #{g}" if g
end

ARGV.each {|arg| print "other = ", arg, "\n"}

__END__
# $Log: getopts.test,v $
# Revision 0.5  2001/04/10 09:43:24  nobu
# * /usr/bin/ruby
#
# Revision 0.4  2000/05/03 14:48:24  nobu
# * adding documents
#
# Revision 0.3  2000-04-29 09:51:35+09  nobu
# Forgotten argument check(^^;
#
# Revision 0.2  2000-04-28 14:49:24+09  nobu
# Shows (({f})) and (({g})) themselves.
#
# Revision 0.1  2000-04-24 23:57:17+09  nobu
# Initial revision
#
