Tag Archives: ruby

Moduleでextend self

なるほどなーと思ったのでメモ。

Moduleのクラスメソッドを作りたいとき、次のような方法もある。

module Foo
  extend self

  def some_method
    puts "Hello!"
  end
end

実行すると次のようになる。

irb> Foo.some_method
#=> Hello!

module_methodだと取りこぼしがあり得るのでこの方法なら安心。 もしプライベートメソッドを作りたければprivateを付ければいい(これは結構いい)。 関数的な処理をまとめるだけでSingletonパターンを使うほどではないっていうときにきっと便利。

聞きかじったことをメモしてるだけなのでクラスでincludeしたらどうなるのかは気になるところだけど、多分インスタンスメソッドとしてもsome_methodが使えるんだろうな。


ファイルの更新を検知してコマンドを実行するRubyスクリプト

lessとかautospecとかはファイルの更新を検知してコマンドを実行してくれるけど、それっぽいのを任意のファイルとコマンドについてやりたくなったのでRubyで書いてみた。

コマンド名は適当にjamieとする(誰かすでに書いてあるだろうものに名前をつけるのもおこがましいけど)。次のような感じで使う:

$ jamie
Usage: jamie file command
$ jamie ~/.zsh_history "echo 'こんにちは!こんにちは!'"

やっつけでつくったのでクオリティとかは気にしない。

追記 (2010-11-12)

動機は、autospecだとそのときテストしていたspecファイルが通ったら自動で全部のspecファイルをテストするので、バッチ処理などの独立性の高いスクリプトを書いてるときにちょっとウザったかったこと。なので指定した一つのspecファイルを継続的にテストしてくれる仕組みが欲しかった。

で、思わぬ副作用として得られたのが、テスト対象のspecファイルが文法エラーなどで例外を吐いてもこのスクリプトだとループの処理自体は止まらない。autospecだと止まってしまっていたので、全体のテストの実行時間が長い現状ではなかなか辛いものがあった。


Shibuya.rbに行ってきた

ECナビさんのバーAJITOで行われた第1回Shibuya.rbに行ってきた。

LT的なものの内容を覚えている限り書き出してみる。

  • 渋谷.rbとか東京Ruby会議04とか
    • お手伝いしてくれる人募集中
  • Azabu.gem
    • 毎週水曜の19:30-21:30
  • DSLの話
    • DSLを使ってスライドを作る
    • Excel方眼紙
  • BrainF*ckやってみた
    • []のネストに未対応
  • メタプログラミングRuby読んでる
    • 定数名から定数を取得してポリモーフィズム
    • PHPだとnew "Hoge"ができる
  • 11/20にRailsDevConあるよ
    • お手伝いしてくれる人募集中
    • 内容募集中

ネタとして「CassandraのORMにチャレンジ」があったけど、これは「Cassandra+ActiveSupport+ActiveModelでActiveRecordっぽいORMを作ってみる」ことを目的としてるので、実用性という面ではCassandraObjectとかSmallRecordとかを使った方が良いと思う&扱ってるものが広すぎてなにを話せば良いのやら&最近触っていない、というわけで話さなかった。 CassandraObjectを使ってちゃんと何か作るのが一番いいんだろうな。 ちなみに http://github.com/a2ikm/chronicle のlib/crossingというやつがそれ。これが使えるかどうかはどうでもいいんだけど、とりあえずメタプログラミングは便利!クラスの動的生成とか凄い。

ほか、ちょいちょいTwitterのアイコンを知られているのが嬉しかった。 渋谷は職場から近いのでぶらりと寄れたのがとても良かった。 東京Ruby会議04のお手伝いをすることにした。よろしくお願いします。


ActiveSupport::Concern

以前書いたモジュールの特異メソッドをincludeして使うと同じことが、ActiveSupport::Concernを使うことでもうちょっと綺麗に書ける。

module A
  extend ActiveSupport::Concern

  included do
    # Aがincludeされた際に、includeしたクラスのコンテキストで実行される
  end

  module InstanceMethods
    def instance_method_of_a
      p "instance_method_of_a"
    end
  end

  module ClassMethods
    def class_method_of_a
      p "class_method_of_a"
    end
  end
end
class B
  include A
end

こんなモジュールをとあるクラスBでincludeすることで、

  • InstansMethods以下に定義されたメソッドがincludeによってインスタンスメソッドとして追加
  • ClassMethods以下に定義されたメソッドがextendによってクラスメソッドとして追加

される。

モジュールをincludeすることによって追加されるクラスメソッド、インスタンスメソッドが明確になるので嬉しい。

ただそれだけだとちょっとメリットが弱い気がしなくもない。きっとincludedや、今回触れなかったextendedあたりが活躍するんだろう。

参考資料


ActiveModel::Callbacks

ActiveModel::Callbacksを使えばafter_saveなどのコールバックを手軽に実装できる。

まずActiveRecord::Baseのような抽象クラスを次のように定義する。

class AbstractModel
  extend ActiveModel::Callbacks
  define_model_callbacks :save

  def save
    _run_save_callbacks do
      # 具体的なsaveメソッドの内容
      # この前後にbefore_saveとafter_save、そしてaround_saveが呼ばれる
    end
  end
end

重要なのはsaveメソッドの内容を_run_save_callbacksでラップすること。これはコールバックを利用するメソッドに応じて動的に生成され、createメソッドであれば_run_create_callbacksを利用する。

そしてコールバックを利用したい具象クラスでは次のようにする。

class SomeModel < AbstractModel
  before_save :foo

  protected

  def foo
    # before_saveで実行したい内容
  end
end

これ以外にもbefore_saveにブロックを渡したり、before_saveをインスタンスメソッドとして定義しているオブジェクトを渡したりすることで処理を実装できる。

define_model_callbacksでは複数のコールバック対象のメソッドを指定することができる。

define_model_callbacks :save, :create

:onlyオプションを使うことで例えばafter_*だけに制限することもできる。

define_model_callbacks :save, :only => :after

オブジェクトをbooleanとして評価

rubyでオブジェクトをbooleanとして評価した値を得たい場合、次のようにすれば良い。

!!object

!objectで反転した真偽値が得られるので、さらに!で反転することでobjectをbooleanとして評価している。


rvmふたたび

ruby 1.9.2が出たのでrvmを入れ直して整理した。

$ gem install --user-install rvm
$ PATH=$HOME/.gem/bin:$PATH
$ rvm-install
$ vi ~/.zshrc # ~/.gem/binをパスに追加、rvmの初期化コードを追加
$ exit

1.9.2と1.8.7をインストールする。 ただしMacPortsで入れたreadlineを使うために今回はオプションを付ける。

$ rvm install 1.8.7 -C "--enable-shared=true,--with-opt-dir=/opt/local"
$ rvm install 1.9.2 -C "--enable-shared=true,--with-opt-dir=/opt/local"

とりあえず1.8.7にRails2.3のgemsetを作り、1.9.2にRails3 RCのgemsetを作る。 そして前者をデフォルト(シェルを立ち上げたときに最初に読み込む)のgemsetとする。

$ rvm use 1.8.7@rails23 --create --default
$ gem install rails -v 2.3.8
$ rvm use 1.9.2@rails3rc --create
$ gem install rails --pre

rails2.3で作ったプロジェクトでは1.8.7@rails23を使いたいので、RAILS_ROOTで次のように.rvmrcを作る。rails3rcのほうも同様。

$ echo "rvm use 1.8.7@rails23" > .rvmrc

MeCabをrubyから使ってみる

インストール

まずMacPortsで本体と辞書を入れて読み込む辞書を設定する。

$ sudo port install mecab
$ sudo port install mecab-ipadic-utf8
$ sudo vi /opt/local/etc/mecabrc # 編集内容は下記を参照

mecabrcは次のように書き換える。

dicdir = /opt/local/lib/mecab/dic/ipadic-utf8

次にrubyバインディングのmecab-rubyをインストールする。 ソースを SourceForge から落としてきて展開し、そのディレクトリで以下を実行。

$ ruby extract.rb
$ vi Makefile # 編集内容は下記を参照
$ make
$ ruby test.rb # これでエラーが出なければ問題無し
$ sudo make install

Makefileは次のように/opt/local/libを読むように修正を加える。

LIBS =  -lstdc++ -ldl -lobjc -L/usr/local/lib -L/opt/local/lib -lmecab

参考: MeCab + Ruby で形態素解析(わかち書き)

rubyから使う

irbで次をやってみる。

require 'MeCab'
wakati = MeCab::Tagger.new('-O wakati')
puts wakati.parse('最近の夜は寒い')

mecab = MeCab::Tagger.new()
node = mecab.parseToNode('最近の夜は寒い')
while node do
  puts "#{node.surface}\t#{node.feature}"
  node = node.next
end

参考: MeCab + Ruby で形態素解析(わかち書き)

地味にはまったのがnewするときの引数。特にオプションを指定しないなら上記のようになにも与えないか、もしくは空の文字列を与える。明示的にnilを与えるとエラーになるので要注意。


モジュールでnamed_scopeを追加

モデルの機能ごとにモジュールにまとめてあとからまとめてincludeしたいというときに、どうせならその機能のために用意したちょっと複雑なnamed_scopeも同じように外に出してしまいたい。

そんな場合にはincludedとclass_evalを使う。

class Message < ActiveRecord::Base
  include Extensions::Search
end

module Extensions
  module Search
    def self.included(mod)
      mod.class_eval do
        named_scope :name_like, lambda { |arg| {
          :conditions => ["name LIKE ?", "%%arg%%"]
        }}
      end
    end
  end
end
> Message.name_like("ob").first.name #=> "Bob"

named_scopeも結局はクラスメソッド。この方法はnamed_scope以外にも利用できる。


テスト用oauth_callback

oauth gem(0.4.0)のoauth_callbackでちょっとはまったのでメモ。

/rubyを参考に、Twitterに登録しているコールバックURLとは別にテスト用のURL(が使いたかったので、いろいろ試してみた結果、次で動いた。

test_callback  = "http://example.org/login"
request_token = consumer.get_request_token(:oauth_callback => test_callback)
aurhorize_url = request_token.authorize_url(:oauth_callback => test_callback)

なんで両方で指定しなければならないのかは謎。