APIデザインケーススタディ
APIデザインケーススタディ ~Rubyの実例から学ぶ。問題に即したデザインと普遍の考え方 (WEB+DB PRESS plus)
技術評論社
著者:田中哲
本書に寄せて
本書について
本書の構成
目次
第1章 I/O
1.01 RubyのIOクラスとC言語のstdioライブラリ プログラマが知っている名前を利用する
FILE構造体とIOクラスの対応
IOクラス以外のC言語とRubyの対応
まとめ
1.02 feof関数とIO#eof?メソッド 過去にEOFに出会ったのか,それとも今現在EOFなのか
C言語とPascalにおけるファイルの終端
ユーザにとってわかりやすいファイルの終端
まとめ
1.03 IOバッファが空でなければsysreadは例外 明らかに危険で役に立たない動作は禁止する
sysreadメソッドの由来
sysreadメソッドの危険性
Rubyでは混用禁止
まとめ
1.04 EOFフラグの除去 モードで挙動が変化するのは良くない
stdioのEOFフラグ
RubyにおけるEOFフラグ
EOFフラグの再実装の試み
まとめ
1.05 0バイト読んだときに何を返す? 用例を探して良い挙動を判断する
readメソッド
read(0)の挙動
CGIでPOSTされたデータを読み込む
CGI以外の用法
固定長レコード
可変長レコード
用法を探す
Rubyの実際の挙動
まとめ
1.06 selectとstdioのバッファ 無理をしても使いやすくする
I/Oの多重化
selectとstdioのバッファリング
RubyのIO.select
まとめ
1.07 readpartial I/Oを多重化したときに適切なメソッド
I/Oの多重化と読み込み/書き込み
適切な読み込み/書き込みサイズはわからない
バッファを考慮したreadシステムコールが欲しい
readpartialの動作
まとめ
1.08 ノンブロッキングI/O モードで挙動が変化するのは良くない
ノンブロッキングI/Oはfdのフラグ
ノンブロッキングI/Oと1行入力
ノンブロッキングモードと競合状態
まとめ
1.09 ノンブロッキングI/Oメソッドの導入 read_nonblockやwrite_nonblock
ノンブロッキングI/Oの用途
書き込みの多重化
読み込みの多重化
1.ノンブロッキングI/Oの設定
2.複数スレッドからの読み込み
3.圧縮されたストリームや,SSLなストリームからの読み込み
ノンブロッキングI/Oメソッドを用いたI/Oの多重化
読み込むために書き込み可能になるのを待つこともある
4. Linux固有の問題
ノンブロッキングI/Oの他プロセスへの影響
まとめ
Column ノンブロッキングI/OとRubyの歴史
UnixにおけるノンブロッキングI/Oの歴史
RubyにおけるノンブロッキングI/Oの歴史
1.10 PTY.open ptyを利用するためのプリミティブ
擬似でない端末
tty
制御端末
擬似端末
PTY.spawnメソッド
擬似端末が必要になる状況
擬似端末の利用の仕方
PTY.openメソッド
まとめ
1.11 IOによるエンコーディング変換 正しく処理すべきだが,速度も重要
IOクラスによるエンコーディング変換
改行の変換
IOでの変換と文字列の変換の違い
ファイルの現在位置の扱い
現在位置を扱える変換手法
まとめ
第2章 ソケット
2.01 Addrinfoクラスの導入 関連して扱う情報をまとめてオブジェクトにする
Addrinfo.getaddrinfoメソッドとSocket.getaddrinfoメソッド
Addrinfoクラス
逆引きの有無
プロトコル非依存
アドレスの種類を判定するプレディケートメソッド
ソケットアドレス構造体を返すメソッドと互換性
まとめ
2.02 Socketクラスの勧め 使いやすく,かつ,低レベルな操作も可能
ソケットのクラス階層
細分化されたクラスの問題
Socketクラスの強化
クラスメソッド
インスタンスメソッド
initialize
特定の種類のソケットのためのメソッド
ソケットアドレスを返すメソッド
まとめ
2.03 Socket.ip_address_list 自ホストのIPアドレスを正しく簡単に得る
自ホストのIPアドレスの必要性
自ホストのIPアドレスを得る方法
Socket.ip_address_listメソッド
Socket.ip_address_listの実装
まとめ
2.04 ソケットオプション 関連して扱う情報をまとめてオブジェクトにする
ソケットオプションの使い方の例
ソケットオプションの内容の表示
ソケットオプションの型の詳細を隠蔽
Socket::Optionの内容
Socket::Option導入に伴う非互換性
クラスを細分化しない
まとめ
2.05 send_ioとrecv_ioによるfd passing ポータブルで引数の少ないAPI
fd passing
recvmsgとsendmsgシステムコール
recv_ioとsend_ioメソッド
まとめ
2.06 recvmsgとsendmsg 多機能なシステムコールを工夫して提供
recvmsgとsendmsgシステムコール
recvmsgとsendmsgメソッド
「不連続なバッファを扱える」機能は提供しない
「MSG_OOBなどのフラグを指定できる」機能
「recvmsgではMSG_TRUNCなどのフラグが返ってくる」機能
「recvmsgで送信元を得られ,sendmsgで送信先を指定できる」機能
「バイト列を読み書きできる」機能
「補助データを扱える」機能
まとめ
2.07 getpeereid 簡単確実なユーザ認証
ユーザ認証
マシン内のユーザ認証システムコール
getpeereidメソッド
まとめ
第3章 プロセス
3.01 プロセス起動プリミティブspawnメソッド ポータブルで高機能で簡単なプロセス起動
forkとexecはポータブルでない
プロセス起動の悩ましい選択
spawnメソッド
まとめ
Column async-signal-safe関数
async-signal-safeでない関数
forkで生成された子プロセス
経緯と現状
3.02 close-on-execフラグ 意図しないfdの継承を防止する
子プロセスへのさまざまな継承
fdを継承する用途
意図せざるfdの継承
デフォルトでclose-on-execにする
Rubyにおけるclose-on-exec
POSIXとRubyのデザインの違い
すべてのfdをcloseする実装
まとめ
3.03 Open3.popen3の修正 互換性を保って問題を解決する
Ruby 1.8のopen3ライブラリ
open3ライブラリの改善
double forkの除去
まとめ
3.04 open3における標準エラー出力の扱いと
デッドロック ——用途に応じたメソッドの追加
メソッドの追加
popen3,popen2,popen2eメソッド
デッドロック
capture3,capture2,capture2e
まとめ
3.05 open3のパイプラインサポート パイプで接続したプロセス起動
パイプで接続されたプロセスの起動
パイプの使われ方
まとめ
Column PerlとPythonでパイプラインを作る
使う例について
Rubyのopen3
PerlのIPC::Open2
Pythonのsubprocess
fdがオープンされたまま残る
クローズして問題を解決する
3.06 双方向popenのソケットによる実現 ソケットペアによる実装の失敗
コマンドとの単方向通信
コマンドとの双方向通信
1つのIOオブジェクトで2つのfdを扱う問題
ソケットペアによる双方向popenの実現
2つのfdの問題への対応
まとめ
3.07 forkは他のスレッドを子プロセスに残さない 用途に適した挙動が重要
マルチスレッドとfork
子プロセスですべてのスレッドが実行されることの問題
まとめ
第4章 時刻
4.01 POSIXの時刻機能とRubyのTimeクラス プログラマが知っているPOSIXの機能を提供する
POSIXの時刻機能で用いる型
POSIXの時刻機能とRubyの対応
まとめ
4.02 Time.utcと閏秒 POSIXが提供していなくても必要なら提供する
Time.utcメソッド
timegm関数と閏秒
Rubyによるtimegm関数のエミュレーション
まとめ
4.03 Time#monとTime#ydayの範囲 実際の用法を検討してAPIをデザインする
「月」を整数でどう表すか
「年初からの日数」を整数でどう表すか
まとめ
4.04 時刻に関するOSの制限 本質的でない制限を取り除く
C言語における時刻表現の制限
time_tの制限
struct tmの制限
関数の制限
RubyのTimeクラス
まとめ
4.05 localtimeの逆関数 mktimeに依存しない
地方時とタイムゾーンデータベース
mktimeの不安定性
mktimeをlocaltimeと2分探索で実現する
まとめ
4.06 localtimeの外挿 2038年問題への対応
2038年問題への対応
未来の地方時の推定
いくつかの地方時の規則
localtime関数の外挿
過去の地方時の推定
その他の制約の除去
まとめ
Column さまざまなタイムゾーン
キューバと夏時間
ブラジルと夏時間
イスラエルと夏時間
モロッコと夏時間
イランと夏時間
イギリスと夏時間
夏時間の時差の例外
夏時間と歴史的観点
日付変更線付近の夏時間
夏時間と極地
日付をまたぐ例
時刻を取り巻くさまざまな理由
4.07 UTCからの時差と夏時間 ——対象を確実に表現するデータ構造
C言語とRubyにおける時刻表現の違い
C言語における時刻の扱いの問題
Rubyにおける時刻の扱いの問題
Time#utc_offsetメソッドの追加
Time#strftimeメソッドの強化
まとめ
4.08 UTCからの時差を指定 メンテナンスを増やさない範囲で表現を広げる
UTCと地方時の扱い
さまざまな地方時とタイムゾーンデータベース
時差が一定のタイムゾーン
まとめ
4.09 秒未満の表現 有理数による表現
外部から時刻が伝達される状況
Timeオブジェクトの内部表現
Marshalフォーマットの問題
まとめ
4.10 タイムゾーンの略称 問題が多過ぎるので避ける
略称はタイムゾーンに1つでない
タイムゾーンの略称は複数のタイムゾーンで共有される
タイムゾーンの略称の生成はOSによっては期待されない結果になる
UTCからの時差を直接使う
まとめ
Column 一方,PHPはタイムゾーンデータベースを同梱した
PHPのタイムゾーンデータベースの同梱
タイムゾーンデータベースの更新
OSのタイムゾーンの検出
まとめ
Column Pythonの時刻 ——naiveとaware
datetimeの2種類の時刻
naiveなdatetimeとawareなdatetime
PythonとRuby
4.11 time.rbが提供するメソッドの意図 正しい方が簡単になるようにしておく
time.rbが追加するメソッド
特定用途のためのメソッド
Time#strftimeメソッド
strftimeのlocale依存性の問題
strftimeの%zの問題
RFC 2822の時刻を生成する正しい方法
プログラマを正しい方法に誘導する
まとめ
4.12 Time.localとTime.utcの引数順 あからさまに奇妙だが互換性のために残っている
Time.localとTime.utcの奇妙な引数順
引数順の理由と歴史
問題を修正するかどうか
まとめ
第5章 数,文字列
5.01 Math.gammaのメソッド名 慣習は無視することもある
ガンマ関数と対数ガンマ関数
Rubyのメソッド名とC言語の関数名
まとめ
5.02 Integer#nonzero?の返り値 意外な動作だけど役に立つ
Integer#nonzero?の返り値
Enumerable#sort
Enumerable#sortでInteger#nonzero?を使う
まとめ
5.03 有理数のビット演算 一貫性を拡張するのは無理かもしれない
Rubyにおける数の演算
有理数のビット演算は交換法則が成り立たない
有理数のビット演算で交換法則を実現できるか
まとめ
5.04 Integer#bit_lengthメソッド 用途と前例を調べる
メソッドの用途
仕様決定の難しさ
用途を検討する
前例を検討する
既存のメソッドの問題
まとめ
5.05 文字列中の式展開構文の一貫性 一貫性を優先
バージョンによる式展開の違い
式展開の変更の影響
まとめ
5.06 URI.encode_www_formとURI.decode_www_form 間違いにくいAPI
URI.decode_www_formとURI.encode_www_formメソッド
間違いに気がつきやすいAPI
まとめ
おわりに
索引