| @技術/プログラミング
[1] pry(2.0.0-p195)> require 'open-uri'
[2] pry(2.0.0-p195)> require 'active_support/core_ext/hash/conversions'
[3] pry(2.0.0-p195)> Hash.from_xml(open('http://www.portalshit.net/index.atom').read)
=> {"feed"=>
  {"xmlns"=>"http://www.w3.org/2005/Atom",
   "id"=>"http://www.portalshit.net/",
   "title"=>"portal shit!",
   "updated"=>"2013-06-18T19:07:44+09:00",
   "link"=>
    [{"type"=>"text/html",
      "rel"=>"alternate",
      "href"=>"http://www.portalshit.net/"},
     {"type"=>"application/atom+xml",
      "ref"=>"self",
      "href"=>"http://www.portalshit.net/index.atom"}],
   "entry"=>
    [{"id"=>"tag:www.portalshit.net,2013-06-18T19:04:33+09:00",
      "title"=>"Alfred で Chrome のブックマークを検索する",
      "published"=>"2013-06-18T19:04:33+09:00",
      "updated"=>"2013-06-18T19:07:44+09:00",
      "link"=>
       {"type"=>"html",
        "rel"=>"alternate",
        "href"=>
         "http://www.portalshit.net/2013/06/18/chrome-bookmark-search-with-alfred"},
      "content"=>
       "<p><img src=\"https://resources.portalshit.net/945-chrome-bookmark-search-with-alfred.png\" alt=\"945-chrome-bookmark-search-with-alfred.png (708×295)\
      "author"=>{"name"=>"morygonzalez"}},

で XML の内容を Hash にできる。ActiveSupport 便利。

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

昨日、こういう Pull Request が Lokka に来てた。

「あれ、Lokka の master ブランチは Ruby 2.0 対応してないんじゃ」と思って Ruby 2.0.0-p0 で試してみたところ案の定 json.gem のインストールに失敗する。しかしものは試しにと思って、最新のパッチレベルの 2.0.0-p195 をインストールしてみたら Ruby 2.0 で Lokka 起動できた。

というわけでポータルシットは Ruby 2.0 で動いております。

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

以前、Unicorn が暴走して困ってるという記事を書いていた。しかしよくよく調べてみると暴走してるのは Unicorn ではなく、Syntax Highlight に使っている Python の Pygments を呼び出している Ruby のプロセスだった(pygments.rb)。こいつが CPU をバカ食いしてしまい、アプリケーションのレスポンスがすこぶる悪くなっていた。

少し前、Pure Ruby の Pygments 互換 Syntax Highlighter で Rouge というのを発見していたので、Python を spawn するプロセスがなくなれば CPU バカ食いも止まるはず、と思って pygments.rb から移行してみた。しかし…

なんと逆にサイトが重くなってしまった。やはり Pure Ruby のプログラムは重いみたいである。pygments.rb は C のネイティブコードも含んでいて、速さがボトルネックになるところは C で処理しているみたい。

というわけで残念ながら EC2 のマイクロインスタンスで Rouge を常用するのは無理そうなので Pygments + pygments.rb に戻します。あとはキャッシュして誤魔化すしかなさそう。

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

Lokka で Syntax Highlight するプラグイン(morygonzalez/lokka-pygmentize · GitHub)、これまで Ajax で Syntax Highlight させてたんだけど、HTTP リクエストが増えていけてないと感じたのでサーバーサイドでハイライトが完結するように変更した。Railscasts の #272 Markdown with Redcarpet - RailsCasts を参考にして、プラグインの中で Entry クラスを再オープンした。こんな感じ。

class Entry
  def body
    doc  = Nokogiri::HTML(self.long_body)
    doc.search("//pre").each do |pre|
      code = pre.css("code")[0]
      pre.replace Pygments.highlight(
        code.text.rstrip,
        :lexer   => code[:class],
        :options => { :encoding => 'utf-8' }
      )
    end
    doc.to_s
  end
end

前の実装はだいぶいけてなかったと思うので随分マシになったと思う。あと この Pull Request でレンダリングエンジンに Redcarpet が追加されたので、GitHub と同じように

```ruby

```

みたいな感じの書き方でコードがハイライトされるようになった。便利。

追記1

ちゃんと動いてなかった…。夜直します…

追記2 2013/02/04 0:45

最終的にオープンした Entry クラスのコードは以下のようになった。

class Entry
  alias_method :original_long_body, :body
  def highlighted_long_body
    syntax_highlight(self.original_long_body)
  end
  alias_method :body, :highlighted_long_body

  alias_method :original_short_body, :short_body
  def highlighted_short_body
    syntax_highlight(self.original_short_body)
  end
  alias_method :short_body, :highlighted_short_body

  def syntax_highlight(body)
    doc = Nokogiri::HTML(body)
    doc.search("//pre").each do |pre|
      code  = pre.css("code")[0]
      lexer = if pre[:class].present?
                pre[:class]
              elsif code.present? && code[:class].present?
                code[:class]
              else
                nil
              end
      begin
        pre.replace Pygments.highlight(
          code.text.rstrip,
          :lexer   => lexer,
          :options => { :encoding => 'utf-8' }
        ) if code
      rescue MentosError
        next
      end
    end
    doc.to_s
  end
end

Lokka、結構メタプログラミングが多くて、Entry クラスのインスタンスの body メソッドは単なるゲッターではなく、 index アクションのときと show アクションのときで別々にエイリアスが設定されていて、index アクションのときは Entry#short_body 、show アクションのときは Entry#long_body が呼ばれるようになっていた。アラウンドエイリアス使って力業で解決したけど他のプラグインが同じように振る舞ったら破滅を招きそうな気がする…。それにしても『メタプログラミング Ruby』読んでなかったらどうすればいいか皆目検討付かなかっただろうなー。

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

自戒エントリー。

自分は Mongoid を使って開発してるんですけど、時間の境界値のテストが不十分で問題に遭遇したのでメモっておきます。

Mongoid には Date 型や DateTime 型があるけど、データベースにデータを保存するときには Time 型に変換されます(MongoDB に Date とか DateTime がないから?)。Mongo Shell で DB の中の値を見ると Time 型の値が入ってる。

だからクエリを生成するときに Date.today はなるべく使わない方がいい。Date.today では時間を 00:00:00 として扱い、月末のデータの取得に失敗する可能性が高いから。Rails console で確認すると、以下のようになります。

pry(1.9.3-p194)> 1.month.ago.end_of_month
# => Fri, 31 Aug 2012 23:59:59 JST +09:00
pry(1.9.3-p194)> Date.today.prev_month.end_of_month.to_time
# => 2012-08-31 00:00:00 +0900

上記は二つとも「先月末の月の終わり」を検索していますが、後者では時間が 00:00:00 になっています。例えば以下のような Mongoid でのクエリでは、8月31日の午前0時0分1秒以降に作成された Document を取得することが出来ません。ナンテコッタイ!!!

Model.where(:created_at.lte => Date.today.prev_month.end_of_month)

Date.today を時刻の生成の起点にしたとしても、to_time してあげればいいのでは?」と思う方もいるでしょう。確認してみましょう。

pry(1.9.3-p194)> Date.today.prev_month.end_of_month.to_datetime
# => Fri, 31 Aug 2012 00:00:00 +0000

#prev_month メソッドが呼び出された時点のオブジェクトの型は Date 型なので、Date 型に対して時間の操作を行って最後に DateTime 型に

結論

時刻まで考慮した時間の範囲でクエリを作るときは以下に留意すると良いでしょう。

  • Date.today は使わない
  • DateTime.now1.month.ago などを使う

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

ここにはっつけるコードのシンタックスハイライトには Google Code Prettify をこれまで使ってたんですけど、どうもいまいちでした。有名な SyntaxHighlighter も好きになれなかった。はてなブログのように綺麗なシンタックスハイライトさせたい! ということで作った。

かなりシャレオツな感じにシンタックスハイライトできるようになったと思います。拾ってきた Monokai スタイルの CSS を当てています。

動作環境

裏側で使っているのは Python の Pygments。JavaScript オンリーで色付けするやつよりもこいつの方が圧倒的に綺麗でした。なのでこのプラグインを使うには Python と Pygments が必要です。Heroku では動くんでしょうか。動作未確認です。

使い方

HTML の pre タグのクラス名を lexer として Pygments に渡します。Ruby のコードを書くのであれば以下のようにします。

<pre class="ruby">
  <code>
  class Book
    def off
      "all your book is 10 yen"
    end
  end
  </code>
</pre>

やってること

JavaScript で pre タグを探してサーバーにコードの中身と pre タグのクラス名を投げると、Pygmentize された HTML が返ってくるようになっております。そいつを JavaScript で拾って pre タグの中身を入れ替え。

当初は

```ruby
class Book
  def off
    "all your book is 10 yen"
  end
end
```

みたいな感じで GitHub 風にしたいと思っていて、以下のような JavaScript を書いていたんですが JavaScript 力が低すぎて断念。

$('.body').each(function() {

  var Pygmentize = function(lexar, snippet) {
    var result;

    $.ajax({
      type: 'POST',
      url: '/pygmentize',
      async: false,
      data: {
        lexar: lexar,
        snippet: snippet
      },
    }).done(function(data) {
      result = data;
    });

    return result;
  }

  var entryBody = $(this).text();

  entryBody = entryBody.replace(/```(.+?) ([sS]*?)```/g,
    function(whole, lexar, snippet) {
      return Pygmentize(lexar, snippet);
    }
  );

  $(this).html(entryBody);
})

※Ajax なのに async: false とかしててイマイチ感ありますね。

とはいえ、汎用性の高い pre タグのクラス名を拾ってくる、という形での実装にしたので、 Markdown でなくても HTML でも Textile でもハイライトできるので結果オーライとします。

ちなみに Kramdown でクラス名を指定したいときは以下のようにするみたいです。

> hogehoge
{: .hoge }

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

RSense という Ruby のプログラムを書いているときに、レシーバの型に応じた補完候補を表示してくれるソフトがあります。Emacs とか Vim と組み合わせて使うと便利らしいです。Java で IDE 使って開発すると補完候補がわさわさ出てきて殆ど鼻くそほじってるだけでプログラミングできるという話を聞いたので、Ruby でも鼻くそほじりながらプログラミングしたいなと思ってこいつを導入してみることにしました。春頃やったときはなかなかうまく Vim から使うことが出来なくて諦めてたんだけど 、つい最近できるようになったのでやり方をメモっておきます。

Mac でのお話です

前提条件ですが、Mac で使ってます。環境は Homebrew で構築してます。また RSense を使うには Java Runtime Environment が必要です。あなたと Java

RSense のインストール

JRE のインストールは済んでいるものとします。Homebre で RSense をインストールしましょう。簡単です。

brew install rsense

インストールが済んだら以下のようなメッセージが表示されると思うので、指示に従いましょう。

If this is your first install, create default config file:
    ruby /usr/local/Cellar/rsense/0.3/libexec/etc/config.rb > ~/.rsense

すると ~/.rsense というファイルが作られ、中身は以下のようになっています。

home = /usr/local/Cellar/rsense/0.3/libexec
load-path = /Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby/1.9.1/x86_64-darwin11.4.0:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/site_ruby:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby/1.9.1/x86_64-darwin11.4.0:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/vendor_ruby:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1:/Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/x86_64-darwin11.4.0
gem-path = /Users/morygonzalez/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1:/Users/morygonzalez/.gem/ruby/1.9.1

rbenv 使ってる人は load-path にちゃんと rbenv のパスが含まれているか確認して下さい。

Vim の設定

このままではまだ RSense がインストールされただけで、Vim から利用することができません。Homebrew で入れた RSense には rsense.vim がついてくるので、こいつを Vim の plugin ディレクトリにコピーします。

cp /usr/local/Cellar/rsense/0.3/libexec/etc/rsense.vim ~/.vim/plugin/

次に .vimrc で rsenseHome を指定しなければなりません。

let g:rsenseHome = "RSense home"

と書きます。僕はこの rsenseHome が分からなくてハマりました。先ほどの config.rb を実行したときに生成された ~/.rsense に書いてある home をここに指定します。なので rsenseHome は /usr/local/Cellar/rsense/0.3/libexec です。

ここまで済んだところで Vim から RSense が認識されているか確かめます。適当に Vim を起動して :se ft=ruby とし、:RSenseVersion とコマンドを打ってみます。ここで RSense 0.3 のように成功されたら設定完了です。filetype が ruby になっているファイルで 1. と打った後、補完候補を呼び出すコマンド(^X ^U)で候補を呼び出せます。以下のような感じ。

8da56d016a74ebc9d38afcf593bfeadd.png (200×116)

“ネオコン” と連携させましょう

さらに言うと、たいていの Vim ユーザーの皆さんは neocomplcache も使ってるでしょうから、neocomplcache と RSense を連携させましょう。ネオコン作者の Shougo さんのブログ記事を参考にして下さい。

以上で完了です。これで Vim で Ruby な皆さんも鼻くそほじりながらプログラミングできますね。