黒魔術に触れる

すでに1週間以上前の出来事ではありますが、前回の日記に続き、 yokohama.rb 第4回に参加しました。

http://atnd.org/events/11853

今回は jugyo さんによる発表をもとに、みんなでRubyを凄さを学ぶ、という内容でした。

以下、自分的な内容のメモ。

  • Ruby すげー、という点

irb便利
 言わずもがな。irb コマンドですぐにREPLが起動してコードを確認できる。

オブジェクト指向
 Rubyはオブジェクトとしてよくできてる。

>> 1+1
# => 2

 は、実はメソッド呼び出しになっている。

>> 1.+(1)
# => 2

 このようにオブジェクトとして一貫性のあるところがすごい。
 そして、正しくものが作れる感じがする。

オープンクラス
 前回の勉強会で each を再実装したように、
 既存のクラスを拡張して簡単に機能を拡張できる。

DSLが作り易い
 例:rake など

  • ライブラリの作り方

 jeweler でgem が楽に作れる

https://github.com/technicalpickles/jeweler

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
http://rvm.beginrescueend.com/

 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の凄さがまざまざとわかりますね。
動的に組み込みの機能をいくらでも拡張していけるあたり、驚くほど柔軟な上に強力。
かなり理解が難しい部分でもありますが……。