標準添付ライブラリ
- 標準添付ライブラリはRubyに付属しているライブラリ
- 組み込みライブラリと違う点はrequire等でライブラリをロードする必要がある
- Ruby技術者認定試験の出題範囲はGold
pp []
=> NoMethodError: undefined method 'pp'
require 'pp'
=> true
pp []
[]
=> []
StringIO
- 文字列をIOクラスと同じインターフェイスで取り扱う為のクラス
- IOクラスとの継承関係はないオブジェクト内のバッファに文字列を格納したり取り出したりする
バッファとポインタ
あれ?(少なくとも)Ruby2.4ではrequireしなくても使えたわ
バッファ | StringIO#string | デフォは “” |
ポインタ | StringIO#pos | デフォは 0 |
sio = StringIO.new
sio.string
=> ""
sio.pos
=> 0
- StringIO#string は設定も取得もできる
- 設定するとポインタは0に戻るっぽい(modeによるのかも)
sio = StringIO.new
sio.string
=> ""
sio.pos
=> 0
sio.string = '12345'
sio.pos
=> 0
sio.gets
=> "12345"
sio.pos
=> 5
StringIO#puts
- 引数の文字列をバッファへ格納し、ポインタを移動する
- 格納されるバッファには改行が付加される(put系ぽい)
- ポインタはバッファ上にて追加された文字列と改行の後に移動する
sio = StringIO.new
=> #<StringIO:0x007fdf310478c0>
sio.puts('12345')
=> nil
sio.string
=> "12345\n"
sio.pos
=> 6
StringIO#gets
- 現在のポインタから改行(もしくは末尾)までの文字列を返し、ポインタを移動する
- 返される文字列には改行が付加される(put系ぽい)
- ポインタはバッファ上にて、返した文字列と改行の後に移動する
sio = StringIO.new
=> #<StringIO:0x007fdf310478c0>
sio.puts('12')
sio.puts('34')
sio.string
=> "12\n34\n"
sio.pos
=> 6
sio.gets
=> nil
sio.pos = 0
sio.gets
=> "12\n"
sio.pos
=> 3
インスタンス生成
new | インスタンスを返す |
open | ブロックの評価結果を返す |
open | ブロックを取らない場合は new と同じ |
StringIO.new(string = '', mode = 'r+')
StringIO.open(string = '', mode = 'r+')
StringIO.open(string = '', mode = 'r+') { |sio| ... }
mode r
- 読み込みモード
- ポインタは先頭を指す
StringIO.open('1234', 'r') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
sio.puts('a')
#=> IOError: not opened for writing
end
=> "string:1234, pos:0"
mode w
- 書き込みモード
- バッファは空になる
- 当然ポインタは先頭
StringIO.open('1234', 'w') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
p sio.gets
#=> IOError: not opened for reading
end
=> "string:, pos:0"
mode a
- 書き込みモード
- ポインタは末尾を指す
- ・・ポインタは末尾じゃなくて0を指しているように見えるけど、putsしたら末尾に出力するし、ポインタも末尾に移動したんでまぁいいか
StringIO.open('1234', 'a') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
sio.puts('AB')
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:1234, pos:0"
=> "string:1234AB\n, pos:7"
mode r+
- 読み書きモード
- ポインタは先頭を指す
StringIO.open('1234', 'r+') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
sio.puts('AB')
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:1234, pos:0"
=> "string:AB\n4, pos:3"
mode w+
- 読み書きモード
- バッファは空になる
StringIO.open('1234', 'w+') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:, pos:0"
mode a+
- 読み書きモード
- ポインタは末尾を指す
- ・・ポインタは末尾じゃなくて0を指しているように見えるけど、putsしたら末尾に出力するし、ポインタも末尾に移動したんでまぁいいか
StringIO.open('1234', 'a+') do |sio|
p "string:#{sio.string}, pos:#{sio.pos}"
sio.puts('AB')
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:1234, pos:0"
=> "string:1234AB\n, pos:7"
StringIO#putc
- 現在のポインタの位置に1文字だけ書き込む
- 引数に2文字以上を渡しても先頭以外は無視される
- もちろんポインタは1つずれる
StringIO.open('1234', 'r+') do |sio|
sio.pos = 2
p sio.putc('AB')
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:12A4, pos:3"
String#print StringIO#puts
- String#print と StringIO#puts は殆ど同じ動作をする(どうやらウソのようです)
- 末尾に改行を付加するかどうかが違う
- 複数の引数、もしくは配列を取る
StringIO.open do |sio|
p sio.puts('A', 'B')
p sio.puts(['C', 'D'])
p sio.print(['E', 'F'])
p "string:#{sio.string}, pos:#{sio.pos}"
end
=> "string:A\nB\nC\nD\n[\"E\", \"F\"], pos:18"
StringIO#printf
まぁいつものアレ
StringIO.open do |sio|
p sio.printf('%02d', 1)
p "string:#{sio.string}, pos:#{sio.pos}"
end
"string:01, pos:2"
StringIO#read
- 現在のポインタから引数に指定した文字数分、文字を取り出す
- その前に終端になったらそこまで返す
- 既にポインタが終端を指していたら nil を返す
- 第1引数を省略したなら
- 終端までを返す
- 既にポインタが終端だったら空文字が返る
- 第2引数を指定したらその変数に取り出した文字列を格納する(あたまおかしい?)
ぶっちゃけややこしい
StringIO.open('1234') do |sio|
p sio.read
end
=> "1234"
StringIO.open('1234') do |sio|
sio.pos = 2
p sio.read
end
=> "34"
StringIO.open('1234') do |sio|
sio.pos = 2
p sio.read(1)
end
=> "3"
StringIO#getc StringIO#readchar
現在のポインタから1文字取得する
StringIO.open('123') do |sio|
sio.pos = 1
p sio.getc
p sio.getc
p sio.getc
end
=> "2"
=> "3"
=> nil
readchar は似たような動作だが、終端でエラーが発生する
StringIO.open('123') do |sio|
sio.pos = 1
p sio.readchar
p sio.readchar
p sio.readchar
end
=> "2"
=> "3"
=> EOFError: end of file reached
StringIO#gets StringIO#readline
現在のポインタから改行までの文字列を取得する
StringIO.open("123\n456") do |sio|
sio.pos = 1
p sio.gets
p sio.gets
p sio.gets
end
=> "23\n"
=> "456"
=> nil
readline は似たような動作だが、終端でエラーが発生する
StringIO.open("123\n456") do |sio|
sio.pos = 1
p sio.readline
p sio.readline
p sio.readline
end
=> "23\n"
=> "456"
=> EOFError: end of file reached
StringIO#pos= StringIO#seek
ポインタを移動する
- pos= は先頭を0とした絶対位置に移動する(マイナスは指定できないぽい)
- seek は第2引数を基準として相対位置に移動する
- IO::SEEK_SET ファイル先頭(デフォ)
- IO::SEEK_CUR 現在のポインタ位置
- IO::SEEK_END ファイル末尾
StringIO.open("123\n456") do |sio|
sio.seek(-2, IO::SEEK_END)
p sio.getc
end
=> "5"
YAML
- 簡単な記法でデータの階層構造を表記できるフォーマット
- ハッシュ・配列の組み合わせをスペース・インデントで表現する
require 'yaml'
yaml_data = <<-DATA
data_array:
- a
- b
- c
data_key_value:
key1: value1
key2: value2
DATA
yaml = YAML.load(yaml_data)
=> {"data_array"=>["a", "b", "c"], "data_key_value"=>{"key1"=>"value1", "key2"=>"value2"}}
yaml.class
=> Hash
YAML.load_file
ファイルを直接読み込んでオブジェクトを生成する
require 'yaml'
YAML.load_file('path/to/yaml_file')
YAML.load_stream
- --- までが一つのデータの区切りとみなされ、通常(YAML.load等)は以降は無視される
- が、YAML.load_stream はデータを順番にYAML::stream で返す
- ・・・らしいが、何かそんなインスタンスじゃないのが返って来るんだけど? 文字列を渡した時とIOインスタンスを渡した時で変わる?
require 'yaml'
yaml_data = <<-DATA
- a
---
- b
DATA
yaml = YAML.load(yaml_data)
=> ["a"]
yaml.class
=> Array
yaml = YAML.load_stream(yaml_data)
=> [["a"], ["b"]]
yaml.class
=> Array
IOインスタンスを渡す方が実際の使い方?
yaml = YAML.load_stream(open('path/to/yaml_file'))
YAML.load_document
こんなことも出来るらしいけど、ホントこの書籍はやる気ないわ 俺のやる気も失せるわ
YAML.load_document(open('path/to/yaml_file')) do |yaml|
yaml.first
end
YAML.dump
オブジェクトをyaml形式の文字列にして返す
moko = {
homo: true,
homos: [
:a,
:b,
]
}
YAML.dump(moko)
=> "---\n:homo: true\n:homos:\n- :a\n- :b\n"
第2引数にIOオブジェクトを渡すとそちらに書き込む
moko = {
homo: true,
homos: [
:a,
:b,
]
}
sio = StringIO.new
YAML.dump(moko, sio)
sio.string
=> "---\n:homo: true\n:homos:\n- :a\n- :b\n"
YAML.dump_stream
- 複数のオブジェクトを引数に取れる
- からなんだってんだよな
moko = {
homo: true,
homos: [
:a,
:b,
]
}
YAML.dump_stream(moko, moko.dup)
=> "---\n:homo: true\n:homos: &1\n- :a\n- :b\n---\n:homo: true\n:homos: *1\n"
JSON
- Ruby1.9から標準添付ライブラリに追加された
- YAMLみたいに簡単な記法でデータの階層構造を表現できる
- to_str, to_io, readメソッドを持つオブジェクトなら文字列以外も渡せる(えーと?)
JSON.parse JSON.load
require 'json'
json_data = <<-DATA
{"homo": true, "homos": ["a", "b"]}
DATA
json = JSON.parse(json_data)
=> {"homo"=>true, "homos"=>["a", "b"]}
json.class
=> Hash
JSON.parse は第2引数に 要素を読み込む毎のオプションがあったりする
- max_nesting : 入れ子になっているデータの最大の深さを指定
- symbolize_names: 真を指定するとハッシュのキーを文字列ではなくシンボルに
JSON.load は第2引数に 要素を読み込む毎に呼び出されるProcオブジェクトを渡せる 渡した所で何の役に立つのか謎 今回の例で言うと配列を順番に渡し、最後に配列を渡す。jsonの評価結果に影響を及ぼさないなら何の意味が・・?
require 'json'
json_data = <<-DATA
[1, 2]
DATA
json = JSON.load(json_data, lambda { |i| p i * 2 })
=> 2
=> 4
=> [1, 2, 1, 2]
=> [1, 2]
json
=> [1, 2]
JSON.parse(IO)
JSON.load_file 的なメソッドは用意されていないが、IOから読み込むことが出来る
require 'json'
File.open('path/to/json_file') do |f|
JSON.load(f)
end
require 'json'
sio = StringIO.new('["a", "b"]')
JSON.load(sio)
=> ["a", "b"]
JSON.dump(object, io = nil, imit = nil)
- 第2引数を省略すると文字列として取得
- ioを指定するとIOに書き込む
- 第3引数limitはダンプするオブジェクトの深さを制限することが出来る
require 'json'
sio = StringIO.new()
JSON.dump({hoge: ["a", "b"]}, sio)
sio.string
=> "{\"hoge\":[\"a\",\"b\"]}"
CSV
CSV.parse
- CSV.parse(csv_data)
-
CSV.parse(csv_data) { row … }
ブロックを渡すと行毎に処理ができる
require 'csv'
csv_data = <<-DATA
a,b,c
d,e,f
DATA
csv = CSV.parse(csv_data)
=> [["a", "b", "c"], ["d", "e", "f"]]
require 'csv'
csv_data = <<-DATA
a,b,c
d,e,f
DATA
csv = CSV.parse(csv_data) do |row|
p row
end
=> ["a", "b", "c"]
=> ["d", "e", "f"]
CSV.open
- CSV.open(‘path/to/csv_file’, option = {})
- CSV.open(‘path/to/csv_file’, option = {}) { |row| … }
- CSV.open(‘path/to/csv_file’, mode = ‘rb’, option = {})
-
CSV.open(‘path/to/csv_file’, mode = ‘rb’, option = {}) { |row| … }
- ブロックを渡すと行毎に処理ができる
- オプションで改行コードを指定したりできる
mode
r | 読み込み |
w | 書き込み |
rb | バイナリ書き込み |
wb | バイナリ書き込み |
require 'csv'
CSV.open('path/to/csv_file') do |row|
row.each do |col|
col
end
end
CSVデータの書き込み
ブロックを渡すパターンと渡さないパターン
require 'csv'
CSV.open('temp1.csv', 'w') do |row|
row << [1, 2, 3, 4]
row << [5, 6, 7, 8]
end
csv = CSV.open('temp2.csv', 'w')
csv << [1, 2, 3]
csv << [4, 5, 6]
csv.close
$ cat temp1.csv
1,2,3,4
5,6,7,8
$ cat temp2.csv
1,2,3
4,5,6
CSV.foreach CSV.read CSV.readlines
- CSV.foreach ブロック引数にEnumeratorオブジェクトで各行を受け取る
- CSV.read CSV.readlines 内部の処理は違うが結局は配列の配列を受け取る
正直、何のために複数のメソッドになっているのか謎
FileUtils
- 基本的なファイル操作を集めたライブラリ
- ファイルやディレクトリの操作はDirやFileでも行えるが、FileUtilsはUNIXコマンドライクに呼び出しやすく扱いやすくしたもの
- だからこそ、どっちを使うのか、今どっちを使っているのかちゃんと意識すべし
- FileUtilsはモジュール関数として実装されているのでincludeしてもよし
- 殆どのコマンドはoptionとして (noop: true(実際には実行しない), verbose: true(詳細をコンソール表示)) 等が渡せる
- ぶっちゃけ ファイル、ディレクトリ操作は FileUtils を使えばいいんだよ
FileUtils.cd(dir, options = {})
カレントディレクトリを引数dirに変更する ブロックを渡すとブロック内部のみカレントディレクトリが変わる
FileUtils.pwd
カレントディレクトリを文字列で返す
require 'fileutils'
FileUtils.pwd
=> "/Users/mokoaki/xxxx/xxxx"
FileUtils.cd('/Users') do |a|
p a
p FileUtils.pwd
end
=> "/Users"
=> "/Users"
FileUtils.pwd
=> "/Users/mokoaki/xxxx/xxxx"
FileUtils.cp FileUtils.copy(src, dest, options = {})
- エイリアスで同じ動作
- ファイルをコピーする
- srcがファイルでなければエラー
- destがファイルならそのファイルとして上書き、destがディレクトリならそのディレクトリ以下にsrcをコピーする
FileUtils.cp_r(src, dest, options = {})
- 再帰的にファイルをコピーする
- srcをdestにコピーする
- srcがディレクトリであったら再帰的にコピーする
- destがディレクトリならdest/srcにコピーする
- srcを配列にて指定する事も出来る
FileUtils.mv FileUtils.move(src, dest, options = {})
- エイリアスで同じ動作
- ファイルを移動する あれ?ディレクトリは?
- srcを配列にて指定する事も出来る
- srcが1つの場合
- descが存在しない場合、それがファイル名であるかのように移動される
- descが存在する
- descがディレクトリの場合、srcをdest/srcに移動する
- descがファイルの場合、上書きする
- srcに配列を指定した場合
- destがディレクトリでなければエラー
- descがディレクトリであれば移動 src[0] => dest/src[0], src[1] => dest/src[1]
ぶっちゃけややこしい!
FileUtils#rm FileUtils#remove(list, options = {})
- エイリアスで同じ動作
- ファイルを消す
- ディレクトリを指定するとエラー
- listを配列にて指定する事も出来る
FileUtils#rm_dir(list, options = {})
- エイリアスで同じ動作
- ディレクトリを消す
- ファイル指定するとエラー
- listを配列にて指定する事も出来る
FileUtils#rm_r FileUtils#rm_fr(list, options = {})
- FileUtils#rm_fr は FileUtils#rm_r(list, options = {force: true}) と同じ
- ファイル、ディレクトリを再帰的に消す
- listを配列にて指定する事も出来る
FileUtils#touch(list, options = {})
- ファイルの最終変更時刻、アクセス時刻を更新する
- ファイルが存在しなければ新規作成される
- listを配列にて指定する事も出来る
FileUtils#mkdir FileUtils#mkdir_p(list, options = {})
- ディレクトリを作る
- pの方はディレクトリが存在してもエラーにならない
- pの方は2階層以上深いディレクトリを指定しても再帰的に作成してくれる
- listを配列にて指定する事も出来る
FileUtils#chown(user, group, list, options = {})
- ファイルまたはディレクトリの所有権を変更する
- listを配列にて指定する事も出来る
FileUtils#chmod(mode, list, options = {})
- ファイルまたはディレクトリのパーミッションを変更する
- listを配列にて指定する事も出来る
FileUtils#ln_s FileUtils#symlink(src, dest, options = {})
- エイリアスで同じ動作
- ファイルまたはディレクトリ(src)のシンボリックリンク(dest)を作成する
- srcに配列を指定した場合
- destがディレクトリでなければエラー
- descがディレクトリであれば移動 src[0] => dest/src[0], src[1] => dest/src[1]
FileUtils#ln FileUtils#link(src, dest, options = {})
- エイリアスで同じ動作
- ファイルまたはディレクトリ(src)のハードリンク(dest)を作成する
- srcに配列を指定した場合
- destがディレクトリでなければエラー
- descがディレクトリであれば移動 src[0] => dest/src[0], src[1] => dest/src[1]
ネットワーク
この辺りは辛い!けどがんばれ!
クラスツリー
- IO
- BaseSocket
- IPSocket
- TCPSocket
- TCPServer
- SOCKSSocket
- UDPSocket
- TCPSocket
- UNIXSocket
- UNIXServer
- Socket
- IPSocket
- BaseSocket
IO | IOクラス |
BaseSocket | ソースを表す抽象クラス |
IPSocket | インターネットドメインソケットの抽象クラス |
TCPSocket | TCP通信を扱うのに適したインターフェイスを提供 |
TCPServer | ↑そのサーバ側のソケットのクラス |
SOCKSSocket | TCPSocketをSOCKS対応したクラス(?)使用にはコンパイル時にオプションが必要 |
UDPSocket | UDP通信を扱うのに適したインターフェイスを提供 |
UNIXSocket | UNIXドメインソケットによるプロセス間通信を扱うのに適したインターフェイスを提供 |
UNIXServer | ↑そのサーバ側 |
Socket | ホスト間、プロセス間の通信を行う通信ソケットを扱うクラス。IOクラスを継承しており、IOクラスを同じインターフェイスでデータの送受信を行うことができる |
TCPServer
- TCPをサーバ用途で使用する
- ブラウザで http:localhost:10080 にアクセスすると文字列を表示する
- なんかChromeだとエラーになる、厳密にエラーを見てるとかそんな感じがする?
require "socket"
server = TCPServer.new(10080)
loop do
client = server.accept # この行でクライアントからの接続を待機する
client.puts('Hello TCPServer.')
client.puts(Time.now.strftime('%Y/%m/%d %T %N'))
client.close
end
TCPSocket
- TCPをクライアント用途で使う
- 上記で作成したTCPServerにアクセスすると文字列が返ってくる
require "socket"
socket = TCPSocket.new('localhost', 10080)
while line = socket.gets
puts line
end
UDPを扱うクラス
- UDPを扱う際にはサーバー、クライアント用の区別はなく、UDPSocketクラスを使う
- TCPに比べ、コネクションレスで伝達ミスの確認をしない為オーバーヘッドが少なく素早い通信が可能
- データの送信ミスがあっても大きな影響がない動画配信などで使われる
UDPSocketクラスのインスタンスの生成を行う
UDPSocket.new([socktype])
socktype にはアドレスファミリーと呼ばれるネットワークアドレスの種類を指定する
Socket::AF_INET | IPv4ネットワーク (デフォ) |
Socket::AF_INET6 | IPv6ネットワーク |
定義したソケットをホストのポートに関連付ける
socket.bind(host, port)
- パケットの受信にBasicSocketに定義されているrecvメソッドを利用する
- ソケットからデータを受け取り文字列として返す 引数には受け取るデータの長さを指定する
- flagsには受信データ処理のオプションを指定する
socket.recv(max_packet[, flags])
UDPSocketを使い、10000ポートでUDP通信を待ち受ける
require 'socket'
max_packet = 1024
socket = UDPSocket.new
socket.bind('0.0.0.0', 10000)
print socket.recv(max_packet)
- UDPでデータ送信するには2つの方法がある
- connectメソッド
- UDPサーバに接続してからsendメソッドを使用する
- sendメソッド
- 接続先とポートを指定する
- 引数のdest_socketaddrはSocket.pack_sockaddr_inを使って生成したソケットアドレス構造体を指定する(?)
- connectメソッド
UDPSocketを使い、10000ポートに対してデータを送信する
- 上記で作成したUDPサーバにアクセスし文字列を表示させる事ができる
require 'socket'
socket = UDPSocket.new
socket.send("Hello UDP world.\n", 0, 'localhost', 10000)
socket.close
プロセス間通信を制御するクラス
- UNIXServer, UNIXSocketクラスはの名前の通り、UNIX系OSで使用できるクラスでWindowsでは使用できない
- 通常、プロセスは独立したアドレス空間で動作している為、プロセス間でデータを共有したい場合や情報のやり取りにはプロセス間通信を使用する
- Rubyはプロセス間通信の為にこの2つのクラスを用意している。C言語ではTCP/IPにおけるデータ通信もSocket関数を使いその中で通信方式を指定するが、Rubyでは別のクラスに分離することで用途が明確になっている
- TCPServerとTCPSocketとほぼ同じように使える
- TCPServerではホスト名とポートを指定したが、UNIXServerでは任意のパス名を指定する
UNIXServerを使ってプロセス間通信の待ち受けを行う
実行するとカレントディレクトリにソケットファイルが作成される。プロセス間通信はをのソケットを経由して行われる
require 'socket'
require 'fileutils'
socket_path = 'test_socket'
# 同名ソケットは削除する
if File.exist?(socket_path)
FileUtils.rm(socket_path)
end
server = UNIXServer.new(socket_path)
loop do
client = server.accept # この行でクライアントからの接続を待機する
client.puts('Hello UNIXServer.')
client.puts(Time.now.strftime('%Y/%m/%d %T %N'))
client.close
end
UNIXSocketを使ってプロセス間通信にてデータの取得を行う
require 'socket'
socket_path = 'test_socket'
socket = UNIXSocket.new(socket_path)
print socket.gets
print socket.gets
uri
- uriはURI(Uniform Resouce Identifier、ネットワーク上のリソースを表現)を扱うライブラリ
- uriとurlの事はぐぐれ
スキームってなんぞ
プロトコル毎に、どんなふうに並べて書くかの決まり
スキーム | ユーザ情報 | ホスト情報 | ポート番号 | パス | ||
https:// | username:password | @ | example.com | : | 21 | /public |
- スキームは管理組織に登録されているものや非公式なスキームもある
- Rubyが対応しているスキーム http https ftp ldap ldaps mailto
- これらのスキームのパーサは自分でどのスキームなのか判断し、パーサしてくれる
URI.parse(uri_string) URI(uri_string)
- スキームがhttpなので、内部ではURI::HTTPクラスのパーサが個々の要素に分割する
- RFCの規格に従って厳格にパースする為、使用できない文字などが含まれていたりするとエラーを返す
- URL(uri_string) は内部で URI.parse(uri_string) を呼び出す
require 'uri'
uri = URI.parse('http://username:pass@example.com:80/index.html')
uri.scheme #=> "http"
uri.user #=> "username"
uri.password #=> "pass"
uri.host #=> "example.com"
uri.port #=> 80
uri.path #=> "/index.html"
URI.split(url_string)
[scheme, userinfo, host, port, registory, path, opaque, query, fragment] を返す
require 'uri'
p URI.split('http://username:pass@example.com:80/index.html')
=> ["http", "username:pass", "example.com", "80", nil, "/index.html", nil, nil, nil]
URI.escape(string) URI.encode(string)
- URIで使用できるASCII以外の文字データは16進数で表記したバイトコードを[%xx]という形で表記するように定義されている
- デフォルトの文字エンコードによってエンコードされる
- エンコードを指定する場合は先に変換しておく必要がある
- 引数のunsafeにはURIに使用できない文字を指定するが、デフォルトで使用される URI::UNSAFE で十分であり、通常指定する必要な無い
require 'uri'
p URI.encode('http://example.com/モコ')
=> "http://example.com/%E3%83%A2%E3%82%B3"
p URI.encode('http://example.com/モコ'.encode('EUC-JP'))
=> "http://example.com/%A5%E2%A5%B3"
URI.unescape(str) URI.decode(str)
- URLエンコードされた文字列を元の文字列に戻す
- 「このメソッドは obsolete です。代わりに CGI.unescape, URI.decode_www_form, URI.decode_www_form_component などの使用を検討してください」だそうです
require 'uri'
p URI.decode('http://example.com/%E3%83%A2%E3%82%B3')
=> "http://example.com/モコ"
p URI.unescape('http://example.com/%A5%E2%A5%B3').force_encoding('EUC-JP').encode('UTF-8')
"http://example.com/モコ"
net/http
- Netモジュール以下にはネットワーク上でよく使われているプロトコルに対応したサーバと通信するためのクライアントライブラリが用意されている
- net/http はその中の一つでその名の通りHTTPを扱う
- HTTPのリクエストデータの組み立て、サーバへの通信、レスポンスデータの処理などを行ってくれる
- net/http は以下の3つのクラスで構成されている
Net::HTTP | HTTPクライアントの為のクラス |
Net::HTTPRequest | HTTPリクエストの為のクラス |
Net::HTTPResponse | HTTPレスポンスの為のクラス |
- HTTPは
- TCPの80番ポートを使い通信を行うテキストベースのプロトコル
- クライアントから取得対象へのパスとメソッド(get, post..)を指定し、リクエストヘッダ、リクエストボディと一緒に送信する
- サーバはクライアントからの命令を解釈し、処理結果をクライアントに返す
接続されていないインスタンスの作成
Net:HTTP.new(address, port = 80, proxy_addr = nil, proxy_port = nil)
接続オープン、クローズ
ブロックを渡すと処理終了時に自動でfinishしてくれる
- #start, #finish
-
#start { http … }
接続されたインスタンスを作成
ブロックを渡すと処理終了時に自動でfinish
Net:HTTP.start(address, port = 80, proxy_addr = nil, proxy_port = nil)
Net:HTTP.start(address, port = 80, proxy_addr = nil, proxy_port = nil) { |http| ... }
コンテンツの取得 get(path, header = nil)
- GETメソッドを利用してコンテンツの取得を試みる
- headerはHashで渡せる
- 接続がクローズされていた場合、自動的にオープンする
- 戻り値は HTTPRequestインスタンスが返る
コンテンツの取得 Net::HTTP.get(address, path, port = 80)
- インスタンスを作らずにコンテンツを取得のみ行い、文字列を返す
- というのは昔の話で、最近は Net::HTTPOK インスタンスを返してくるらしい
未接続のインスタンスを作成してからコンテンツを取得する
require 'net/http'
net = Net::HTTP.new('example.com')
net.start
response = net.get('/')
net.finish
p res.body
#=> htmlがダラダラと流れる
上記の例にブロックを渡してfinish自動版
require 'net/http'
net = Net::HTTP.new('example.com')
net.start do |http|
response = http.get('/')
p response.body
#=> htmlがダラダラと流れる
end
接続済のインスタンスを作成してからコンテンツを取得する
require 'net/http'
net = Net::HTTP.start('example.com')
response = net.get('/')
net.finish
p response.body
#=> htmlがダラダラと流れる
上記の例にブロックを渡してfinish自動版
require 'net/http'
Net::HTTP.start('example.com') do |http|
response = http.get('/')
p response.body
#=> htmlがダラダラと流れる
end
Net::HTTP.get で直接コンテンツを取得
require 'net/http'
responce = Net::HTTP.get('example.com', '/')
response.class
=> Net::HTTPOK
p response.body
#=> htmlがダラダラと流れる
コンテンツの取得 post(path, data, header = nil, dest = nil)
- 今度はPOSTメソッド
- リクエスト送信時にdata文字列を一緒に送信する
- 戻り値は HTTPResponseインスタンス
- destには « メソッドを持つインスタンスを指定でき、レスポンスをdestに書き込む
↓すんませんテストできてないです
require 'net/http'
Net::HTTP.start('localhost') do |http|
response = http.post('/', '')
p response.body
end
Net::HTTP.start('localhost') do |http|
response = []
http.post('/', '', nil, response)
p response
end
HTTPRequest
- コンテンツ取得時にBASIC認証されたり、細かいリクエストのパラメータを調整したい時に使う
- Net::HTTP 自体は抽象クラスで、以下の4つのクラスに実装されている
- Net::HTTP::Get
- Net::HTTP::Post
- Net::HTTP::Head
- Net::HTTP::Put
require 'net/http'
http = Net::HTTP.new('www.mokoaki.net', 80)
request = Net::HTTP::Get.new('/')
request[:hage] = 'hage-!' # ヘッダを指定してみたり
request.basic_auth('user_name', 'password') #BASIC認証用のヘッダを付加してみたり
response = http.request(request)
p response.body
#=> htmlがダラダラと流れる
HTTPResponse
- Net::HTTP.get 等のメソッドの返り値は Net::HTTPResponseのインスタンス
- もちろんステータスやメッセージ、レスポンスヘッダを参照したりできる
require 'net/http'
Net::HTTP.start('example.com') do |http|
response = http.get('/')
p response.code
#=> 200
p response.message
#=> "OK"
p response['content-length']
#=> "606"
end
日付、時間
- もしあなたがRailsを使う時には Time.zone.now とか Time.zone.parse とかを使う事
- 以下の時間系の説明は知識として必要程度にとどめておく
クラスツリー
- Object
- Date
- DateTime
- Date
dateライブラリには日付を扱うDateクラスと日付と時間、タイムゾーンを扱うDateTimeクラスが含まれている
Dateクラス DateTimeクラス の時間データの持ち方
- Date, DateTimeクラスはユリウス日(紀元前4713/01/01)からの経過時間をRationalクラスで保持している
- 整数部が日付、小数部を時間に割り当てている
- 時間の加算、減算にもRationalを使う必要がある
Dateクラスの減算は Rational が返ってくる
require 'date'
DateTime.new(2011, 1, 1) - Date.new(2010, 1, 1)
=> (365/1) # Rationalが返ってくる
<<, >> メソッドは nヶ月前、nヶ月後の日付オブジェクトを返す
(DateTime.new(2011, 1, 1) << 1).class
=> DateTime
(Date.new(2011, 1, 1) << 1).class
=> Date
(DateTime.new(2011, 1, 1) << 1).strftime('%Y/%m/%d')
=> "2010/12/01"
(Date.new(2011, 1, 1) >> 1).strftime('%Y/%m/%d')
=> "2011/02/01"
Date.civil Date.new(year, mon, mday, start)
start はグレゴリオ暦を使い始めた荷を表すユリウス日です。との事だが、だから何って感じ
Date.new.strftime('%Y/%m/%d')
=> "-4712/01/01"
Date.new(2010).strftime('%Y/%m/%d')
=> "2010/01/01"
Date.new(2010, 10).strftime('%Y/%m/%d')
=> "2010/10/01"
Date.new(2010, 10, 10).strftime('%Y/%m/%d')
=> "2010/10/10"
Date.parse(str, complete = true, start)
- 文字列からDateインスタンスを生成する
- 年月日、時分秒、タイムゾーンを判定する
- completeにtrueを渡すと年が2桁だった場合に4桁表記に変換(69 => 1969, 68 => 2068)する。あたまおかしい
require 'date'
Date.parse('10/4/1').class
=> Date
Date.parse('10/4/1').strftime('%Y/%m/%d')
=> "2010/04/01"
Date.parse('10/4/1', false).strftime('%Y/%m/%d')
=> "0010/04/01"
Date.strptime(str, frrmat, start)
- formatに与えた整形ルールに基づいてDateインスタンスを生成する
- strftimeと同じルール
require 'date'
Date.strptime('10/4/1', '%y/%m/%d').class
=> Date
Date.strptime('10/4/1', '%y/%m/%d').strftime('%Y/%m/%d')
=> "2010/04/01"
Date.strptime('10_HOMO_2010_HAGE_20', '%m_HOMO_%Y_HAGE_%d').strftime('%Y/%m/%d')
=> "2010/10/20"
Date.today
本日の日付でDateインスタンスを生成する
Date#year Date#mon(month) Date#day(mday)
require 'date'
Date.today.mon == Date.today.month
=> true
Date.today.day == Date.today.mday
=> true
Date#next Date#succ
次の日付のDateインスタンスを生成
require 'date'
Date.today.day + 1 == Date.today.next.day
#=> true
Date.step(limit, step) Date.downto(min) Date.upto(max)
- 予想通りの動きをする
- limit, min, max はDateTimeインスタンスでもおk
require 'date'
day1 = Date.today
day2 = day1.next.next.next.next.next
p day1.strftime('%Y/%m/%d')
#=> "2017/09/14"
p day2.strftime('%Y/%m/%d')
#=> "2017/09/19"
day1.step(day2, 2) { |d| p d.strftime('%Y/%m/%d') }
#=> "2017/09/14"
#=> "2017/09/16"
#=> "2017/09/18"
day1.upto(day2) { |d| p d.strftime('%Y/%m/%d') }
#=> "2017/09/14"
#=> "2017/09/15"
#=> "2017/09/16"
#=> "2017/09/17"
#=> "2017/09/18"
#=> "2017/09/19"
day2.downto(day1) { |d| p d.strftime('%Y/%m/%d') }
#=> "2017/09/19"
#=> "2017/09/18"
#=> "2017/09/17"
#=> "2017/09/16"
#=> "2017/09/15"
#=> "2017/09/14"
Date#leap?
閏年ならtrue
Date#to_s
あ、’%Y-%m-%d’ なのか。見やすいじゃん
require 'date'
Date.today.to_s
=> "2010-04-03"
Date.strftime(format)
件のフォーマットのアレ。超べんり
require 'date'
Date.today.strftime('%Y_%y_%Y')
#=> "2017_17_2017"
DateTime#civil(year = -4712, mon = 1, mday = 1, hour = 0, min = 0, sec = 0, offset = 0, start = Date::ITALY)
DateTime#new(year = -4712, mon = 1, mday = 1, hour = 0, min = 0, sec = 0, offset = 0, start = Date::ITALY)
- Dateクラスに比べて、時分秒、時差を指定可能
- offsetはRationalインスタンス、もしくは文字で指定する
require 'date'
DateTime.new(2010, 4, 3, 10, 10, 10).to_s
#=> "2010-04-03T10:10:10+00:00"
DateTime.new(2010, 4, 3, 10, 10, 10, Rational('9/24')).to_s
#=> "2010-04-03T10:10:10+09:00"
DateTime.new(2010, 4, 3, 10, 10, 10, 'GMT+09').to_s
#=> "2010-04-03T10:10:10+09:00"
DateTime.new(2010, 4, 3, 10, 10, 10, '+0900').to_s
#=> "2010-04-03T10:10:10+09:00"
DateTime.new(2010, 4, 3, 10, 10, 10, '+09:00').to_s
#=> "2010-04-03T10:10:10+09:00"
DateTime.now
現在時刻でDateTimeインスタンスを生成 ちゃんとタイムゾーンも反映されている
DateTime#hour min sec zone offset
時分秒、タイムゾーンの文字列表現、タイムゾーンのRationalを返す
require 'date'
DateTime.now.hour
=> 18
DateTime.now.zone
=> "+09:00"
DateTime.now.offset
=> (3/8)
マルチスレッド・同期
- 組み込みクラス Thread 単体ではあまり考慮されていない排他処理、同期処理を実現するクラスを提供する
- threadライブラリをrequireするとThreadの拡張、ConditionVariable, Queue, SizedQueueのクラスが定義されるようになる
- Ruby1.8まではMutexクラスも同様だったが、Ruby1.9から組み込みクラスになった
- この書籍ではMutexが説明されている。それ以外はぐぐれ
Threadクラス に書きました
開発ツール
Rubyは
- デバッガ
- プロファイラ
- 自動テスト
等のツールを添付ライブラリの形で提供している。ソースに組み込んで利用するものあれば、使用したいときだけコマンドラインオプションに組み込んで呼び出すものもある
debugライブラリの呼び出し
デバッグを行うdebugライブラリはrubyコマンドに -r オプションを付けて呼び出す
$ ruby -rdebug moko.rb
debug や profile 等のライブラリはバグの原因を特定したり性能問題を解決したりする際に便利(ぐぐってね)
test/unit
- いわゆるユニットテストを自動で行うためのライブラリ
- テスト対象のソースファイルとは別にテストケースと呼ばれるファイルを作成し、その中から利用する
moko.rb
class Moko
def hage
true
end
def hello(name)
"Hello #{name}."
end
end
test_moko.rb
- このファイルから ‘test/unit’ をrequireする
- テスト対象ファイルもrequireする
require 'test/unit'
require 'moko'
class TestMoko < Test::Unit::TestCase
def setup
@moko = Moko.new
end
def teardown
end
def test_hage
assert_equal(true, @moko.hage)
end
def test_hello
assert_equal('Hello World.', @moko.hello('World'))
end
end
moko.rb を require できるようにカレントディレクトリをロードパスに追加して実行する
$ ruby test_moko.rb
Loaded suite test_moko
Started
..
Finished in 0.000781 seconds.
------------------------------------------------------------------------------------------------------------------------------------------------------------
2 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------------------------------------------------------------
2560.82 tests/s, 2560.82 assertions/s
moko_test.rb の、
require 'moko'
の箇所を
require './moko'
か
require_relative 'moko'
と書けば起動時にロードパスの追加は必要ない気がするけど
$ ruby test_moko.rb
Loaded suite test_moko
Started
..
Finished in 0.001216 seconds.
------------------------------------------------------------------------------------------------------------------------------------------------------------
2 tests, 2 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------------------------------------------------------------
1644.74 tests/s, 1644.74 assertions/s
好きに書けばいいと思う
失敗した時の例
moko.rb を変更してから
class Moko
def hage
false
end
def hello(name)
"Hello Hage #{name}."
end
end
再実行してみる
- true じゃなくて false が返ってきたぞ!
- ‘Hello World.’ じゃなくて ‘Hello Hage world.’ が返ってきたぞ!
と表示してくれる訳です
$ ruby test_moko.rb
Loaded suite test_moko
Started
F
============================================================================================================================================================
Failure: test_hage(TestMoko)
test_moko.rb:13:in `test_hage'
10: end
11:
12: def test_hage
=> 13: assert_equal(true, @moko.hage)
14: end
15:
16: def test_hello
<true> expected but was
<false>
diff:
? tru e
? fals
============================================================================================================================================================
F
============================================================================================================================================================
Failure: test_hello(TestMoko)
test_moko.rb:17:in `test_hello'
14: end
15:
16: def test_hello
=> 17: assert_equal('Hello World.', @moko.hello('World'))
18: end
19: end
20:
<"Hello World."> expected but was
<"Hello Hage World.">
diff:
? Hello Hage World.
============================================================================================================================================================
Finished in 0.016101 seconds.
------------------------------------------------------------------------------------------------------------------------------------------------------------
2 tests, 2 assertions, 2 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
0% passed
------------------------------------------------------------------------------------------------------------------------------------------------------------
124.22 tests/s, 124.22 assertions/s
test/unit の使い方
- Test::Unit::TestCase を継承したクラスを作成する
- デフォルトの動作では全てのテストケースを実行するが、-t(testcase)オプションで明示的にテストケースを指定できる
- テストケースを定義するには ‘test_’ を付けたテストメソッドを定義する
- setupメソッドは各テストメソッドを実行する前に呼び出される。各テストメソッドで共通する初期化処理などを記述する
- teardownmメソッドは各テストメソッドを実行した後に呼び出される。各テストメソッドで共通する終了処理などを記述する
検証によく使われるメソッド
を以下で説明していきます だいたいこんな感じのコードで実行し、必要そうなところだけコピペしていきます
require 'test/unit'
class TestMoko < Test::Unit::TestCase
def test_hage
assert(false, '検証')
end
end
assert(boolean, message = nil)
booleanが trueであれば検証が成功 messageにてメッセージを指定できる
Failure:
assert 検証.
<false> is not true.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert(false, 'assert 検証')
6: end
7: end
8:
assert(message = nil) { … }
- gemのtest-unitがVer3になった時ににPower Assertが標準機能として取り込まれ、assertメソッドにブロックを渡せるようになった
- このgemはRuby2.2から同梱されるようになった
Failure: assertブロック付き 検証.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert('assertブロック付き 検証') do
6: false
7: end
8: end
- この PowerAssert が使える子で、ブロック内の式を解析して詳細表示してくれる
- 通常はこのメソッドを使えばおkかと思う
Failure:
assertブロック付き 検証.
['2', '4'] == (1..6).select(&:even?).map(&:to_s)
| | |
| | ["2", "4", "6"]
| [2, 4, 6]
false
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert('assertブロック付き 検証') do
6: ['2', '4'] == (1..6).select(&:even?).map(&:to_s)
7: end
8: end
assert_equal(expected, actual, message = nil)
- expectedに期待する値、actualに検証対象の値を渡す
- expected == actual の時に検証が成功する
Failure: test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_equal(true, false, 'assert_equal 検証')
6: end
7: end
assert_equal 検証
<true> expected but was
<false>
diff:
? tru e
? fals
assert_not_equal(expected, actual, message = nil)
- expectedに期待する値、actualに検証対象の値を渡す
- expected != actual の時に検証が成功する
Failure:
assert_not_equal 検証.
<true> was expected to be != to
<true>.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_not_equal(true, true, 'assert_not_equal 検証')
6: end
7: end
assert_instance_of(klass, object, message = nil)
object が klassクラスのインスタンスであれば検証が成功する
Failure:
assert_instance_of 検証.
<1> was expected to be instance_of?
<String> but was
<Integer>.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_instance_of(String, 1, 'assert_instance_of 検証')
6: end
7: end
assert_nil(object, message = nil)
object が nil であれば検証が成功する
Failure:
assert_nil 検証.
<1> was expected to be nil.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_nil(1, 'assert_nil 検証')
6: end
7: end
assert_not_nil(object, message = nil)
object が nil でなければ検証が成功する
Failure:
assert_not_nil 検証.
<nil> was expected to not be nil.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_not_nil(nil, 'assert_not_nil 検証')
6: end
7: end
assert_kind_off(klass, object, message = nil)
- objectがklassと継承関係にあれば検証が成功する
- 文字列 ‘string’なら .class.ancestors である
- => [String, Comparable, Object, Kernel, BasicObject]
- が継承関係にあると言える
Failure:
assert_kind_of 検証.
<"a"> was expected to be kind_of?
<Integer> but was
<String>.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_kind_of(Integer, 'a', 'assert_kind_of 検証')
6: end
7: end
assert_match(pattern, string, message = nil)
patternに指定した正規表現がstringにマッチするなら検証が成功する
Failure:
assert_match 検証.
</\d/> was expected to be =~
<"a">.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_match(/\d/, 'a', 'assert_match 検証')
6: end
7: end
assert_not_match(pattern, string, message = nil)
patternに指定した正規表現がstringにマッチしないなら検証が成功する
Failure:
assert_not_match 検証.
</\d/> was expected to not match
<"0">.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_not_match(/\d/, '0', 'assert_not_match 検証')
6: end
7: end
assert_raise(expected_exception_klass, message = nil) { … }
ブロック処理中に expected_exception_klass にて指定した例外をraiseした場合に検証が成功する
Failure:
assert_raise 検証.
<ZeroDivisionError> exception was expected but none was thrown.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_raise(ZeroDivisionError , 'assert_raise 検証') { 1 / 1 }
6: end
7: end
assert_nothing_raised(*expected_exception_klass, message = nil) { … }
ブロック処理中に expected_exception_klass にて指定した例外がraiseされなかった場合に検証が成功する
Failure:
assert_nothing_raised 検証.
Exception raised:
ZeroDivisionError(<divided by 0>)
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_nothing_raised(ZeroDivisionError, NameError, 'assert_nothing_raised 検証') { 1 / 0 }
6: end
7: end
flunk(message = nil)
- 常に検証が失敗するテストケースを作る
- 特定のテストケースの実装を後回しにする時とかの目印になる
Failure: 例えばこの検証は後で実装する、等.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: flunk('例えばこの検証は後で実装する、等')
6: end
7: end
8:
assert_throw(expected_symbol, message = nil) { … }
ブロック処理中に expected_symbol が throwされ、catchされない場合に検証が成功する
Failure:
assert_throw 検証.
<:exit> should have been thrown.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_throw(:exit, 'assert_throw 検証') { catch(:exit) { throw :exit } }
6: end
7: end
8:
assert_nothing_thrown(message = nil) { … }
ブロック処理中に何もthrowされない場合に検証が成功する
Failure:
assert_nothing_thrown 検証.
<:exit> was thrown when nothing was expected.
test_hage(TestMoko)
test_moko.rb:5:in `test_hage'
2:
3: class TestMoko < Test::Unit::TestCase
4: def test_hage
=> 5: assert_nothing_thrown('assert_nothing_thrown 検証') { throw :exit }
6: end
7: end
8:
RDoc
- ソースからHTML形式のAPI仕様書を出力する
- Ruby1.8.1から標準添付ライブラリに
- ソースコードを解析してクラス、モジュール、メソッドの定義、include、require の関係を抽出したものを直前のコメントと併合して出力してくれる
- コメントは # =begin〜=end
- rdocコマンドで、カレントディレクトリ以下にあるファイル探して解析、docディレクトリ内にドキュメントが配置される
適当に試してみる
moko.rb
class Moko < Integer
class << self
# コメント1
def test1
'a'
end
end
=begin
コメント2
=end
def test2
'b'
end
end
$ rdoc
Parsing sources...
100% [ 1/ 1] moko.rb
Generating Darkfish format into /current/dir/doc...
Files: 1
Classes: 1 (1 undocumented)
Modules: 0 (0 undocumented)
Constants: 0 (0 undocumented)
Attributes: 0 (0 undocumented)
Methods: 2 (1 undocumented)
Total: 3 (2 undocumented)
33.33% documented
Elapsed: 0.1s
doc/index.html あたりをブラウザで確認すると、ナウい感じのドキュメントが見れる
コメントはいろいろマークアップしてかっこよくできる でも・・なんかアスキー文字しか対応してない感じがする
いつもの太字で表示される
# = 見出し1
# == 見出し2
# === 見出し5
# ==== 見出し5
# ===== 見出し5
# ====== 見出し6
- http:// 等で始まる文字列は自動的にリンクに変換される
* アスタで囲むとボールド体で装飾される
# *リスト1*
_ アンスコで囲むとイタリック体で装飾される
# _リスト1_
+ プラスで囲むとタイプライター体で装飾される
# +リスト1+
* - で文字列を開始すると番号なしリストが作れる
# * リスト1
# * リスト2
# * リスト3
# - リスト1
# - リスト2
# - リスト3
番号.(ピリオド)で番号付きリスト
# 1. リスト1
# 2. リスト2
# 3. リスト3
[]で囲んだ文字列で開始するとラベル付きリストが作れる
# [大項目1] コメント1
# [大項目2] コメント2
# [大項目3] コメント3
コロンで区切ってもラベル付きリストが作れる
# 大項目1:: コメント1
# 大項目2:: コメント2
# 大項目3:: コメント3
コマンドライン
シェル上で動作するプログラムへオプションを渡したい時、コマンドライン解析用のライブラリとして optparse を利用できる
正直、感覚的な動作ではなく、ショボいと思う
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-v', '--version') do |value|
options[:version] = value
end
opt.on('-i value') do |value|
options[:input_path] = value
end
opt.parse!
end
p options
p ARGV
$ ruby moko.rb -v -i file_path hage
{:version=>true, :input_path=>"file_path"}
["hage"]
--help を渡すと登録されたオプションの一覧を出力する
$ ruby moko.rb -v -i file_path hoge --help
Usage: moko [options]
-v, --version
-i value
インスタンス作成時に –helpオプションで表示される文字列を指定できる。正直ショボい
ヘッダに表示される文字, widthを超えるようなら改行して表示、インデント
OptionParser.new('ヘッダに表示される文字', 10, '> ')
こんな感じでインスタンスを作っておくと こんな感じで表示される
$ruby moko.rb --help
ヘッダに表示される文字
> -v
> --version
> -i=
onメソッドでオプションの定義を行い、parseメソッドで引数の解釈を開始する
- 感覚的じゃねえ
- オプションの名前の定義により、オプションの意味やブロックに渡される値が変わる
- ロングオプションを定義すると、勝手にショートオプションが定義されるような動きをする気がする、注意
-v | ショートオプションを定義 | ブロック引数には true が渡される |
-vnandemo | ショートオプションを定義 | ブロック引数には オプションの後に渡した文字列が渡される |
–version | ロングオプションを定義 | ブロック引数には true が渡される |
–no-version | ロングオプションを定義 | ブロック引数には false が渡される |
–[no-]version | ロングオプションを定義 | 呼び出し方により、ブロック引数には true / false が渡される |
–version=nandemo | ロングオプションを定義 | ブロック引数には オプションの後に渡した文字列が渡される |
parse(argv = ARGV) / parse!(argv = ARGV)
- argvに渡した配列を解析する
- 省略するとARGVが使用される
- parse!は onメソッドで定義したオプションを、渡した配列から破壊的に削除する