Vimの正規表現では常に\vを使うことにする

vim正規表現は以下のような変なクセがある。

:echo match('aaa', 'a*') => 0 (マッチする)
:echo match('aaa', 'a+') => -1 (マッチしない)

このような結果になるのは、*はエスケープが不要だが、+はエスケープが必要なため。

:echo match('aaa', 'a\+') => 0

とすればマッチする。

エスケープが必要なのか不要なのか、メタキャラクタによってまちまちで分かりにくい。
また、magicというオプションによっても、挙動が変わる。

このややこしさは、\vというメタキャラクタを使うと解決できる。

:echo match('aaa', '\va+') => 0

このように正規表現の最初に\vを付加すると、あらゆるメタキャラクタがエスケープ無しで効果を発揮するようになる。

詳細は以下のヘルプで参照

:help magic

Rubyのカレントクラスという概念

書籍「メタプログラミングRuby」を読んでカレントクラスという概念を初めて知った。
Rubyはselfで参照できるカレントオブジェクトとは別に、常にカレントクラスを持っているとのこと。

defキーワードでメソッドを定義する時、このカレントクラスに対してインスタンスメソッドを定義する。

classキーワードでクラスをオープンすると、カレントクラスがそのクラスになるため、クラス定義内で定義したメソッドはそのクラスのインスタンスメソッドとなる。

class_evalでクラスをオープンした場合も同様で、レシーバのクラスがカレントクラスとなる。

class Hoge
end

Hoge.class_eval do # カレントクラスがHogeになる
  def meth
  end
end

Hoge.instance_methods(false) #=> [:meth]

class_evalはカレントオブジェクトも変更するので、
class_evalの動作は、「カレントオブジェクトとカレントクラスをレシーバのクラスに変更する」、と明確に定義することが出来るのだ。
ここら辺は今まで曖昧に理解していたので目から鱗だった。

ちなみにinstance_evalは、
「カレントオブジェクトをレシーバのオブジェクトに変更し、カレントクラスをレシーバのオブジェクトの特異クラスに変更する」。
そのためinstance_evalを使って、オブジェクトの特異メソッドを定義したり、クラスメソッドを定義することができる。

class Hoge
end

obj = Hoge.new
obj2 = Hoge.new

obj.instance_eval do # カレントクラスがobjの特異クラスになる
  def obj_meth
    puts 'obj'
  end
end
Hoge.instance_eval do # カレントクラスがHogeの特異クラスになる
  def hoge_meth
    puts 'hoge'
  end
end

obj.obj_meth #=> obj
obj2.obj_meth #=> NoMethodErrorが発生
Hoge.hoge_meth #=> hoge

ところで、メソッドをネストした時のカレントクラスは意外なものになる。

class Hoge
  def meth1
    def meth1_inner
    end
  end

  def self.meth2
    def meth2_inner
    end
  end

Hoge.new.meth1 # meth1_innerを定義する
Hoge.meth3 # meth2_innerを定義する

Hoge.instance_methods(false) #=> [:meth1, :meth1_inner, :meth2_inner]
Hoge.singleton_methods #=> [:meth2]

meth1_innerはクロージャにならずに、Hogeクラスに定義される。
クラスメソッド内で定義したmeth2_innerも同様。

結局、カレントクラスについては、クラスをオープンした時にのみ変更されて、
たとえメソッド定義内であっても関係なく常にオープン中のクラスがカレントクラスになっていると考えれば良さそう。


カレントクラスは便利な概念なのでselfのようにプログラムから参照できたら良いのにと思っていたら、そういう提案が既にされていた。(http://redmine.ruby-lang.org/issues/4043

Rails3のflashの新記法ではまった

Rails3のredirect_toで:noticeオプションを付けることでflashを使えるようになった。

ということで

flash[:notice] = 'message'
redirect_to :action => 'index'

と書いていたものを

redirect_to {:action => 'index'}, :notice => 'message'

と修正したのだがこのままだとエラーになってしまう。
ネットで調べると、どうやら引数で渡したハッシュの「{」記号がブロックとして解釈されてしまうらしい。

redirect_to ({:action => 'index'}, :notice => 'message')

このように引数全体をカッコで括ってもだめ。


結局以下の書き方が正解だった。

redirect_to ({:action => 'index'}), :notice => 'message'

とても気持ち悪いけど仕方ないのかな?

RVMのインストール

開発環境のrubyをrvmで管理することにしたので、導入手順をまとめておく。
(参考 https://rvm.beginrescueend.com/rvm/install/)
rvmのインストールにはbashcurlとgitが必要なので事前に入れておくこと。
rvmは以下のコマンドでインストールする。

$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

このコマンドを実行すると、gitレポジトリから最新バージョンのrvmを取ってきて、カレントユーザーのホームディレクトリ直下に、~/.rvm/というディレクトリを作成し、そこへインストールしてくれる。
この際に~/.rvm/bin/にあるスクリプト群が勝手に~/bin/にコピーされたのが気持ち悪かったので、これらを削除して~/.rvm/binにパスを通すことにした。

$ rm ~/bin/rvm*
$ export PATH=$PATH:$HOME/.rvm/bin

インストールされたrvmのバージョンを確認してみる。

$ rvm --version
rvm 1.6.14 by Wayne E. Seguin (wayneeseguin@gmail.com) [https://rvm.beginrescueend.com/]

これでrubyのインストールが可能になるのだが、その前にインストールに必要な依存ライブラリを確認しておく。
以下のコマンドで確認できる。

$ rvm notes
…(省略)
dependencies:
# For RVM
  rvm: bash curl git

# For Ruby (MRI & ree)  you should install the following OS dependencies:
  ruby: /usr/bin/apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev

# For JRuby (if you wish to use it) you will need:
  jruby: /usr/bin/apt-get install curl g++ openjdk-6-jre-headless
  jruby-head: /usr/bin/apt-get install ant openjdk-6-jdk

# In addition to ruby: dependencies,
  ruby-head: subversion

# For IronRuby (if you wish to use it) you will need:
  ironruby: /usr/bin/apt-get install curl mono-2.0-devel

自分の環境に無いものをインストールする。

$ sudo aptitude install bison build-essential libreadline6-dev libssl-dev libyaml-dev sqlite3 libxslt-dev autoconf ncurses-dev 

インストール可能なrubyのバージョンを確認をする。

$ rvm list known
# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.6-head
[ruby-]1.8.7[-p334]
[ruby-]1.8.7-head
[ruby-]1.9.1-p378
[ruby-]1.9.1[-p431]
[ruby-]1.9.1-head
[ruby-]1.9.2[-p180]
[ruby-]1.9.2-head
ruby-head
…(省略)

ruby-1.8.7-p334をインストールする。

$ rvm install 1.8.7
'gem' command not found, cannot select a gemset.
Installing Ruby from source to: /home/ak/.rvm/rubies/ruby-1.8.7-p334, this may take a while depending on your cpu(s)...

ruby-1.8.7-p334 - #fetching 
ruby-1.8.7-p334 - #downloading ruby-1.8.7-p334, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 4092k  100 4092k    0     0   616k      0  0:00:06  0:00:06 --:--:-- 2746k
ruby-1.8.7-p334 - #extracting ruby-1.8.7-p334 to /home/ak/.rvm/src/ruby-1.8.7-p334
ruby-1.8.7-p334 - #extracted to /home/ak/.rvm/src/ruby-1.8.7-p334
ruby-1.8.7-p334 - #configuring 
ruby-1.8.7-p334 - #compiling 
ruby-1.8.7-p334 - #installing 
Removing old Rubygems files...
Installing rubygems dedicated to ruby-1.8.7-p334...
Retrieving rubygems-1.6.2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  236k  100  236k    0     0  47095      0  0:00:05  0:00:05 --:--:-- 2077k
Extracting rubygems-1.6.2 ...
Installing rubygems for /home/ak/.rvm/rubies/ruby-1.8.7-p334/bin/ruby
Installation of rubygems completed successfully.
ruby-1.8.7-p334 - adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
ruby-1.8.7-p334 - #importing default gemsets (/home/ak/.rvm/gemsets/)
'gem' command not found, cannot select a gemset.
Install of ruby-1.8.7-p334 - #complete 

このコマンドによって、
ruby1.8.7
~/.rvm/rubies/ruby-1.8.7-p337/に、
rubygems1.6.2が
~/.rvm/gems/ruby-1.8.7-p334と
~/.rvm/gems/ruby-1.8.7-p334@globalにインストールされた。

同様にruby-1.9.2-p180をインストールする。

$ rvm install 1.9.2
'gem' command not found, cannot select a gemset.
Installing Ruby from source to: /home/ak/.rvm/rubies/ruby-1.9.2-p180, this may take a while depending on your cpu(s)...

ruby-1.9.2-p180 - #fetching 
ruby-1.9.2-p180 - #downloading ruby-1.9.2-p180, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 8609k  100 8609k    0     0  1139k      0  0:00:07  0:00:07 --:--:-- 3698k
ruby-1.9.2-p180 - #extracting ruby-1.9.2-p180 to /home/ak/.rvm/src/ruby-1.9.2-p180
ruby-1.9.2-p180 - #extracted to /home/ak/.rvm/src/ruby-1.9.2-p180
ruby-1.9.2-p180 - #configuring 
ruby-1.9.2-p180 - #compiling 
ruby-1.9.2-p180 - #installing 
Removing old Rubygems files...
Installing rubygems dedicated to ruby-1.9.2-p180...
Installing rubygems for /home/ak/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
Installation of rubygems completed successfully.
ruby-1.9.2-p180 - adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
ruby-1.9.2-p180 - #importing default gemsets (/home/ak/.rvm/gemsets/)
'gem' command not found, cannot select a gemset.
Install of ruby-1.9.2-p180 - #complete 

インストールされたものを確認してみる。

$ ls -l ~/.rvm/rubies/
ruby-1.8.7-p334/
ruby-1.9.2-p180/
$ ls -l ~/.rvm/gems/
cache/
ruby-1.8.7-p334/
ruby-1.8.7-p334@global/
ruby-1.9.2-p180/
ruby-1.9.2-p180@global/

rubyのバージョンごとに複数のrubygemsを作成することが出来るようになっており、
rubyのバージョン同様rubygemsを自由に切り替えることが出来る。
デフォルトでは、一つのrubyのバージョンに対して、「名前無し」と「global」というrubygemsが作られる。

インストール済みのrubyを確認する。

$ rvm list
rvm rubies

   ruby-1.9.2-p180 [ i386 ]
   ruby-1.8.7-p334 [ i386 ]

この時点でrubyコマンドやirbコマンドなどを使えるのだが、

$ ruby-1.8.7-p334
$ irb-ruby-1.9.2-p180

のようにバージョン付きのコマンドを使い分けなければならない。
これでは意味が無いので、rubyirbと指定しただけで特定のバージョンのコマンドを実行できるように設定する。
この機能を使うには以下を.bashrcに記述する必要がある。

[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

後は以下のようにコマンドを実行すると、rubyバージョンとrubygemsを切り替えることが出来る。

rvm @

$ rvm 1.8.7 # ruby(1.8.7), rubygems(名前無し)に切り替わる
$ ruby --version
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-linux]
$ rvm 1.9.2@global # ruby(1.9.2), rubygems(global)に切り替わる
$ ruby --version
ruby 1.9.2p180 (2011-02-18 revision 30909) [i686-linux]

この設定はシェルを閉じると無効になってしまう。
シェルを閉じても設定を維持させるには以下のように--defaultオプションを付けて指定する。

$ rvm --default 1.9.2

新しくrubygemsを作成する場合は、

$ rvm 1.8.7

のように作成先のrubyバージョンを指定してから

$ rvm gemset create rails235

と実行する。
ちゃんとrubygemsが作成されたか確認してみる。

$ rvm gemset list
gemsets for ruby-1.8.7-p334 (found in /home/ak/.rvm/gems/ruby-1.8.7-p334)
   global
   rails235

gem installコマンドを使うと設定中のrubygemsにのみインストールされる。
ただしglobalなrubygemsにインストールしたライブラリは特別扱いされ、そのrubygemsが所属するrubyバージョンを設定している間は常に利用できる。
例えば、ruby-1.8.7@rails235に設定している時でもruby-1.8.7@globalにインストールしたライブラリを参照できる。
rubygemsは自由に切り分けることが出来るが、Railsのバージョンが異なると依存する多くのライブラリも変わってくるのでRailsのバージョンごとにrubygemsを作るのが良さそう。

rvm自体をアップデートするには以下のコマンドを実行する。

$ rvm get latest
$ rvm reload

rvmをアンインストールする場合は、~/.rvmディレクトリを丸ごと削除すればよい。
rvmコマンドのヘルプ参照は以下の通り。

$ rvm help

公式HPもドキュメントが充実してるので要参照。