| @技術/プログラミング

Rails で public_activity.gem を使っていて、 activities テーブルにレコードが追加されたタイミングで callback を仕掛けたい衝動に駆られた。ちょっと調べてみたけどやり方が見つからなかったので、チームの人と相談して以下のようにした。 public_activity は PublicActivity::Activity というモデルを gem の中に持っていて、こいつが belongs_to :model になる。

なのでこのモデルクラスを open して以下のように記述した。

# RAILS_ROOT/lib/public_activity/activity.rb

module PublicActivity
  class Activity
    after_create do
      HogeHogeMailer.send_mail(self.trackable).deliver
    end
  end
end

ただしこのファイルを config/initializers/ とかで require してやらないと Rails がファイルを読み込んでくれない。 Rails.application.config.autoload_pathslib/**.rb とかを追加しとけば自動的に読み込まれるんじゃないかなと思ったけどそうじゃなかった。 Rails の autoload は、 ConstMissing という例外が発生したときに定数の名前からファイル名を推測して require するらしい。名前が既に定義済みだと ConstMissing 例外が発生せず autoload では読み込まれないので明示的に読み込む必要があるということらしい。

読み込みされていないクラスを使用すると ConstMissing という例外が発生します。 この部分に介入して autoload_paths の中に規約に合うファイルがあるか確認します。 存在する場合は読み込みします。 存在しない場合は ConstMissing を発生させます。

Rails の自動読み込みの話 - そんなこと覚えてない

ナルホディウスですぞ〜!!!

そのうち忘れそうなのでここに書き記しておきます。

| @技術/プログラミング

Fukuoka.rbで Ruby で連続するダブルクオートの扱いについて @nagachika さんに聞いたら面白い話が聞けた。

Lokka のテストで FactoryGril でフィクスチャーデータ作ってるところに、以下のような文字列があった。

  factory :post do
    association :user
    sequence(:title){|n| Test Post #{n}” }
    body <p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>”
    type ‘Post’
    created_at create_time
    updated_at update_time
  end

この body の部分で、ダブルクオートが連続して書いてある場所があって、ここがらみでテストを流してたら失敗する現象に遭遇した。テストに失敗したときのメッセージは以下。

Failures:

  1) Post markup default should == “<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”
     Failure/Error: it { post.body.should == post.raw_body }
       expected: “<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”
            got: “<p>Welcome to Lokka!</p><p><a href=\”/admin/\”>Admin login</a> (user / password : test / test)</p>” (using ==)
     # ./spec/unit/post_spec.rb:56:in `block (4 levels) in <top (required)>

なんか expect の方で HTML 内のダブルクオートが省略されてる。ダブルクオートが連続してるのが怪しいなと思って、文字列内でダブルクオートが連続したら、一つ目の はエスケープ文字列みたいな扱いになるのかなと思った。

しかし実はそうではなくて、 @nagachika さんの説明によると、ダブルクオートが連続した場合、一つ目の " で文字列の終端と判定される。すぐ右隣の " は新しい文字列の開始と見なされ、結果としては文字列として連結されるらしい(C 言語由来の慣習とのこと)。

たとえば、次のような文字列は

<p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>”

“<p>Welcome to Lokka!</p><p><a href=“”/admin/“”>Admin login</a> (user / password : test / test)</p>” という三つの文字列と見なされ、クオートとクオートの間に何も挟まらないので自動的に一つの文字列として連結され、以下のようになる。

<p>Welcome to Lokka!</p><p><a href=/admin/>Admin login</a> (user / password : test / test)</p>”

これ、なぜいままで Lokka でこの状態で CI のテスト通ってたのかわからないけど、今日 wercker で CI の設定していてテストが通らなくてこの問題に気がついた。同じように手元でテスト実行しても落ちた。ひょっとすると Ruby 2.1.0 で落ちるのかも知れない。いずれにせよ "" は使わない方が良さそうなので修正して Pull Request 出そう。

| @技術/プログラミング

このブログを Capistrano 3 でデプロイするようにした。こちらを参考にした。

一点、 deploy:restart 内で、 invoke メソッドで他の namespace の task を呼び出すところ

The deploy has failed with an error: #<NoMethodError: undefined method `verbosity' for "/usr/bin/env unicorn:restart\n":String>

というエラーが出てた。調べたらどうも sshkit のバグっぽかった。

最新版では Pull Request マージされてて治ってるぽかったので Gemfile で

gem 'sshkit', github: 'capistrano/sshkit'

と書いておいた。

Capistrano 3、他の gem いれなくても色付いたりマルチステージになってたり rbenv 対応しててモダンになってると思った。あとシンボリックリンク作ってくれる task が便利。

| @技術/プログラミング

@udzura さんがペーパーボーイ社福岡支部に移ってきたこともあって、約半年振りに Fukuoka.rb をやった。前来てたメンバーに加えて初めて参加した人もいた。今日は今後の活動方針なんかを決めた。毎週開催は負担が大きいので隔週へ、継続が大事なので本読みというよりルビー好きな人が集まっておしゃべりしてるような緩やかな会へ、というような提案があった。ルビーコミッターの @nagachika さんが定例を強く復活させたいとおっしゃっていて熱かった。 Asakusa.rb みたいな感じでやれたらいいなと思う。ただ初めて参加した人からは、場所が特定の会社の会議室だと参加しにくい、 AIP カフェなどのパブリックなスペースで、気軽に参加して発表を聞けるような場を設けてはという意見も出たので来年なんかやりましょうという話になった。

自分はこれまでの Facebook での開催告知とかコミュニケーションがあまり好きじゃなかったので、開催告知は Doorkeeper 、コミュニケーションは Lingr でやることになったのが良かった。

@chikanaga さん、 @udzura さんの二大著名ルビーストのおかげで盛り上がりそうな気配ある。第一期は自分の力不足でポシャってしまったけど、これから良いコミュニティになっていけば良いなと思う。

ということで、毎月第二第四木曜の19時から、グルーブノーツ社とペーパーボーイ社で交互に場所をホストしてやることになりましたので福岡市近辺のルビーストの皆さんはぜひお気軽にお越しください。本とか読んでないのでいきなり来ても大丈夫です。

| @技術/プログラミング

会社で使ってる GitHub のプライベートリポジトリで master ブランチに対して出てる Pull Request を Merge したらコードが消えるという珍事があった。ファイルを削除する commit とかないにもかかわらず、全消しされてしまった。ちなみに同じ Merge を手もとでやるとコードが消えたりはせずちゃんと Merge された。極めて謎な現象だった。

master ブランチが空になるとデプロイができなくなって不都合があるので( Webistrano 上でデプロイするとき master ブランチからしかデプロイできないようなレシピになってる)、コードが消滅したブランチを bukkowaremaster にリネームして手もとで Merge したブランチを force push してしのいだ。

GitHub に問い合わせてみたところ、ぬるい感じの一次返信が来たので原因教えて欲しいと送った。すると今度は別の人( Jeff King という人物)から返事が来た。名前でググったら git のコミッターだった。どうやら大事みたいだ。

We are still tracking down the exact cause of the problem you experienced, but we believe it is related to an unchecked error condition in git that triggers only occasionally (it does not reproduce reliably on your repository, and over all of our repositories over the past few years, yours is only the second instance we have seen).

In the meantime, we've put additional checks into the merge process to sanity-check the result. This should help us narrow down the root cause, but more importantly, it will prevent such a bogus merge from being presented to the user (we will catch the error and retry it).

I hope that addresses your concern.


(以下拙訳)

引き続き問題の原因を調査してるけど、これは git の unchecked error condition に起因する問題だと思っています。(再びあなたのリポジトリで同じ問題が起こることはないはずです。ここ数年間でこの問題に遭遇したのは GitHub の全リポジトリでもあなたのケースが 2 例目です)

一方で、 Merge のプロセスにはもっと厳密なチェックを追加するようにしました。このことで問題の原因を絞り込むことができますし、さらに大事なことに、今回のような空の Merge をユーザーの目にさらすことがなくなります(問題があれば我々が事前に察知し、 Merge を再実行します)

この対応であなたの懸念を解消できるのではないかと思います。

unchecked error condition ってのが何なのかがよく分からなくて、軽くググってみたら例外キャッチできなくて死ぬ現象で、セキュリティ上の懸念事項っぽい( CWE - CWE-391: Unchecked Error Condition (2.5))。

「 Pull Request を Merge したらコードが消えました」と聞かされたときは、冗談か何かかなと思ったけど、見てみたら本当に消えててびっくりした( git なので手もとにも同じコードあるからそんなに不安な感じはしなかった)。

GitHub 、問い合わせたらちゃんとまともな感じの返信が返ってくるのでその点は評価できる。今回も git の中の人みたいな感じの人から返信来たし、すでに追加的なチェック処理を行うようにしたと書いてあって対応速かった。 GitHub が git の問題直せる人を雇ってるというの、いいと思う。 OSS を使ってる会社が OSS の問題を直せる人を雇ってると強いなぁと感じた。

GitHub には今年の春の時点で 600 万リポジトリくらいあるらしいから( Five years )、 600 万リポジトリの中で行われた Pull Request のすべての Merge の中で 2 例目のレアな経験をしたということになる。なのでこれ読んでる人が同じ現象に遭遇することはまずないと思うけど、こういうことがありましたよというお話でした。

| @技術/プログラミング

公開している Lokka の DB (MySQL) のデータを、手もとの Lokka の DB (SQLite) に取り込むときに毎回やり方を忘れるので書いておきます。

今回は MySQL to Sqlite converter という gist のコメント欄に書いてある以下の方法を試した。

sudo gem install sequel
sudo gem install sqlite3
sudo gem install mysql
rbenv rehash
sequel mysql://user:password@host/database -C sqlite://db.sqlite

これでデータを MySQL から SQLite に変換できた。Lokka をローカルで起動してアクセスしてみるが記事が表示されない。DB の中にはちゃんとデータが入ってるし、管理画面から記事一覧を表示するとちゃんと記事が表示される。どうも Post.published[] を返すみたい。

手でクエリを投げてみると

sqlite> select * from entries order by created_at desc limit 1;
             id = 960
        user_id = 1
    category_id = 5
           slug = eye-pro-is-shit
          title = Eye-Fi Pro を買ったけどクソだった
           body = [hitode909さんの日記](http://hitode909.hatenablog.com/entry/2013/05/24/093749 "hitode909の日記")...
           type = Post
          draft = 0
     created_at = 2013-10-06 07:28:00.000000
     updated_at = 2013-10-06 13:11:23.000000
frozen_tag_list =
         markup = redcarpet

draft = 0 なので表示されそうなもんなんだけど、よくよく調べてみると SQLite には Boolean 型がなくて、DataMapper は SQLite に Boolean 型のデータを保存するときは t/f の文字列で保存するみたい。これは盲点だった。

結局 draft = 0draft = 'f' に変えてあげればすべての記事を表示できるようになった。

| @技術/プログラミング

最近 Emacs を触っていることもあってテキストエディター熱が高まっているので思いつきで社内で Vim 勉強会を開催した。

自分は最初はプラグインをいかに使いこなすかというところから Vim を使い始めて(Vim テクニックバイブル的 Vim の活用)、Emacs に手を出したことによる素の Vim の少ないキーストロークでの編集能力の高さ再発見(実践Vim的 Vim の活用)し、Vim の真骨頂は後者にあるのではないかとむさ苦しく語った。

準備不足で資料とか作れなかったのであまり意味わからない感じだったかも知れないけど、話をするために実践Vimを読んだり積ん読になっていたオライリーの Learning the vi and Vim Editors を読んだりして勉強のきっかけを作ることができて良かった。

自分の他に同僚の @monochromegane 氏が Unite.vim の上級活用法 (agとUnite.vimで快適高速grep環境を手に入れる - Thinking-megane)や、氏が EUC-JP/Shift-JIS 対応を進めている ag による Unite grep の高速化などについて話した。カーソル位置の単語で Unite grep する方法*1なんかが個人的には参考になった。Unite の設定とかはネットで拾った情報のコピペで済ませていたので、実際に目の前で解説してもらいながら使い方の設定をすることで新たな発見をすることができ、大変良かったと思う。

4月に勉強会がつらくなって Fukuoka.rb もやってなかったんだけど、またぼちぼち再開していきたいと思った。


*1 以下のようなコードだけどなぜか自分の環境だと期待した通りに動いてくれなくて、Pattern: というプロンプトが出て検索キーワードが確認できるはずなんだけど、何かキーを入力しないキーワードが表示されなくて不便な感じ。

" grep current word
nnoremap <silent> ,cg :<C-u>Unite grep:. -buffer-name=search-buffer<CR><C-r><C-w>