Ruby の標準ライブラリ

Ruby の標準ライブラリーの使い方です。最近ちょっと疲れ気味なので説明抜きの筆者のメモ書きです。

Readline の使い方

自作の Ruby プログラムでコマンドライン編集を可能にするためのライブラリです。readline 関数の第2引数が false, nil 以外ならヒストリー機能が使えます。

require "readline"
include Readline

while line = readline("> ", true)
  puts line
end

Mathn の使い方

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 の使い方

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 の使い方

Shell を使うと、シェルコマンドと同じ名前のメソッドが使えるようになります。引数は引用符で囲む必要があります。

$ irb
irb(main):001:0> require 'shell'
=> true
irb(main):002:0> ls "ruby"

strscan の使い方

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 の使い方

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 を使うと、環境変数の値をハッシュ ENV に読みこむ事ができます。

$ irb
irb(main):001:0> require 'Env'
=> true
irb(main):002:0> ENV['OSTYPE']
=> "linux-gnu"

Socket の使い方

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

HTTP プロトコル

Delegate の使い方

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 の使い方

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 の使い方

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 の使い方

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 でプログラミングするときの参考になるそうです。