東京Ruby会議#03に行ってきた
とても内容が濃く、充実した会議(講義?)でした!
スタッフ・講師の皆様お疲れ様でした。
セッションがかなりヘビーというか、Rubyの実装周りの話がてんこ盛りで
ついていくのがやっと or ついていけてない部分もありました。
でも、勉強になる部分がとても多かったです。
勉強不足を痛感したので、さらに頑張ろうっと。
会議自体へのフィードバック
- 喫茶無償++
- 今度はGCの話とか聞いてみたい!
というわけでメモ公開。
聞くだけでもいっぱいいっぱいだったので、正確ではないかもしれません。
Yuguiさん メタプログラミング
- メタプログラミングとは
- Rubyにおける特性
- 道具立て
- 用例
メタプログラミングとは
プログラムをプログラミングすること
- ループ
- 繰り返し
- 例としてのアクセサ
- getter/setter をメタプロ
Rubyの特性
- S式ではない!
- First Class Object
- コンパイルがない
非S式
- メタプログラミングといえばLisp
- Rubyには?
- ブロック
- proc
- 文字列
- 文字列を生成してeval
- eval<<-EOS, binding,FILE, __LINE__+1を渡して、スタックトレースを読めるようにする
- ブロック
コンパイルがない
- 実行時間とコンパイル時間が同一=隠蔽している
- 内部的にはASTに変換、1.9ではコンパイルしている
- 副作用が気軽に使える
- classは定義ではなく、副作用を持つ式である
- クラスの継承元すら動的に定義できる
- メソッド定義も条件式で分岐させるなど
- 動的すぎるため、最適化が難しい。rubyを遅くしている原因のひとつ
- C++では?
- Templateでできる。けどつらい。
- コンパイル時に決まってしまう
- Boost MPLライブラリを使う
- 副作用の代表格: IO
ex.) Rake
task :a, do... end
道具立て
- class
- 特異クラス
- callable object
- eval族
- method_missing, send
class
- Classクラスのインスタンス
- クラス階層
- BasicObject
- Object
- Module
- Class
- 特異メソッド
- クラスのあるインスタンスにだけ存在するメソッド
- クラスメソッドとして頻繁に使用
- 特異クラス
- 元クラスから継承したクラスに内部的に付け替える
- 特異クラスの定義イディオム
eigenclass = (class << obj; self end)
- 特異メソッドは内部で特異クラスを定義している
obj = Object.new
class << obj
def foo; end
end
- クラスメソッド
- 実際はClassオブジェクトの特異メソッドを定義している
- つまり親クラスの特異メソッドとして扱われているだけ。特殊な定義ではない。
- クラスのクラスはメタクラス
- 全てのクラスはClassクラスの特異クラス
- 図が欲しい。 => yuguiさんのflikr
- メタクラスのクラスはメタメタクラス
- boot時にメタクラスを全て作るとメモリがなくなる。
- 高位のメタクラスはオンデマンドで作成
呼び出し可能オブジェクト
- Object
- Proc
- Method
- UnboundMethod
- Continuation
- 相互変換可能
eval族
- eval
- instance_eval
- class_eval
- current class
- TOP LEVEL
- self: main
- current class: Object
- class expr
- self: the class
- cc: the class
- def expr
- self: insatnce
- cc: NONE
- コンテキストについて
- instance_eval
- self: instance
- cc: eigenclass
- レシーバの特異クラス
- class_eval
- self: class
- cc: class
- eval
- コンテキストはbindingに依存 (引数で指定)
用例集
メソッド定義
- define_methodでのブロック定義より、eval+heredoc の方が実行速度は早い
- railsはeval多用
- define_methodならシンタックスハイライトとかシンタックスエラーを検出
メソッドをまとめて定義
- 継承元クラスで実装が必要なメソッドにまとめてエラー定義
クラス定義
DSL
- メソッド内でサンドボックスとなるオブジェクトを提供し、そこにModuleをextend。instance_evalで評価することで
そのメソッド内のみでModuleの機能を使用可能
モジュール定義
- サンドボックス定義
- 評価するブロック内で変数を定義されたりしても、予期せぬ書き換えなどが防げる
def sandbox(block)
Module.new(&block)
end
- リモートのdrbのプロセス状態を、手元の端末からirb相手側のプロセスにinstance_eval流し込み
- drbのポートを不用意に開けておくとものすごく危険!
Rails 3 ハンズオン 松田さん
Rails 3とは?
- beta版がリリースされている
インストール
- gitプロトコルが通らない!
- gemが入らない!
- 環境は事前に準備しておきましょう。
アプリケーションを作成
- コマンド出力がカラフル
- .gitignore がデフォルト
- 今後のバージョン管理はgitで!
- .gitkeep もある
- Model名にする予定のものはアプリケーション名にできない
- Rubyのクラス名に使えないものはアプリケーション名にできない
HTML5準拠
- jsが綺麗になった
Model
- validates がカラムを基準にかけるようになった
- Rails 3 はedgeを使いましょう!*1
- 松田さんによるRails 3 ハンズオン サンプルアプリケーション
A Reintroduction to Ruby i17n 成瀬さん
- 日本に生まれたことを後悔したくなるらしい
- 字体としては、JISでは同じだがUnicodeでは別
- 符号化文字集合 = コードポイント
- 文字符号化方式: 符号化文字集合の番号をバイト列に変換する
- エンコーディングとは?
- Charset: 各エンコーディングに名前をつけて管理するためのもの
- 現実と仕様の乖離も存在する
- 用語の整理
I18N
- 略語まとめ
- L10N (地域化)
- I18N (国際化)
- M17N (多言語化)
歴史
- ASCIIが始まり
- 拡張してみた
- 大量の文字集合が・・・
- 敵を知る。知った上で個別撃破
- ISO 646 – 分裂の始まり
- Unicode
- 1つのUnicode文字列には複数の言語を混ぜられない
- フラットな空間にすべての文字を入れる
- 漢字統合: 言語の指定が必要
- ASCIIも言語の指定が必要
- Unicode以前の文字コードは?
- CP932
円記号問題
波ダッシュ問題
- U+301C (WAVEDASH)
機種依存文字
処理系の問題
- 内部コードをどうするか?
- UCS正規化: 内部では特定の1つに変換する
- 内部コードを統一
- 入出力時に変換を行う
- CSI (Code Set Independent): それぞれの文字コードに対応する
- UCS正規化: 内部では特定の1つに変換する
Ruby1.9
- 文字単位での処理
- 正規表現エンジンが鬼車に
- 文字型はなく、1文字Stringを使う
- 文字型に必要なもの
- コードポイント、エンコーディング
- 1.9.1-p3xxや1.9.2では\wがASCIIのみにマッチするようになった
- 適切にencodingを設定しておく
Magic Comment
- デフォルトはASCII
- ERBにも必要
<%= #coding : utf-8 %>
IOとEncoding
- 入出力のEncodingを指定する
- open (‘text.txt’, ‘r:UTF-8’)
- ネットワークではバイナリ扱い => 強制指定 (force_encoding)する
- socketやnet系のライブラリで必要
- 濫用は避けましょう
- KCODEはやめましょう
- UTF- {16, 32} はエンディアンの指定が面倒なので未サポート
エンコーディング
- ASCII互換
- ASCII非互換
- ダミー
エンコーディング変換
- SJISとWindows-31Jは混同しやすい
- M17N未対応のライブラリを使った時など
- ASCII-8BIT でかえされるので、明示的にエンコーディングを指定する
Ruby 1.9 での課題
- 携帯絵文字: 対応中
- 結合文字
- 複数のコードポイントを1文字として扱う。濁音や半濁音など
- 異体字セレクタ (IVS): Ideographic Variation Sequence
- 結合文字同様、複数のコードポイントを1文字扱いする必要がある
- 1つのStringに2つの言語を入れたい
- 文字幅
- 言語に依存して期待される幅が異なる
- Unicode大文字小文字
- ASCIIでしか動作しない
- String#sort
- デフォルトでは文字コード順
- 文字集合によって並び順が変わってしまう
- sort_byで指定する
Open 3 のはなし 田中 哲 (akr)さん
- Rubyからのプロセス起動
- 問題点: system, IO.popen…
- 解決
- spawn メソッドの追加
- open3ライブラリの追加
プロセス属性
- ファイルディスクリプタ
- 0:標準入力, 1: 標準出力, 2: 標準エラー出力
- Unixのプロセス起動はforkとexecを組み合わせる
- 2つのコマンドの間にプロセス属性を変更することで、パイプやリダイレクトを実現する
- Unix以外ではプロセス属性変更のために引数が複雑になりがち
Rubyで実現したいこと
API案
既存のメソッドを拡張する?
- forkは非Unixでは動きそうにない
- system: プロセスを待たないようにするのは変
- IO.popen: パイプなしは変
- `command`: 構文上、指定を付加しづらい
新しいメソッドを作る
- 要求が衝突する
- よくやることを簡単にできる
- なんでも可能なのが欲しい
- => ひとつのメソッドで全ての要求を満たすのは困難
- 高位・低位APIへ分割
ライブラリのレイヤ
- どのレイヤのライブラリを提供するか?
- 高位になるほどライブラリの自由度が高く「賢い」
- 低位になるほど詳細な制御が可能になる
低位API: spawn
- OS固有の機能も利用可能
- OS間で共通の機能については差を気にしなくてよいくらいには高位
既存メソッドへの不満
- 既存メソッドはシェルを呼び出す
- シェルを呼びたくないこともある
基本の使い方
spawn(env, command, option)
pid = spawn("make all")
Process.wait pid
- 引数を分けて与えるとシェルを経由しない
spawn("make", "all")
- 最初の引数を配列にするとargv0も指定できる
- リダイレクト
spawn("make all", :out => "make.log")
spawn("make all", :err => :out)
spawn("make akk", :out => :err, :err => :out)
- パイプ
IO.pipe {|r,w|
spawn("ls", :out => w)
spawn("sort -r", :in => r)
}
spawn({LC => 'env'}, "ls -l")
spawnのデザイン
- 多機能なものが単一関数でいいのか?
- 簡単なことが難しくなっていいのか?
- posix_spawnの複雑な引数を吸収している
- 将来の拡張に耐えられるか?
- キーワード引数
- fdの扱いは適切か?
- 最終的な状態を指定する
- 3番以降のfdを継承しない
- 子と親のfdの関係を書く
- fork はメモリ内部もコピーする
- Rubyでforkを避ける理由
- NetBSD4ではforkした子プロセスではスレッドが動かない
- Ruby1.9 ではタイマースレッドというスレッドを常に必要とするから
spawnのまとめ
- プロセスを起動するプリミティブ
- fork + プロセス属性変更 + exec
高位API: open3
- とりあえずシェルのパイプラインくらいまで提供
- シェルの嫌なところは避ける
- パイプよりも複雑な用途は今後の課題
- 標準添付ライブラリ
- 3つの標準パイプでコマンドと通信する
- 命名はPerl由来
Open3.popen3("command") {|i, o, e|
# some script
}
既存のopen3nの問題
- ゾンビの回収とdouble fork
- 親がwaitせずに死ぬと、子はinitの養子になる
- initはwaitしまくって亡骸を回収する
- 意図的にdouble forkすることでゾンビを防止
- double forkの問題点
- pidを得られない: シグナルを送りづらい
- 終了ステータスを得られない
- open3 類が乱立
解決法
- Process.detach によってwaitスレッドを生成することで解決
- 標準エラー出力はパイプにしない方がよいことも多い
- ひとつのパイプにして提供して、制御しやすくもできる
Open3で解決した問題
※ (問題自体を書き逃した)
- Open3.capture*
- Open3.popen2e*
- Open3.pipeline*
- spawnによるパイプラインは厄介
- 大学の試験になるくらい
- shell ライブラリ
- shellの記法を模したDSL
*1:帰宅後にedgeの環境用意したけど、まだ試せていない