Ruby の標準ライブラリーの使い方です。最近ちょっと疲れ気味なので説明抜きの筆者のメモ書きです。
自作の Ruby プログラムでコマンドライン編集を可能にするためのライブラリです。readline 関数の第2引数が false, nil 以外ならヒストリー機能が使えます。
require "readline" include Readline while line = readline("> ", true) puts line end
Mathn を利用すると分数計算が簡単にできます。
$ irb irb(main):001:0> require 'mathn' => true irb(main):002:0> a = 1/2 => 1/2 irb(main):003:0> b = 1/3 => 1/3 irb(main):004:0> (a + b) * 4/5 => 2/3
Complex を使うと複素数の計算ができます
$ irb irb(main):001:0> require 'complex' => true irb(main):002:0> a = Complex(1, 2) => Complex(1, 2) irb(main):003:0> puts a 1+2i
拡張ライブラリのメソッドを検索するプログラムです。ruby methods.rb "complex" のように使います。
# methods.rb ruby_lib = "/usr/local/lib/ruby/1.8/" file_name = ARGV[0] file_name = ruby_lib + file_name + ".rb" file = open(file_name) while(line = file.gets) print if line =~ /^\s*def/ end file.close
Shell を使うと、シェルコマンドと同じ名前のメソッドが使えるようになります。引数は引用符で囲む必要があります。
$ irb irb(main):001:0> require 'shell' => true irb(main):002:0> ls "ruby"
strscan は高速スキャナーです。
$ irb irb(main):001:0> require 'strscan' => true irb(main):002:0> s = StringScanner.new('This is an example string') => #<StringScanner 0/25 @ "This ..."> irb(main):003:0> while !s.eos? irb(main):004:1> s.skip(/\A\s+/) irb(main):005:1> if tmp = s.scan(/\A\w+/) irb(main):006:2> puts tmp irb(main):007:2> end irb(main):008:1> end This is an example string => nil
OptionParser はコマンドライン引数のオプションを取りだします。
require 'optparse' opt = OptionParser.new opt.on('-a') {|v| p v } opt.on('-b') {|v| p v } opt.parse!(ARGV) p ARGV ruby sample.rb -a foo bar -b baz # => true true ["foo", "bar", "baz"]
Env を使うと、環境変数の値をハッシュ ENV に読みこむ事ができます。
$ irb irb(main):001:0> require 'Env' => true irb(main):002:0> ENV['OSTYPE'] => "linux-gnu"
socket.so を使ってソケットのコネクションを作ります。
$ irb irb(main):001:0> require 'socket' => true irb(main):002:0> begin irb(main):003:1* soc = TCPSocket.new('www.mnet.ne.jp', 'http') irb(main):004:1> soc.puts 'GET /~tnomura/' irb(main):005:1> 5.times do irb(main):006:2* puts soc.gets irb(main):007:2> end irb(main):008:1> soc.close irb(main):009:1> end <HTML> <HEAD><TITLE>I love CUI</TITLE></HEAD> <BODY BGCOLOR="white"> <CENTER><H1>ノームラーのCUI大好き</H1></CENTER> => nil
delegate (委譲) は自分のクラスに他のクラスのメソッドを導入したいときに使います。継承と少し似ていますが、継承とは違って、自分のクラスのメンバーとリンクした他のクラスのオブジェクトを作り、そのオブジェクトに対して委譲したいメソッドを適用しているようです。あまり自信がない...
require 'delegate' class A def initialize(name) @name = name end attr_accessor :name def hello puts "hello, #{@name}" end end class B < DelegateClass(A) def initialize(name, id) @name = super(A.new(name)) @id = id end def show puts "#{@name.name} - #{@id}" end end b = B.new("James", "007") b.show b.hello b.name = "Dolly" b.show
Racc を使いたくてネットにアクセスしたが分かりませんでした。配布アーカイブのサンプルコード calc.y を Racc でコンパイルしたら動いたので、コメントをつけて分かったつもりにします。(Racc の使い方の記事を追加しました。Racc で遊ぼう)
calc.y は racc -ocalc.rb calc.y でコンパイルできます。作成された calc.rb は ruby calc.rb で動かすことができます。
calc.y のソース
# simple calc parser class Calcp # パーサのクラス名 prechigh # 演算子の優先順位 nonassoc UMINUS left '*' '/' left '+' '-' preclow rule # BNF記法による文法の記述開始 target: exp #ターゲットの定義 | /* none */ { result = 0 } ; exp: exp '+' exp { result += val[2] } #式とアクション(式の値)の定義 | exp '-' exp { result -= val[2] } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' exp ')' { result = val[1] } | '-' NUMBER = UMINUS { result = -val[1] } # UMINUS(負の数)の定義 | NUMBER # NUMBER(数値を表す定数) ; end ---- header ---- # calc.rb : generated by racc # オブジェクトファイルのヘッダー ---- inner ---- def parse( str ) # 構文解析用メソッド(parser)を記述 @q = [] # 取りだされた語句はキュー@qに収まる while str.size > 0 do case str when /\A\s+/o # 空白は読み跳ばす when /\A\d+/o # 数値の処理 @q.push [:NUMBER, $&.to_i] when /\A.|\n/o # 一文字の記号(演算子)の処理 s = $& @q.push [s, s] end str = $' # 語句を取りだした部分を切捨てる end @q.push [false, '$end'] # キューの終り do_parse # パーサーを呼び出す end def next_token # 次のトークンを取りだすメソッド @q.shift end ---- footer ---- parser = Calcp.new # パーサ・オブジェクトの生成 count = 0 scnt = 0 puts # 'Q' でプログラムを抜け出す puts 'type "Q" to quit.' puts while true do # メインループ(無限ループ) puts print '? ' str = gets.chop! break if /q/i === str begin val = parser.parse( str ) # 入力文をパースして値を出力 print '= ', val, "\n" rescue ParseError # パースエラー時の表示 puts $! rescue puts 'unexpected error ?!' # それ以外のエラーの処理 raise end end
YAML は、オブジェクトのデータをテキストファイルとして残すための規格です。Ruby のオブジェクトの配列は、項目の先頭に - 記号をスペースを1つ開けて記述することで表します。ハッシュの場合は、キー項目とデータとの間をコロンとスペースで区切って表します。また、[1, [2, 3]] のように入れ子になった構造のデータも先頭をスペースでインデントすることによって表すことができます。
Marshal のようなものですが、データがテキストファイルなのでエディタでデータの修正をすることができます。また、同じデータを他のプログラム言語でも使用することができ、データとプログラムの分離ができます。
Ruby 標準ライブラリの Syck を使えば、オブジェクトのデータを YAML に落したり、YAML のデータをオブジェクトにしたりすることができます。使い方は、require 'yaml' として、オブジェクトのデータを YAML にするときは、a.to_yaml とします。YAML のデータをオブジェクトにするには、a = YAML.load(yaml) のようにします。irb で実行すると次のようになります。
YAMLの記事を書きました。YAML で遊ぶ。
$ irb irb(main):001:0> a = <<END irb(main):002:0" - 1 irb(main):003:0" - 2 irb(main):004:0" END => "- 1\n- 2\n" irb(main):005:0> require 'yaml' => true irb(main):006:0> b = YAML.load(a) => [1, 2] irb(main):007:0> puts b.to_yaml --- - 1 - 2 => nil
次のプログラム to_yaml.rb を使うと色々なオブジェクトがどのように YAML として記述されるかが分かります。
#!/usr/bin/ruby require 'yaml' require 'readline' include Readline print "Type Q to quit\n\n" while line = readline("> ", true) break if /q/i =~ line a = nil eval("a = #{line}") puts a.to_yaml end
実行例
Type Q to quit > [1, [2, 3]] --- - 1 - - 2 - 3
改行のある文のデータは先頭に | をつけます。
$ irb irb(main):001:0> require 'yaml' => true irb(main):002:0> a = <<END irb(main):003:0" hello, world irb(main):004:0" how are you? irb(main):005:0" END => "hello, world\nhow are you?\n" irb(main):006:0> puts a.to_yaml --- | hello, world how are you?
複数のオブジェクトのデータを1つのファイルに記述できますが、異なるオブジェクトデータは --- で区切ります。また、複数のオブジェクトを一括して YAML に書き出すのは YAML.dump_stream() メソッドで、YAML ファイルから複数のオブジェクトを読みこむのは YAML.load_stream() メソッドです。下に irb によるテストの結果を示します。
$ irb irb(main):001:0> require 'yaml' => true irb(main):002:0> objects = [{'dog'=>'bow'}, {'cat'=>'mew'}] => [{"dog"=>"bow"}, {"cat"=>"mew"}] irb(main):003:0> str = YAML.dump_stream(*objects) => "--- \ndog: bow\n--- \ncat: mew\n" irb(main):004:0> puts str --- dog: bow --- cat: mew => nil irb(main):005:0> reld = YAML.load_stream(str) => #<YAML::Stream:0x403b3488 @documents=[{"dog"=>"bow"}, {"cat"=>"mew"}], @options={}> irb(main):006:0> reld.documents => [{"dog"=>"bow"}, {"cat"=>"mew"}]
次の実行例でも分かるように、YAML::dump_stream(*objects) は可変長引数です。
irb(main):002:0> a = {1=>2}; b = [3, 4] => [3, 4] irb(main):003:0> puts YAML.dump_stream(a, b) --- 1: 2 --- - 3 - 4
今のところ Syck の動作が遅いので、大量のデータ処理には向いていませんが、テキストファイルでオブジェクトのデータが記述できるので、設定ファイルにはうってつけのような気がします。様々な Ruby のアプリケーションが YAML で書いてあれば、ユーザは新しく設定ファイルの書き方を学習しないで済むので便利です。
YAML にはその他にも便利な機能があります。たとえば、同じ項目が何回も使用される場合、alias と anchor を使うことができます。alias は再利用する項目の前に &が先頭についた文字列を使います。&の変わりに * のついた文字列が anchor で、alias で指定された文字列と置換されます。
irb(main):008:0> str = <<END irb(main):009:0" - &girl Hanako irb(main):010:0" - Taro irb(main):011:0" - *girl irb(main):012:0" END => "- &girl Hanako\n- Taro\n- *girl\n" irb(main):013:0> YAML.load(str) => ["Hanako", "Taro", "Hanako"]
上の例は単純な置き換えですが、alias と anchor の効用は、もっと複雑なデータ構造に使われたときに発揮されます。
irb(main):024:0" - &fruits irb(main):025:0" apple: red irb(main):026:0" lemon: yellow irb(main):027:0" - green vegitables irb(main):028:0" - *fruits irb(main):029:0" END => "- &fruits\n apple: red\n lemon: yellow\n- green vegitables\n- *fruits\n" irb(main):030:0> YAML.load(str) => [{"apple"=>"red", "lemon"=>"yellow"}, "green vegitables", {"apple"=>"red", "lemon"=>"yellow"}]
また、YAML では、先頭の文書区切り --- の横に、!ruby/object:Class_name と書くことで Class_name オブジェクトのデータを記述できます。
irb(main):031:0> class Sample irb(main):032:1> def initialize irb(main):033:2> @name = nil irb(main):034:2> @age = nil irb(main):035:2> end irb(main):036:1> end => nil irb(main):037:0> str = <<END irb(main):038:0" --- !ruby/object:Sample irb(main):039:0" name: Taro irb(main):040:0" age: 30 irb(main):041:0" END => "--- !ruby/object:Sample\nname: Taro\nage: 30\n" irb(main):042:0> YAML.load(str) => #<Sample:0x40396068 @age=30, @name="Taro">
WEBrick は Ruby で HTTP サーバを動かすための標準ライブラリです。数行でサーバーが記述できてしまいます。WEBrick のホームページで紹介されている使い方を参考に試してみます。
まず、時刻を知らせるサーバー daytime.rb を次のように作成します。
#!/usr/bin/ruby require 'webrick' s = WEBrick::GenericServer.new( :Port => 2000 ) trap("INT"){ s.shutdown } s.start{|sock| sock.print(Time.now.to_s + "\r\n") }
コンソールから ruby daytime.rb でサーバーを起動した後、telent を使って次のようにアクセスします。
$ telnet localhost 2000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Wed May 03 10:56:02 JST 2006 Connection closed by foreign host.
趙単純 httpd は次のようにして作成できます。まず webrick ディレクトリを作りそのなかに htdocs ディレクトリと次のような httpd.rb プログラムを作成します。
#!/usr/bin/ruby require 'webrick' include WEBrick s = HTTPServer.new( :Port => 8080, :DocumentRoot => Dir::pwd + "/htdocs" ) trap("INT"){ s.shutdown } s.start
また htdocs ディレクトリには、次のような簡単な index.html を置いておきます。
<html> <head><title>index</title></head> <body> <p>hello, world</p> </body> </html>
準備ができたら ruby httpd.rb で httpd.rb で起動したら、別のターミナルを立ち上げて、w3m http://localhost:8080 で index.html を表示させます。httpd.rb は ^C で終了することができます。
CGI も使えるようになっているようです。次のプログラムを hello.cgi というファイル名で htdocs に作成し、chmod 755 hello.cgi で実行可能にしておきます。別ターミナルから w3m http://localhost:8080/hello.cgi とすると、CGIが使えるのが分かります。
#!/usr/bin/ruby print "Content-type: text/html\n" print "\n" print "<html>\n" print "<head>\n" print "<title>test</title>\n" print "<body>\n" print "this is a test of CGI\n" print "</body>\n" print "</html>\n"
Web アプリケーションには WEBrick ではサーブレットを使います。次のような servlet.rb を作成して、ruby servlet.rb で起動し、別のターミナルから w3m http://localhost:8080/hello とアクセスすると、"hello, world" と表示されます。
#!/usr/bin/ruby require 'webrick' include WEBrick s = HTTPServer.new( :Port => 8080 ) class HelloServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res.body = '<HTML>hello, world.</HTML>' res['Content-Type'] = 'text/html' end end s.mount('/hello', HelloServlet) trap('INT'){ s.shutdown } s.start
サーブレットを作成するためには、HTTPServlet::AbastractServlet クラスから、サーブレット HelloServlet を導出して、そのクラスに do_GET のようにリクエストの名前に関連したメソッドを定義します。それからサーバオブジェクト s の mount メソッドを使って、URI と サーブレットクラスを登録すると、ブラウザからの GET リクエストにたいして do_GET メソッドが実行されます。
WEBrick は動作が遅いそうですが、だれでも簡単に http サーバを作ることができるのは魅力です。また、WEBrick のソースは非常にきれいなので Ruby でプログラミングするときの参考になるそうです。