MySQL/Rubyのバージョン確認方法

gemでインストールするMySQL/Rubyのバージョンは2.7と少々古いので、念の為に2.7.4にバージョンアップしておく。

require 'mysql'
Mysql.const_get(:VERSION)
=> 20704 

でバージョンをチェックする。
20704だと2.7.4になる。


今見てみたら1.9対応の2.8pre1がリリースされている。Railsが対応できているかどうか怪しいので2.7系に留めておくのが無難か。
MySQL/Ruby

Rubyでの正規表現についての覚書

Ruby正規表現オブジェクトを作る方法は以下の三通り。

  1. /pattern/option
  2. %r{pattern}option
  3. Regexp.new('pattern', option)


通常扱うのは/pattern/option。所謂正規表現リテラルで、リテラル内には式展開を含める事ができる。

/hoge#{"foo"}/
# => /hogefoo/


ただし、optionには式展開を適用することができない。これはコンパイルエラーにもならないので注意が必要。

/hoge#{"foo"}/#{"ni"}
# => /hogefoo/ #式展開でくっつけたoption値が無視されている。

この問題は%r記法でも同様だった。
optionを動的にしたい場合はRegexp.newメソッドを使う。

Regexp.new("hoge", "i")
# => /hoge/i

Regexp.new("hoge", "o")
# => /hoge/i


……あれ?
どんな値を入れても何故かignore caseがオプションになってしまう。


Regexpのrdocを見て謎が解明。

第二引数が Fixnum であった場合、その値は

Regexp::IGNORECASE
Regexp::MULTILINE
Regexp::EXTENDED
論理和でなければなりません。

第二引数が Fixnum 以外であれば真偽値の指定として見なされ、真 (nil, false 以外)であれば Regexp::IGNORECASE の指定と同じになります。

第三引数が与えられた時には、$KCODE の値にかかわらず、指定された文字コードでマッチを行います。文字コードは $KCODE への代入と同様に文字列引数の最初の一文字で決定されます。

http://www.ruby-lang.org/ja/man/?cmd=view;name=Regexp


つまり、文字コードの取り扱いは第3引数で、第2引数はそれ以外のオプションになるようだ。今回は文字コードを動的に変えたかったので、最終的には以下のようなコードとなった。

charset = "o"
Regexp.new("hoge", nil, charset)
# => /hoge/o

charset = "u"
Regexp.new("hoge", Regexp::MULTILINE , charset)
# => /hoge/mu

インストールモジュールを確認する

bin/httpd -M 2>&1 | sort
 actions_module (static)
 alias_module (static)
 asis_module (static)
 auth_basic_module (static)
 authn_default_module (static)
 authn_file_module (static)
 authz_default_module (static)
 authz_groupfile_module (static)
 authz_host_module (static)
 authz_user_module (static)
 autoindex_module (static)
 cgid_module (static)
 core_module (static)
 dir_module (static)
 env_module (static)
 expires_module (static)
 filter_module (static)
 headers_module (static)
 http_module (static)
 include_module (static)
 log_config_module (static)
 mime_module (static)
 mpm_worker_module (static)
 negotiation_module (static)
 proxy_ajp_module (static)
 proxy_balancer_module (static)
 proxy_connect_module (static)
 proxy_ftp_module (static)
 proxy_http_module (static)
 proxy_module (static)
 rewrite_module (static)
 setenvif_module (static)
 so_module (static)
 status_module (static)
 userdir_module (static)
Loaded Modules:
Syntax OK

Apacheのコマンドよりも標準エラーを標準出力にリダイレクトさせるコマンドが覚えられない…。

adv_attr_accessorの実装から感じるRailsの哲学

ActionMailerのソースを読んでいたら、

  adv_attr_accessor :bcc

と言うコードが合ったので、このadv_attr_accessorという謎のメソッドを調べてみた。
短かいコードだったので全文引用してしまう。

module ActionMailer
  module AdvAttrAccessor #:nodoc:
    def self.included(base)
      base.extend(ClassMethods)
    end

    module ClassMethods #:nodoc:
      def adv_attr_accessor(*names)
        names.each do |name|
          ivar = "@#{name}"

          define_method("#{name}=") do |value|
            instance_variable_set(ivar, value)
          end

          define_method(name) do |*parameters|
            raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
            if parameters.empty?
              if instance_variables.include?(ivar)
                instance_variable_get(ivar)
              end
            else
              instance_variable_set(ivar, parameters.first)
            end
          end
        end
      end
    end
  end
end

setterはCで書かれたデフォルトのアクセッサをそのままRuby版に置き換えたようなコードだ。
一方、getterは独自実装になっており、引数がある場合はその引数を変数にセットすると言う内容。


つまり、目的は#=の代わりに

class C
  adv_attr_accessor :bcc
  
  def do_something
    bcc "hoge@hoge.com" #== bcc = "hoge@hoge.com"
  end

と書きたい、と言う事のようだ。
ActionMailerのサンプルでは以下のようなコードがよく出てくる。

  def mail(message)
    recipients message[:address]
    from   message[:from]
    subject message[:subject]
    body message[:body]
  end


DSLっぽいインターフェースを実現したい、と言う為だけに新しいアクセッサを用意する辺りがRailsらしい。こう言った強引さがRails以前からのRubyistからすると不満なのだろう。Railsフレームワークと言う枠を飛び越えて、Rubyimproveした新しい言語になろうとしているかのようで。


必要に迫られてRailsのコードを読む事もあるが、Railsのコードは一行一行から思想が滲み出ていて、読んでいて単純に楽しい。勉強にもなる。RailsだってやっぱりRubyで出来ているんだよな、なんて事を思った。

BackgrounDRbのWorkerオブジェクト削除タイミングについての悩み

BackgrounDRbのサンプルでよく見かけるのが以下のようなソース。

  def start_background_task
    session[:KEY] = MiddleMan.new_worker(:class => :foo_worker, :args => "hoge")
  end
  
  #start_background_task.rhtmlから非同期で呼ばれるアクション
  def update_progress
    if request.xhr?
      worker = MiddleMan.worker(session[:KEY])
      progress_percent = worker.progress_percent
      render :update do |page|
        page.call('progressPercent', 'progressbar', progress_percent)
        page.redirect_to(:action => 'done') if progress_percent >= 100
      end
    end
  end

  def done
    MiddleMan.delete_worker(session[:KEY])
  end


分かりやすい例なのだけれど、肝心な点が抜けている。
それは、「処理中にブラウザを閉じたらどうなるのか?」と言う事。
このソースだと、処理の完了後に表示されるアクションでワーカーオブジェクトの削除処理を実装している為、途中でブラウザを閉じたりするとワーカーオブジェクトが削除されなくなってしまう。


じゃあどうしよう、という事で悩んでいたのだが、タイミングよくWEB+DB PRESS 40号のRails連載記事でBackgrounDRbが紹介されていた。
記事では、ワーカーの作成時のオプションにttlとexpire_typeと言う値を指定できるとの事。
例えば以下のように書くと、最後にアクセスした時刻から300秒経過するとワーカーオブジェクトが削除されるらしい。

  MiddleMan.new_worker(:class => :foo_worker, :args => "foo", :ttl => 300, :expire_type => :accessed)


キタコレと思ったものの、BackgrounDRbのソースを読んだ時にはttlとexpire_typeオプションを処理している箇所が見当たらなかった。
再度見なおしても、やっぱりない…。
記事内ではBackgrounDRbのバージョンについて何も言及されていなかったのだけれど、WEB+DB PRESSのサポートページにソースがアップされていた。このソースだと、BackgrounDRb起動時に削除用のスレッドが生成され、それがttlとexpire_typeオプションを見てワーカーオブジェクトを削除するように実装されていた。


今のBackgrounDRbの最新版は0.21だが、過去のバージョンは0.20までしかない。
どちらのバージョンにも上記の処理がなかったところを見ると、WEB+DB PRESSで使用したBackgrounDRbのバージョンはかなり古いものなのだと思う。


うーんどうしよう…。
古いバージョンに戻すのも怖いし、かと言ってそれなりに巨大化した今のBackgrounDRbを勝手に拡張して問題が起こらない自信もなし。
そもそもあって然るべきの削除用のスレッド周りの処理が何故削除されたのか、という事情も分からないからなあ。


もうちょっと調べてみよう。

Ctrl + Cで終了しないRubyプログラム

バッチ用のRubyプログラムをちょっと動かしてからCtrl + Cで終了させたものの、止まってくれなくて焦る。標準出力にガンガン出力されていく…。
kill -s 9
で強制的にKillするが、そもそもCtrl + Cで止まらないことが問題。


ちょっと考えてみたところ、原因は簡単だった。
例外処理のところでJava時代の癖が残っていて、Exceptionクラスをキャッチしてしまっていたのだ。
Ctrl+Cを押下した際にRuby側に例外として投げられるのはInterruptクラス。スーパークラスを辿って行くとSignalException -> Exceptionクラスとなる。
プログラミング言語 Ruby リファレンスマニュアル


つまり、例外が発生して終了すべきところを、プログラム内でキャッチして吸収してしまったと言う事。最低だ…。


教訓:
例外を明示的にキャッチする時は一番粗い粒度はStandardErrorクラスとする事。
Exceptionクラスはキャッチしちゃダメ、絶対。