忘れたときに備えた記録

トップ 最新 追記
2005|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|11|12|
2009|01|02|03|04|05|06|10|12|
2010|06|07|08|12|
2011|07|09|
2012|09|11|
2013|02|03|09|
2015|10|11|
2016|01|08|11|
2017|02|08|10|
2018|11|

2007-06-02(Saturday)

風邪

風邪ひいて寝込んでいるうちに6月になってしまった。

読めなかったメールにとても大事なお知らせがあったり、Ruby会議2007が目前だったり、今回の風邪はどうもタイミング的に最悪。早く治らないものかな。

MathMLライブラリ with REXML on Ruby1.8.6

少し良くなってきたようなので、起きだしてWikiのコメントで報告されていた件について、調べてみた。

どうもRuby1.8.6でREXMLの挙動が変わったようで、Windowsばかりの話ではないらしい。MathMLで使っている部分以外にも変更があったらしく、こんな報告が出ている。

手元の環境でも、こんな感じだ。

まず、ソースその1

require "rexml/document"
require "rexml/element"
base = REXML::Element.new('base')
e = REXML::Element.new("sub")
e.add_attribute("test", "&")
base << e
p base.to_s

実行結果

~$ ruby -v
ruby 1.8.5 (2006-08-25) [i486-linux]
~$ ruby test.rb
"<base><sub test='&amp;'/></base>"
~$ $HOME/opt/ruby/bin/ruby -v
ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-linux]
~$ $HOME/opt/ruby/bin/ruby test.rb
"<base><sub test='&amp;amp;'/></base>"

1.8.6は手元でソースからコンパイルしたもの。ほかに例えばWindows版のOne Click Installerでインストールしたものでも同じ結果になるはず。

で、属性に'&amp;'と書くには、とりあえずこうしてみる。

require "rexml/document"
require "rexml/element"
base = REXML::Element.new('base')
e = REXML::Element.new("sub")
a = REXML::Attribute.new("test")
a.normalized = "&amp;"
e.add_attribute(a)
base << e
p base.to_s

実行する

~$ ruby test.rb
"<base><sub test='&amp;'/></base>"
~$ $HOME/opt/ruby/bin/ruby test.rb
"<base><sub test='&amp;'/></base>"

そんなわけで、同様の修正を行ったMathMLライブラリを公開しました


2007-06-04(Monday)

ho_enumerable.rb

Matzにっき(2007-05-30)で紹介されている、

#!/usr/bin/ruby
require "ho_enumerable"

class Test
   attr_accessor :v
   def initialize(v)
      @v = v
   end
end
a = []
(0..10).each do |i|
   a << Test.new(i)
end
p a.that.have.v==2

と書けてしまえるライブラリ。

いやいやいや、

a.that.have.v==2

とかありえないっしょ。などと驚いていてもしょうがないので、なんでこんな書き方が出来るのか調べてみた。

そんなわけで、a.that.have.v==2を順番に追いかけてみた。

a.that

ここで、HOM::Thatのインスタンスが返ってくる。That@receiver*1にはa自身が代入されている。

(a.that).have

これはThat#haveで、HOM::Haveのインスタンスが返ってくる。Have@handlerはさっきのThatのインスタンス。

(a.that.have).v

Hom::Have#method_missingが実行される。引数は次の2つ。

  • id = :v
  • args = []

戻ってくるのはHom::ResultMatcherのインスタンス。仮に名前をrmとすると、

  • rm@handler = a.that
  • rm@method = :v
  • rm@args = []

そして最後。

(a.that.have.v)==2

今度はHom::ResultMathcer#method_missingが実行される。引数は

  • id = :==
  • args = [2]

となる。このmethod_missingは以下のとおり。

def method_missing(id, *args)
  @handler.apply do |e|
    result = e.__send__(@method,*@args)
    result.__send__(id,*args)
  end
end

rm@handlerはa.thatなので、@handler.applyはThat#apply。このメソッドは単に@receiver.selectつまりa.selectを呼び出している。

これを踏まえてインスタンス変数を書き換えると

def method_missing(id, *args)
  a.select do |e|
    result = e.__send__(:v,[])
    result.__send__(:==,[2])
  end
end

となる。__send__も書き換えると

def method_missing(id, *args)
  a.select do |e|
    result = e.v
    result==2 # つまり e.v==2
  end
end

となる。こうして、

a.select{|i| i.v==2}

と同等の処理が行われるようになる。

2段階のmethod_missingで

a.that.have.v==2

の"v"と"==2"を巧く捉えているから、こんな書き方が出来るわけか。すごいなぁ。

ところで、ho_enumerable.rbの先頭、

require "delegate"

とあるけど、これいらないんじゃ

Tags: Ruby

*1 Thatインスタンスのインスタンス変数receiverをあらわす記法、ということにしておいて下さい


2007-06-14(Thursday)

日本Ruby会議2007

行って来ました。どの発表も興味深くて、とても面白い2日間でした。

以下、ざっと印象とか

  • Ruby1.9面白そう
    • 後でtruncのソースを持ってきて遊ぶ環境を作ってみよう
    • MathMLライブラリが動くかテストしておいたほうが良いかも
  • JRuby面白そう
    • 後で環境を作ってみたい
  • Rabbitすごい
  • ビジュアル系ライブラリ面白そう
    • ビジュアル系ライブラリ使ってMathMLライブラリに数式レンダリングを追加して、Rabbitで表示できるようにするとかしたら面白いかも
  • 愛って素晴らしい!

RejectKaigi

RejectKaigiで発表させてもらえたのですが、その資料をこちらに置いておきました

あわせて読みたい

なんだか面白そうなサービスだったので入れてみた。Javascriptではないので、iframe要素を使わなくても使えるのが素敵。

Apache2のバーチャルホストで全部suexecする方法

バーチャルホストを作って、その中で動くCGIを全部特定のユーサー権限で動かしたい。

なんでそんなことを考えたかというと、hgやgitの公開リポジトリ用のcgiを、Apacheのサーバアカウントwww-dataではなく、普段使っているユーザアカウントの権限で動かしたいから。

Subversionの場合は、サーバに入っているリポジトリの各ファイルの所有者がwww-dataでも、リモートから

$ svn cp http://svn.hinet.mydns.jp/trunc/hoge http://svn.hinet.mydns.jp/branch/hoge

とか操作することが出来たので特に困らなかった。一方、hgだとローカルのリポジトリしか操作できない(筈)ので、ブランチを作るときに

$sudo -u www-data hg branch .....

とかしないとCGIで公開しているリポジトリを操作できない。ユーザアカウントのpublic_htmlの下にリポジトリを置けばとりあえず解決できるのだけど、公開用のURLが http://www.hinet.mydns.jp/~hiraku/hg/proj とかなって、ちょっと長いし格好悪い。出来たら、http://hg.hinet.mydns.jp/proj でアクセスしたい。

そこで調べてみたら、Apacheの設定にSuexecUserGroup というディレクティブがあって、これをバーチャルホストの設定の中で指定すると、そのバーチャルホストで動くcgiが指定したアカウントの権限で動作するとわかった。

というわけで、早速試してみた。

<VirtualHost *>
    ServerName test.hinet.mydns.jp
    ServerAlias test

    DocumentRoot /home/hiraku/test
    SuexecUserGroup hiraku hiraku
</VirtualHost>

で、 /home/hiraku/test/test.cgiというファイルとして

#!/bin/bash
echo "Content-type: text/plain;"
echo
echo
whoami

を入れておいてアクセスしてみる。しかし、サーバーエラーが出て駄目。ログを見てみると、

[2007-06-14 22:12:50]: uid: (1000/hiraku) gid: (1000/1000) cmd: test.cgi
[2007-06-14 22:12:50]: command not in docroot (/home/hiraku/test/test.cgi)

となっていた。調べてみると、Debianの場合、コンパイル時のパラメータでこのdocrootが/var/wwwに指定されていて、test.cgiがその下に置かれていないからエラーになっているようだ。

そこで、

<VirtualHost *>
    ServerName test.hinet.mydns.jp
    ServerAlias test

    DocumentRoot /var/www/virtual/hiraku
    SuexecUserGroup hiraku hiraku
</VirtualHost>

として、/var/www/virtual/hiraku/test/test.cgiにさっきのファイルを作ってアクセスしてみた。

今度はOK。

ただし、/var/wwwはデフォルトのバーチャルサーバのDocumentRootにも指定されているので、http://www.hinet.mydns.jp/virtual/hiraku/test/test.cgi でもアクセス出来てしまう。これを防ぐために、デフォルトのバーチャルホストの設定に

   <Directory /var/www/virtual/>
       Deny from All
   </Directory>

とか書いておく。


2007-06-15(Friday)

JALマイステイの予約画面

ここの旅程作成画面で、飛行機をホテルを一通り設定し終わって「予約へ進む」ボタンを押すと、そのままウィンドウが閉じて先に進めないという問題にぶつかった。

JALに問い合わせたところ、ポップアップブロックがされていると次の画面が出てこなくなるとのこと。Firefoxを使っているので、www.tbj.jal.co.jpをポップアップブロックを許可するサイトに追加したところ、問題なく次の画面に進めるようになった。


2007-06-26(Tuesday)

Mercurial 0.9.4が公開

リリースノート

後でチェックしておく用にメモ。

Hiki 0.8.7も公開

Hiki - 変更点

早く更新しないと!!


2007-06-27(Wednesday)

test/unit の assertion を自作する

assert_hoge みたいなものを自作したときに、余分なbacktraceが表示されないようにする方法のメモ。

例えば、こんなコードを書いてみる。

#!/usr/bin/ruby
require "test/unit"
class TestTest < Test::Unit::TestCase
   def assert_even(n)
      assert(n%2==0)
   end

   def test_even
      assert_even(3)
   end
end

これを実行しても、

  1) Failure:
test_even(TestTest)
    [test1.rb:5:in `assert_even'
     test1.rb:9:in `test_even']:
<false> is not true.

こんな感じで余分なbacktraceが表示されてしまい、面白くない。

     [test1.rb:5:in `assert_even'

これを表示したくないわけだ。

本家のtest/unit/assertsions.rb が提供するassert_hoge ではどうやっているのかというと、 test/unit/util/backtracefilter.rb が提供する filter_backtrace メソッドで、単にtest/unit/以下のファイルのメソッドならbacktraceから削除しているだけのようだ。

また、railsで追加されているassert_responseなんかも、やはり余分なbacktraceが表示されない。こちらは、 actionpack/lib/action_controller/assertions.rb で定義されている clean_backtrace メソッドを使って、このassertsions.rb で定義されているメソッドの分のbacktraceを単に削除している。

てなわけで、もうちょっと一般化して assert_*というメソッドならbacktraceから削除するというようにしてみた。

#!/usr/bin/ruby
require "test/unit"
class TestTest < Test::Unit::TestCase
   def assert_wrapper
      yield
   rescue Object => e
      nb = [] # new backtrace
      nb = e.backtrace.reject do |i|
         p i if i=~/in `assert_/
         i=~/in `assert_/
      end
      raise e.class, e.message, nb
   end

   def assert_even2(n)
      assert_wrapper do
         assert_block("#{n} is not even") do
            n%2==0
         end
      end
   end

   def test_even2
      assert_even2(3)
   end
end

assert_wrapperメソッドで、backtraceから assert_ で始まるメソッドの分を削除している。実行すると

  1) Failure:
test_even2(TestTest) [test2.rb:24]:
3 is not even

こんな具合に見事余分なbacktraceが消えている。 ちなみにassert_blockを使ったのは、メッセージに

  1) Failure:
test_even2(TestTest) [test2.rb:22]:
3 is not even.
<false> is not true.

といった感じで元のassertのメッセージが追加されてしまうのを防ぐため。

Tags: Ruby

2007-06-28(Thursday)

Enumable#all?とany?は不要な反復を行わない

ソース

#!/usr/bin/ruby
a = [1, 2, 3, 4, 5]
a.any? do |i|
   p i
   i>2
end
a.all? do |i|
   p i
   i<2
end

実行結果

1
2
3
1
2
Tags: Ruby

Enumable#all?とany?を空の配列で実行したら

ソース

#!/usr/bin/ruby
a = []
p a.all? do |i|
   false
end

p a.any? do |i|
   true
end

結果

true
false

つまり、[].all?はtrueを、[].any?はfalseを常に返す。

Tags: Ruby

2007-06-29(Friday)

ほのぼのrake

開発中のプログラム作りで、依存するライブラリを含む全部のソースをリポジトリにチェックインするなどの雑多な作業をシェルスクリプトでこなしていたけど、ふと思いついてrakeに挑戦することにした。

参考にしているのはFile: rakefile.rdocなど。

タスクとファイル

makeで言うところの仮想ターゲット。cleanとかinstallとかの、ファイルではないけどターゲットとするやつだ。 rakeではファイルであるターゲットとファイルではないターゲットを明確に分けている。

task :hoge do
   puts "ほげぇ"
end

task :fuga => :hoge do
   puts "ふがぁ"
end

file "tmp" do
   touch "tmp"
end

依存関係は、タスク名の直後に=>に続けて書く。複数ある場合は配列で書ける。タスクに依存する場合はシンボル、ファイル(の存在)に依存する場合は文字列で指定する。

touchはFileUtilsのメソッド。だから、(事前に変数を作っていない限り)作成するファイル名は文字列で指定する。makeと混同していると単に tmp と書いてしまって「そんな変数ないっすよ」と怒られる。

コマンドラインで単に rake とした場合には、タスク default が実行される。

ファイルリスト

FileListメソッドでファイルの一覧を作れる。

task :default => FileList["*_test.rb"] do |t|
   p t.prerequisites
end

タスクの実体のブロックにはブロック変数が渡されて、そのprerequisitesメソッドで依存しているタスクやファイルを参照できる。

リストアップしたファイルのそれぞれに対して何らかの作業をする場合は、ここのファイルに対するfileターゲットを作るのではなく、ファイルリストに依存するタスクの中で、prerequisites#eachとかして行うようだ。

コマンド実行

Rubyの外のプログラムを実行するときはshメソッドを使う。これはFileUtilsではなくrakeが提供するメソッドらしい。もちろん、実行したコマンドが正常終了しなかった場合にはそこで例外が投げられてrakeの動作もそこで停止する。

task :exec do
   sh "mkdir test"
   sh "mkdir test"
end

サブディレクトリでの作業

FileUtils#cdのブロックの中で作業すれば良い。

task :clone do
   cd "lib" do
      sh "hg pull https://hg.hinet.mydns.jp/math_ml"
   end
end

cdメソッドに渡したブロックから抜けたときには、元のディレクトリに自動的に戻ってくれる。

Tags: Ruby

rakeの名前空間

railsの

~/$ rake test:unit

で使う"test:"などの名前空間は次のように定義する。

namespace "hoge" do
   namespace "fuga" do
      task :task do
         puts "ほげふがっ"
      end
   end

   task :fuga => "fuga:task"
end

タスクと名前空間は区別されているので、同じ名前の名前空間とタスクを併記できる。これによって名前空間のデフォルトタスクみたいな事が実現できる。例えばこの例だと hoge:fuga タスクは hoge:fuga:task タスクに依存しているので

rake hoge:fuga:task
rake hoge:fuga

はどちらも、同じタスクを実行する。

依存するタスクは文字列で書く。

Tags: Ruby

EmacsでRakefileの編集

EmacsでRakefileを編集する場合は、.emacsファイルに

(add-to-list 'auto-mode-alist '("[Rr]akefile" . ruby-mode))

と書いておくと、Rakefileの編集時にRuby-modeで動いてくれるのでネストを自動的にしてくれて嬉しい。

Tags: Ruby

includeした後のモジュールの改変

ソース

#!/usr/bin/ruby
module M1
   def m1
      puts "m1"
   end
end
class C
   include M1
end
module M2
   def m2
      puts "m2"
   end
end
module M1
   include M2
end

C.new.m1
C.new.m2

結果

m1
./test.rb:20: undefined method `m2' for #<C:0xb7cac6d4> (NoMethodError)

モジュールM1を定義し、クラスCでinclude M1 してから、別のモジュールM2を定義してM1でinclude M2しても、C#m2は使えない。

これが、Cでinclude M1した後に

module M1
   def m2
      puts "m2"
   end
end

とかしてメソッドの追加をした場合は、C#m2を使うことができる。

Tags: Ruby

Rakeでtest/unitを実行

rakeで

require "test/unit"

してあるスクリプトを実行するときの工夫。

def do_test(opts)
   opts << " -n #{ENV["N"]}" if ENV["N"]
   sh "ruby -I ../ -r test/unit #{opts}"
end

こういうメソッドをRakefileに書いておいて、テスト用のタスク(defaultが良さそう)の中で

do_test "hoge_test.rb"

とかする。 rakeを実行するときに環境変数を追加できることを利用して、特定のテストメソッドだけをテストしたい場合は

$ ./hoge_test.rb -n test_hoge

とする代わりに

$ rake N=test_hoge

として実行する。

テスト用のRakefileのテンプレートはこんな感じ。このままでも大抵のテストを実行できる。

TESTS = FileList["*_test.rb"]

def do_test(opts)
   opts << " -n #{ENV["N"]}" if ENV["N"]
   sh "ruby -I ../ -r test/unit #{opts}"
end

task :default do
   TESTS.each do |i|
      do_test i
   end
end
Tags: Ruby