黒魔術に触れる
すでに1週間以上前の出来事ではありますが、前回の日記に続き、 yokohama.rb 第4回に参加しました。
今回は jugyo さんによる発表をもとに、みんなでRubyを凄さを学ぶ、という内容でした。
以下、自分的な内容のメモ。
- Ruby すげー、という点
・irb便利
言わずもがな。irb コマンドですぐにREPLが起動してコードを確認できる。
・オブジェクト指向
Rubyはオブジェクトとしてよくできてる。
>> 1+1 # => 2
は、実はメソッド呼び出しになっている。
>> 1.+(1) # => 2
このようにオブジェクトとして一貫性のあるところがすごい。
そして、正しくものが作れる感じがする。
・オープンクラス
前回の勉強会で each を再実装したように、
既存のクラスを拡張して簡単に機能を拡張できる。
・DSLが作り易い
例:rake など
- ライブラリの作り方
jeweler でgem が楽に作れる
github にアップロードする、ライセンスはどれを使う、バージョンは〜〜みたいに指定して、
簡単に gem が作れるらしい。
ぜひ試してみたい。
・便利ツール
jugyo さん作成の便利ツール
gemedit
gem reademe
g
ir b
toclip
・エディタ:txtmate
rubyが実行出来る
・ifchanged
・attr_accessor
ゲッターセッターが簡単に作れる
・attr_accessor はモジュールのメソッド
・Ruby1.9 のオブジェクトの階層
BasicObject -> Object -> Module -> Class
ruby で class で定義するオブジェクトは Class クラスのオブジェクトとなる。
・RVMとパス
前回定義したとおり、
rvm によって複数のバージョンのRubyを管理して使用できる。
RVMによってバージョンを切り替えると、パスが変わる。
w84mba:ruby yuto$ rvm reset
w84mba:ruby yuto$ echo $PATH
/usr/local/bin:/opt/local/bin:/opt/local/sbin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin
w84mba:ruby yuto$ rvm use 1.9.2
Using /Users/yuto/.rvm/gems/ruby-1.9.2-p0
w84mba:ruby yuto$ echo $PATH
/Users/yuto/.rvm/gems/ruby-1.9.2-p0/bin:/Users/yuto/.rvm/gems/ruby-1.9.2-p0@global/bin:/Users/yuto/.rvm/rubies/ruby-1.9.2-p0/bin:/Users/yuto/.rvm/bin:/usr/local/bin:/opt/local/bin:/opt/local/sbin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin
パスが変わるので、shebang を書く場合、「/usr/bin/ruby」ではなく、「/usr/bin/env ruby」を使用する。
- 中括弧の使い方
オライリーの「初めてのRuby」では著者のyuguiさんが以下のような使い分けをしていると説明されている。
# 「初めてのRuby」より
・基本的に do end を利用する
・メソッドの戻り値を利用する場合のみ波括弧を使用する
・あるいは、a.hoge{}.huga{}のようにメソッドチェーンをする場合のみ波括弧を使うまた、筆者はイテレータには do end を、ブロックによるリソース管理を行う場合には波括弧を利用するようにしています。<略>
他にも1行でかけるものは波括弧を使う。endの後にメソッドが続き場合は波括弧にする、などの意見が。
- 黒魔術に触れる
ここまで出てきた話の中で、いくつの説明でRuby の様々な機能に関する説明があった。
そのなかでも、特にメタプログラミング的な機能に関する単語を拾って、自分でもその機能を試してみた。
# ただし、理解があやふやなので、読まれる方は説明文をあまり参考とせず、注意されたし。
・module
モジュールはメソッドや定数、クラス変数のグループに名前をつけたもの。
Rubyは多重継承をサポートしていないため、複数のクラスからの機能を
引き継ぐ機能として module をサポートしている。
module Foo def a puts "A" end class B def b puts "B-b" end end end class Bar include Foo def initialize B.new.b end end class Baz include Foo end Baz.new.a Bar.new # => A # => B-b
・特異メソッド
特定のオブジェクト専用のメソッド
ruby-1.9.2-p0 > s = "string" => "string" ruby-1.9.2-p0 > def s.output ruby-1.9.2-p0 ?> self + "-text" ruby-1.9.2-p0 ?> end => nil ruby-1.9.2-p0 > puts s.output string-text => nil
・ブロック
ブロックは中括弧かdo end で区切られたコードであり、
メソッド呼び出しの後ろに配置できる。
メソッドの引数に & を付けた場合、proc オブジェクトや method オブジェクトをブロックとして渡すことができる。
#! /usr/bin/env ruby # -*- coding:utf-8 -*- def foo(&block) block.call(10) end foo{|x| puts x * x } # => 100
・binding
Binding オブジェクトには、あるオブジェクト内の変数束縛の情報を返すオブジェクト(?)
eval の第2引数として binding を渡すことで、そのオブジェクトの内部の変数にアクセスすることができるようになる。
#! /usr/bin/env ruby # -*- coding:utf-8 -*- def foo bar = "binding" baz = "test" bindtest :bar,:baz,binding end def bindtest(n,m,context) context.eval('puts '+n.to_s+'+" "+'+m.to_s+'') end foo # => "binding test"
・method_missing
未定義のメソッドを読んだ場合、method_missing の第一引数としてメソッド名のシンボルを受け取り、第二引数以降も引数として受け付ける。
この method_missing の中で、処理を記述することで、動的にメソッドを定義するようなことも可能となる。
ちょっと気の利いた使い方がすぐにはおもいつきませんが……。
#! /usr/bin/env ruby # -*- coding:utf-8 -*- class String def method_missing(name) s = "" if name.to_s =~ /^head_(\d+)$/ (0..$1.to_i).each{|x| s = s + self[x]} return s elsif name.to_s =~ /^tail_(\d+)$/ (0..$1.to_i).each{|x| s = s + self.reverse[x]} return s.reverse else super end end end puts "method_missing_test".head_5 puts "method_missing_test".tail_5 # => method # => g_test
・__send__
Object#send メソッドを使うと、引数として指定したメソッドを実行出来る。
この時、指定したメソッドがprivate ,protected メソッドであっても、実行可能である。
#! /usr/bin/env ruby # -*- coding:utf-8 -*- class Foo def bar(n) "bar"*n end private def baz "baz!" end end foo = Foo.new puts foo.__send__(:bar,5) puts foo.__send__(:baz) # => barbarbarbarbar # => baz!
以上です。
他にも色々ありましたが、自分的に学べた部分はこれぐらい。
それにしても、Rubyのメタプログラミング的な側面に触れてみると、Rubyの凄さがまざまざとわかりますね。
動的に組み込みの機能をいくらでも拡張していけるあたり、驚くほど柔軟な上に強力。
かなり理解が難しい部分でもありますが……。