Jekyll は軽快なブログソフトなので乗り換えてみた

今まで EY-Officeホームページ・ブログRadiant を使って作成していたが、Jekyll に変更しました。

Radiantは高機能なのですが、Webアプリのため原稿を書き確認するというサイクルが遅いとか、文章を入力するtextareaは貧弱などの問題がありました。
また、EY-Officeホームページ・ブログはradiantからスタティックなページを作成し、サーバーに転送していましたが、これも一手間でした。

Jekyll とは?

Jekyll は ローカル(自分のMac,PC)で動くプログラマー向けのCMS、Blog作成ソフトです。

  • ページの原稿は Mac上のエディターで書きファイルに保存します
  • jekyllコマンドは、原稿からスタティックな html ファイルが生成します
  • また、jekyll コマンドは確認用サーバーにもなり、Mac上で生成されたページが確認できます
  • 原稿用のマークアップ言語としては標準で Markdown, Textile, html が使えます
  • 一般的なCMS、Blogソフト同様にレイアウト(テーマ)や生成されるページの共通部分をまとめる機能があります
  • ページ生成に便利なLiquidテンプレート言語が使えます
  • ブログ用の機能も持っています
  • 原稿が変更されると、確認サーバーは自動的に html ファイルを更新します(設定によっては)
  • 原稿ファイルの管理は git 、コマンドの自動化は rake などの既存ツールを使います
  • http://pages.github.com/ で使われます

Jekyll の使い方

インストール

既にRubyがインストールされていれば

% gem install jekyll
% gem install RedCloth

だけです。 マークアップに Markdown を使うなら gem install RedCloth は不要です

Jekyll のドキュメント

Jekyllのドキュメントは https://github.com/mojombo/jekyll/wiki にありますが、構成が良くなく以前みたドキュメントが探せなくて困ったので一覧にしておきました。

作業環境(ディレクトリー)の作成

https://github.com/mojombo/jekyll/wiki/usage にあるようなディレクトリーを準備します。_ から始まるディレクトリー、ファイルが Jekyll 用です。
画像やCSS用ディレクトリーは適当つくれば良いです。
https://github.com/mojombo/jekyll/wiki/sites に Jekyll を使ってるサイトとそのソース(たいていgithub)が公開されているので、いくつかの ソースを clone/ダウンロードして眺めてみるとをお勧めします。

私の環境は以下のようになってます

ekyll-ey-office/
├── Gemfile
├── _config.yml   ← 設定ファイル
├── _includes     ← 共通ファイル
│   ├── footer.html
│   ├── google_analytics.html
│   ├── header.html
│   └── menu_bar.html
├── _layouts       ← レイアウトファイル
│   ├── blog.html
│   ├── default.html
│   └── home.html
├── _plugins       ← 独自プラグイン
│   └── menu_image.rb
├── _posts         ← ブログ原稿ファイル
│   ├── 2009-08-31-type-of-cloud.html.textile
         ...
│   ├── 2012-06-11-is-mac-expensive.html.textile
│   └── 2012-06-18-paas-service.textile
├── _site          ← 生成されたhtml(css,image)が入るディレクトリー
│   └──
├── atom.xml         ↓ ここからは原稿ファイル
├── blog.textile
├── blog_archive
│   └── index.textile
├── company.textile
├── company_development.textile
├── css
│   └── base.css
├── education.textile
     ....
├── images
│   ├── ey_logo3.jpg
│   ├── flower2.jpg
        ....
│   └── xcode.png
├── index.textile
├── news
│   ├── index.textile
         ...
│   └── news-2009.textile
└── robots.txt
コマンドの起動

設定ファイルを書き、原稿を準備し(次のセクションで説明します) jekyll を実行すると

% jekyll 
Configuration from /Users/yy/Documents/jekyll-ey-office/_config.yml
Auto-regenerating enabled: /Users/yy/Documents/jekyll-ey-office -> /Users/yy/Documents/jekyll-ey-office/_site
[2012-06-20 17:04:20] regeneration: 91 files changed
[2012-06-20 17:04:20] INFO  WEBrick 1.3.1
[2012-06-20 17:04:20] INFO  ruby 1.9.3 (2012-04-20) [x86_64-darwin11.3.0]
[2012-06-20 17:04:20] INFO  WEBrick::HTTPServer#start: pid=2698 port=4000
[2012-06-20 17:04:47] regeneration: 1 files changed

のように、原稿からhtmlへの変換され、確認用サーバーが起動されます。自動更新にしておくと原稿が変更されると自動的にhtmlに変換されるので ブラウザーをリロードするだけで、最新のコンテンツを確認できます。

確認サーバーの終了は ^C 等で行ってください。

EY-Officeホームページ・ブログの設定例

設定ファイル _config.yml

server: true でjekyll コマンド実行後、http://localhost:4000 に確認用サーバーが立ち上がります。また auto: true なので原稿が更新されると対応するhtmlが自動的に更新されます。
設定ファイルに書かれた内容でテンプレートから参照出来るので、ここに書いておいきレイアウトファイル等では {{ site.author.name }} のように参照すると良いと思います。

permalink: /blog_archive/:year/:month/:day/:title   ← ブログのURL(ディレクトリー)構造

exclude: [".rvmrc", "Gemfile", "Rakefile", ".gitignore"]  ← 公開しないファイル

sitename:  EY-Office                                         ← レイアウト、RSS等で使う設定値
production_url : http://www.ey-office.com
title : EY-Office
author :
  name : Yuumi Yoshida
  email : yy@ey-office.coms

auto: true             ←  自動更新
pygments: false    ← コードハイライト
server: true           ← 確認用サーバー起動
server_port: 4000 ← 確認用サーバーのポート番号

詳細は https://github.com/mojombo/jekyll/wiki/configuration

原稿ファイル index.textile

原稿は TextileやMarkdown のようなマークアップ言語で書きますが、htmlを書く事も出来ます。マークアップ言語の区別は拡張子で行っているようで Textileは .textile、Markdownは .md です。
先頭の --- に挟まれた部分はレイアウトの選択やカテゴリーなどを指定します。この情報もレイアウトファイル等から参照できます。( 詳細は https://github.com/mojombo/jekyll/wiki/yaml-front-matter )
また、レイアウトのところで説明する、 Liquidテンプレート言語も使えます。

---
layout: home
---
h1. お知らせ

* ブログを更新しました "(detail_link){{ site.posts.first.title }}":{{ site.posts.first.url }}
* Facbookページを始めました "http://www.facebook.com/ey.office.jp":http://www.facebook.com/ey.office.jp です。

h2. 企業向けソフト開発のプライベートレッスン

EY-Officeの教育は画一的な集合教育ではなく、御社の開発プランや開発者のスキルに合わせた教育を御社に出向き実施します。 "(detail_link)詳細はこちら":/education.html

!/images/edu_flow.jpg(EY-Officeの教育)!
     ...
ブログ原稿ファイル 2012-06-11-is-mac-expensive.html.textile

ブログ原稿はブログ独自の処理が加わるので _post ディレクトリーに置きます。
ブログの原稿はファイル名に日付やURLになるファイル名を付けます (詳細は https://github.com/mojombo/jekyll/wiki/permalinks )
また先頭部分の title: にタイトルを書きます。

---
layout: blog
title:  Macは本当に高いのか?
---
iPhone(iOS)アプリの開発にはMacが必要になります、Ruby on Railsの開発にもMacは最適です。しかし世の中ではMacは高いというイメージがあり導入をためらっている方もいるかと思います。今回は本当にMacはWindowsPCより高いの検証してみたいと思います。

h3. 同じスペックのマシンで比較してみよう

本日(2012年6月10日)の時点で Apple Storeを見てみると MacbookAirの一番安い機種は¥84,000です。DELLのホームページを見ると一番安いノートPCは ¥44,980 です。

Macは高い・・・?  
    ….
レイアウト default.html

レイアウトは html で書きます。 {{ … }} や {% … %} の部分は Liquid というテンプレート言語で、値の埋め込み、ファイルインクルード、判断、繰り返しなが出来ます。(Liquidの詳細は https://github.com/shopify/liquid/wiki/liquid-for-designers 、参照出来る値は https://github.com/mojombo/jekyll/wiki/template-data )
{{ content }} の部分に原稿がhtmlに変換された内容が入ります。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
{% include header.html %}
<body>
  <div class="all_area">
    <div class="header">
      {% include menu_bar.html %}
    </div>
    <div class="main_area">
      <div class="side">
        <img src="/images/ey_logo3.jpg" alt="EY-Office">
        {% if page.left_link != '' %}
        <div class="links">
          {{ page.left_link }}
        </div>        
        {% endif %}
      </div>
      <div class="contents">
        {{ content }}
      </div>
    </div>
    {% include footer.html %}
  </div>
  {% include google_analytics.html %}
</body>
</html>
共通ファイル header.html

複数のレイアウト等の共通部分をここに置き、インクルードして使います。このファイルでも Liquidテンプレート言語が使えます。

<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="description" content="EY-Officeの教育は画一的な集合教育ではなく、御社の開発プランや開発者のスキルに合わせた教育を御社に出向き実施します。">
<meta name="keyword" content="iPhone,アプリ内課金, In App Purchase, ソフトウェア,教育,講習,トレーニング,OJT,Ruby,Ruby on Rails,Rails,Lisp,scheme,アジャイル">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">

{% if page.layout == 'blog' %}
<meta property="og:title" content="{{ page.title }}" />
<meta property="og:type" content="blog" />
<meta property="og:url" content="{{ site.production_url }}{{ page.url }}" />
<meta property="og:site_name" content="EY-Office homepage" />
<meta property="fb:app_id" content="133002700083354" />
{% endif %}

<title>企業向けiPhone、Ruby on Rails開発のプライベートレッスンならEY-Office</title>
<link href="/css/base.css" rel="stylesheet" type="text/css">
<link href="/rss" rel="alternate" title="EY-Office RSSフィード" type="application/rss+xml">
<link href="http://feeds.feedburner.com/ey-office" rel="alternate" title="EY-Office ブログ" type="application/atom+xml" />

</head>
Liquid 応用編

下はブログの一覧表示ページの原稿です。 site.posts に全てのブログエントリーが入っていますので、 Liquidテンプレート言語を使い、そこから最新10件を取り出して、タイトル、リンクと記事の先頭160文字を付けた一覧を生成します。

---
layout: default
---
h1. EY-Office ブログ 

<div class="essay">

div(header). EY-Office取締役 吉田裕美が、まだ Ruby on RailsやiPhone開発を行ってない技術者や技術系マネージャー向けに、技術や教育に付いて書いています。

h1. ブログ一覧

{% for post in site.posts limit:10 %}

  <div class="blog_list">
  	<h4><span class="date">{{ post.date | date: "%Y-%m-%d" }}</span> <a href="{{ post.url }}">{{ post.title }}</a></h4>
  	<div>{{ post.content | strip_html | strip_newlines | truncate: 160 }} </div>
  </div>

{% endfor %}
</div>
プラグイン menu_image.rb

Liquidテンプレート言語である程度の処理は書けますが、出来ないこともたくさんあります、そんな時はプラグインを作ります。詳細は https://github.com/mojombo/jekyll/wiki/Plugins に書かれていますが、プラグインには

  1. Generators: 原稿からhtmlに変換するさいにページの自動生成などを行う
  2. Converters: Markdown,Textile に代わるようなマークアップ言語を組み込む
  3. Tags: Liquidテンプレート言語に独自のタグを追加する
  4. Liquid filters: Liquidテンプレート言語に独自のフィルター(関数)を追加する

などが容易されています。プラグインのコードは _plugins ディレクトリーに置くだけで動きます (確認用サーバーは再起動の必要があります)

EY-Officeのホームページでは 以下の画像のように選択されてるタブのマーク画像が変わりますが、その切り替えを行う Tags を書きました。


module Jekyll
  class MenuImageTag < Liquid::Tag
    Syntax = /(.*)\s*,\s*(.*)\s*/o
  
    def initialize(tag_name, markup, tokens)          
      if markup =~ Syntax
        @regexp = Regexp.new($1)
        @url = Liquid::Variable.new($2)
      else
        raise SyntaxError.new("Syntax Error in 'menu_image' - Valid syntax: menu_image [regexp], [url]")
      end
      super      
    end
  
    def render(context)
      if @regexp =~ @url.render(context)
        '<img src="/images/menu_sel.gif" alt="*">'
      else
       	'<img src="/images/menu_unsel.gif" alt="*">'
      end
    end 
  end
end

Liquid::Template.register_tag('menu_image', Jekyll::MenuImageTag)
  • 呼び出し側
<table class="menubar">
  <tr>
    <td>
      {% menu_image ^/index.html, page.url %}
      <a href="/">Home</a>
    </td>
    <td>
      {% menu_image ^/news, page.url %}
      <a href="/news/">ニュース</a>
    </td>
    <td>
      {% menu_image ^/company, page.url %}
      <a href="/company.html">会社案内</a>
    </td>
    <td>
    ....

独自タグの作り方の詳細のドキュメントはありませんので、Liquid テンプレート コード https://github.com/Shopify/liquid を読んで学んで下さい ^^)


6/21日 Jekill を使う人に見つかるように、タイトルを換えました。


今日の結論: Jekyll は便利。ドキュメントが無くてもコードを読めばわかる

MacでRuby on Railsを始めるには RailsInstaller !! Ruby,Rails,Git... が1クリックでインストール完了

Mac OS X には最初から RubyRuby on Railsが入っていますがバージョンが古く現実的ではありません。最新の Ruby on Railsを使うには、 Xcode入れて、 rvm 入れて、 Ruby 入れて、Railsを入れて、MySQLとかをインストールするために Homebrew いれて 。。。 とかなりたへんです でした。 しかし RailsInstaller が出来たのでワンクリックでインストールが完了します。

VMWareFusion 4

私のMacには既に Xcode,rvm,Ruby,Rails,Homebrew, ... が入っているし、この環境を消してまでRailsInstallerのテストは行えません。そんな時には VMWareFusion 4 です。VMWareFusion 4ではWindowsLinux以外に Mac OS X Lion を動かす事ができるのです! インストール実験にはもってこいの環境です。

下の画像は VMWareFusion 4 の上で Mac OS X Lion を動かしている Mac OS X Lionの画面です。Macの中にMacがあるのがわかりますか?

インストール

http://railsinstaller.org/ をアクセスし RailsInstaller をダウンロードし、インストールします。通常のMacアプリと同じです。とくに説明はいりませんが http://railsinstaller.org/ にあるビデオを見ると手順がわかります。

インストール前は ruby 1.8.7 がインストールされています、git や gcc は入っていません。

chappy-vm:~ yy$ ruby --version
ruby 1.8.7 (2011-12-28 patchlevel 357) [universal-darwin11.0]
chappy-vm:~ yy$ git --version
-bash: git: command not found
chappy-vm:~ yy$ gcc --version
-bash: gcc: command not found

インストール後に、再度ターミナル(Shell)を起動すると、Ruby 1.9.3, Rails などがインストールされています。

chappy-vm:~ yy$ ruby --version
ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]
chappy-vm:~ yy$ git --version
git version 1.7.4.4
chappy-vm:~ yy$ gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)
...
chappy-vm:RailsWorks yy$ rails --version
Rails 3.2.2

早速 Todo アプリを作ってみましょう

chappy-vm:RailsWorks yy$ rails new todo_app
      create  
      create  README.rdoc
      create  Rakefile
      ....
         run  bundle install
Fetching gem metadata from https://rubygems.org/.........
Installing rake (0.9.2.2) 
Using i18n (0.6.0) 
Installing multi_json (1.3.6) 
    ...
chappy-vm:RailsWorks yy$ cd todo_app/
chappy-vm:todo_app yy$ rails g scaffold todo due:date task:string
      invoke  active_record
      create    db/migrate/20120603031721_create_todos.rb
      ...
chappy-vm:todo_app yy$ rake db:migrate
==  CreateTodos: migrating ==========================================
-- create_table(:todos)
   -> 0.0011s
==  CreateTodos: migrated (0.0017s) =================================

chappy-vm:todo_app yy$ rails s
=> Booting WEBrick
=> Rails 3.2.2 application starting in development on http://0.0.0.0:3000
...

Todo アプリが出来ました ^^)/

ちなみに RailsInstaller は rvm を使っているので、慣れてくれば 複数のバージョンの Ruby複数のバージョンの Railsを1つの Mac にインストールして使う事ができます。

Jewelrybox

今回、初めて知ったのですが RailsInstaller には Jewelrybox というアプリが含まれています。
これは、 rvm をGUIベース管理できるツールです。GUIが好きな人はインストールしてみると良いと思います。


今日のまとめ: Mac買ったらすぐにいれるべきソフト RailsInstaller

Active Merchant で PayPal Sandbox を使うには

Ruby on Railsで作ったWebサービスからPayPalや海外の決済サービスを使う場合、そのサービス固有のAPIを使わなくても Active Merchant を使うと、簡単に信頼性の高い決済を組み込めます。

Active Merchant の使い方は 以下の3つのRailsCastを見るのが一番良いと思います。

RailsCastを見て、サンプルコードを動かすのは簡単です、またサンプルは Rails2.2.X用ですが Rails 3.0.X でも少しの修正で動かせました。
ところが、テスト用に PayPal を使おうとしたのですが、これがわかりにくい !!
たんに英語力が低いだけかもしれませんが、苦労した結果をまとめておきました。

PayPal sandbox にテスト用にアカウント作成方法

まず、PayPalの決済には利用者側でクレジットカード番号等を入力し決済を呼び出す PayPal Payments Standard と カード番号等は PayPalのページで入力する Express Checkout の2つがりあすが、今回は PayPal Payments Standard の話です。

1. PayPal sandbox に登録する

PayPal sandbox を使うための登録を行います、ここで登録する e-mail アドレスは受信出来るアドレスで、かつPayPalを使うために登録してあるアドレスとは別のアドレスで登録して下さい。
PayPal Sandbox - Log In


2. 決済のテストで使うアカウントを作成する

決済をテストするためには2種類のアカウントが必要です

  • お金を受け取る(ネットショップ など)側の Bussiness アカウント
  • お金を払う(お客様など)側の Personal アカウント

Test Accountページの New test account: Preconfigured をクリックしてアカウントを作って下さい。Create Manually を使うと日本語名のアカウントが作れますが、私が試した範囲では上手く決済できませんでした。

Preconfiguredをクリックすると下のような画面が表示されます、ここで重要な事は

  • Country: を United States にする
  • Account Typeは
    • ネットショップ 側は Seller
    • お客様側は Buyer
  • First name, Last name はお客様側では入力しておくと良いと思います
  • メールアドレスは適当なアドレスが設定されています。実際にメールを受信する必要はないのでデフォルトで良いと思います。
  • Passwordは適当な値が入っていますが、自分の覚えやすいものに替えた方が良いとおもいます

後はそのままで Create Account して下さい。


下の画像のように、Bussiness, Personal 2つのアカウントを作成します。View Details で表示される情報の中に テストで使える クレジットカード番号があるので Personalアカウントのカード番号をメモしておくと良いと思います。
また、左側の Test Emailメニューでこのアカウントのメールアドレスに送られてきたメールを読めます。


3. API用の情報を確認する

左側の API and Payment Card Credentials メニューをクリックすると APIに設定する Account,Password, Signature が表示されるので Active Metchant のgatewayに設定して下さい。

RailsCast#145のサンプルコードを動かし、Personalアカウントのカード番号を入力すると決済が出来ます。

4. 決済情報の確認

Businessアカウントを選択し、Enter Sandbox Test Site ボタンをクリックし、Businessアカウントのメールアドレス(入力済みです)、パスワードを入力すると下の画像のように、カード決済の情報が確認できます。



以上です、何度も失敗してやっと辿り着けました ^^);
今日の結論「英語力重要

Rails2.3アプリをRails3.2にアップグレードする、その3

少し間が開きましたが、今回はメール、Cucumber、Javascriptまわりです

メール送信

ActionMailer

メールは Rails3で ActionMailerで仕様が以下のように少し変わりました。またActionMailerを継承したクラスは app/mailers に置くようになりました。

仕様の変更は機械的に変更できるものばかりですね。

class CustomerMail < ActionMailer::Base
  helper :application
  
  # パスワード再設定用メールの送信
  def password_reset(to_addr, url, timeout, name)
    subject    "パスワードの再設定"
    recipients to_addr
    from       FROM_ADDRESS
    body       :url=>url, :timeout=>timeout, :name=>name
  end

  ・・・
class CustomerMail < ActionMailer::Base
  helper :Application
  
  # パスワード再設定用メールの送信
  def password_reset(to_addr, url, timeout, name)
    @url = url
    @timeout = timeout
    @name = name
    mail(
      :subject  =>  "パスワードの再設定",
      :to => to_addr,
      :from => FROM_ADDRESS)
  end

  ・・・
テンプレート (view)

メールのテンプレート (view)はほぼそのまま使えます。ただし < & > 等がエスケープされますので、raw を追加する必要がある部分もあるかもしれません。

日本語

あいかわらず、日本語メールが送れません ^^);
mail-iso-2022-jp という gem をインストールすると ISO-2022-JP のメールが送信できます。 ただし、この場合は UTF-8からJISコードに変換出来ない文字でエラーが発生するので、何らかの対応を考えておく必要があります。

最近はPCはもちろん、携帯でもUTF-8のメールを受信出来るようなので、UTF-8で送信すれば上の文字コードの問題は発生しません(一部のメーラー、携帯では文字化けするかも知れませんが・・・)。
私が Rails3.0以降に作ったシステムでは UTF-8で送信していますが、今のところ問題は無いようなので、今回もUTF-8で送信する事にしました。
Rails3で送信するメールの本文をbase64ではなく8bitにする方法 - 思っているよりもずっとずっと人生は短い。 を参考に、 Mail::Message deliver_jp メソッドを追加して使っています(もちろん deliverを置き換えても良いのですが・・・)。

module Mail
  class Message
    def deliver_jp
      self.tap {|m| m.transport_encoding = '8bit'}.deliver
    end
  end
end

Cucumber

このアプリでは受け入れ(統合?)テストを Cucumber で書きました。 その時点ではCucumber 0.8.3 で Webrat を使っていました。 今回は Cucumber 1.1.9 + Capybara-webkit です。

設定や基本的なstep ファイル

設定や web_steps.rb 、 web_steps_ja.rb は空のRailsプロジェクトにCucumberをインストールして出来た物をベースに必要な部分のみ Rails2.3からコピーしました。

step の中で 他のステップを利用する場合は step と書くように変わったんですね

  • Cucumber 0.8.3
When /^"([^\"]*)"ボタンをクリックする$/ do |button|
  When %Q(I press "#{button}")
end
  • Cucumber 1.1.9
もし /^"([^"]*)"ボタンをクリックする$/ do |button|
  step %{I press "#{button}"}
end
シナリオ(フィーチャー)

Cucumberが正常に動くようにるにのに手間がかかりましたが、ほとんどのシナリオはそのまま通りました。

問題があったのは

  • 画像をボタンのように使っている場合
<a href="action"><img src="a_button.png" title="Aボタン"/></a> 

上のHTMLは もし "Aボタン"リンクをクリックする と書けたのですが、Captbaraは画像のtitleを見てくれません。ここは仕方なく もし "a_button"リンクをクリックする と変更しました。(a タグにtitleを付けたりしたのですがダメでした・・・・)

  • エラーメッセージ

Rails2.3ではvalidationのエラーメッセージは 名前 を入力して下さい のように入力項目とエラーメッセージの間にスペースがあったのですがRails3.2では無くなっていましたので修正しました。

  • visit のオプション引数

Webrat では visitのオプション引数でパラメターやメソッド(POST,PUT...)を指定できたので Javascript でリンクからPUTを送る部分は、これを使ってテストしていましたがCapybaraではvisitのオプション引数はありませんでした。
しかし、今回はJavascriptが動作するCapybara-webkit なので step はリンクのクリックを書くだけで正しくテストが行えました。素晴らしい!

Javascript関連

  • :method => :delete のリンク

: method => :delete のリンクが正しく動作しませんでした。 じつはRails2.3の時点で jquery を使っていたので jquery.js ファイルもそのまま使っていましたが、バージョンが1.3.2 と古く、これが原因でした! Rails3.2の jquery.js を使うようにしたら動きました。

  • :method => :delete のリンクをクリックすると Can't verify CSRF token authenticity が発生

ログアウトリンクをクリックすると、セッションが消えてしまう(新しくなってしまう)という不思議な現象が発生していました。ログを良くみると WARNING: Can't verify CSRF token authenticity と出ていいます。たしかに authenticity_tokenが来ていません・・・・・ ここで、Rails3のlayoutテンプレートのの最後に <%= csrf_meta_tags %> が在ることを以前から不思議に感じていた事を思い出し、layoutテンプレートに加えたところ、正しく動作するようになりました ^^)/


Rails2.3からRails3.2へのポーティングはかなり完成に近づいて来ました。

バラバラのバージョンのRuby on Railsを1つのサーバーで動かす

現在、EY-Officeの社内利用サーバーではではいろいろなバージョンのRuby on Rails を2台で管理しています。ただし全てがほぼ私ししか使っていないので、さくらVPSのキャンペーン につられて1つのサーバーにまとめてみました。


http://www.modrails.com/phusion_template/logo-trans.png
http://github.com/images/error/angry_unicorn.png

移行前

さくら VPS 512Mをつかって以下のアプリを運用していました (1.8.7@rails2.3 という記法は rvmで使う記法で Ruby 1.8.7Rails 2.3.X を使っているという事です)

  • サーバー1
    • redmine : 1.8.7@独自、教育で使ったりするので時々負荷が高くなる
    • radiant : 1.8.7@独自、EY-Officeホームページ作成用、スタティックページを生成して使うので超低負荷
    • Ruby on Rails以外: gitリポジトリー、wliki ( Gaucheで書かれたWiki )
  • サーバー2
    • A社ステージング : 1.8.7@rails2.3 、低負荷
    • B社ステージング : 1.8.7@rails2.3、 お客様側の開発者もたまに使うが、低負荷
    • C社テスト用 : 1.8.7@rails3.0、 低負荷

さらに、A社サービスを 1.9.3@rails3.2 に移行中でステージングサーバーを必要としています。

方針

今までは、Ruby on Railsの実行環境は Apache + Passenger を使っていました。PassengerはApacheのモジュールとして動くのでアプリの設定やデプロイが簡単ですし、1つのサーバーで複数のアプリを動かすのも簡単です。

開発環境(Macbook Pro)はrvm を使って複数のバージョンの Ruby/Railsの開発が出来ます、rvm をサーバー上で使えば同じように複数のバージョンの Ruby/Railsの運用が出来るのでは? と考え調べてみました。

Passenger を rvm の上で動かす事は rvmのページ に書かれているように簡単にできます。 しかし、Passenger で動くアプリで複数Rubyを使う事は出来ません! Passengerのblogに 複数のバージョンのRubyをPassengerで動かす方法というエントリーがありますが、図のように 異なるRubyを使うアプリは Passengerをスタンドアロンで起動して使うと書かれています・・・・・ これでは、手軽に使える Passengerの良さが失われてしまいます ^^);

http://blog.phusion.nl/wp-content/uploads/2010/09/setup.png


Apacheを reverse proxyにしてアプリケーションサーバーを別に起動するなら、Passenger以外に Unicornという選択肢もあります。いろいろと調べた結果

  • 別プロセスを起動する手間はあるが、性能を考えなければ設定は簡単
  • rvm の wrappers 機能を使うと 特定のruby@railsで起動する unicorn_railsコマンドを簡単に作れる
    • 例えば以下のようにコマンドで ruby1.8.7 rails2.3 の環境(環境自身は予め作っておく)で起動する unicorn_railsコマンド ruby2.3_unicorn_rails が作れます
rvm wrapper 1.8.7@2.3 rails2.3 unicorn_rails
  • 起動スクリプト(/etc/init.d/xxxx)はrootで動作しますが、unicorn の実行は su - でrvmのインストールされたユーザーで実行すればよい
  • unicornはPassengerと同等の性能でメモリー使用量が少ない(らしい)

ということで、rvm + unicornRails を運用すれば、好きなバージョンの Ruby 、 好きなバージョンの Rails複数起動できます。

現状

ただし、お客様の環境もPassengerで動いていますのでステージングはPassengerで動かしておいた方が良いと思います。そこで今回の移設では

  • 新サーバー

としました。 新サーバーは 2Gメモリーなので、これだけ動かしても快適です :-)

unicorn の運用になれてきたら、お客様の環境も unicorn にしようかな、と思っています。

Rails2.3アプリをRails3.2にアップグレードする、その2

今回は、RSpec と 性能測定結果です。その1はこちら


RSpec

今回のプロジェクトでは、 全model、全lib (少しですが)、view のごく一部にたいしての RSpecが書かれています。
Rspecのバージョンは 1.3.2 から 2.8.0 へのアップグレードでした。

lib
  • RSpec自身たいの問題はありませんでしたが、to_s(:db)がUTCを戻す問題から少し変更しました
model
  • バリデーションのテストで、その入力フィールドでエラーが出ているのかを
c.errors.on(:name).should_not be_nil

↓ のように変更

c.should have(1).errors_on(:name)
  • libと同じく UTC の問題、日時をチェックしてる部分は多数あるので変更量は多かったですが、エディターがやってくれました。
view

view/controller周りは RSpec 2.0 で仕様変更があり、RSpecコードの変更が必要です。

  • テスト対処のviewが partialの場合は
describe "_order_list.html.erb" do
   ...
  render "order/_order_list"

↓ のように変更

describe "order/_order_list.html.erb" do
   ...
  render
  • assigns がなくなった?
   assigns[:order] = orders(:or01)

↓ のように変更

   assign(:order, orders(:or01))
  • response.should have_tag が動かなくなった
    response.should have_tag('#unit_price2', /¥30,000/)

↓ のように変更

    rendered.should have_tag('#unit_price2', /¥30,000/)
helper

このプロジェクトでは helper のRSpecは書いていませんが、教育中に発見しました。ヘルパーメソッドの呼び出しは helper オブジェクトから行います。

    it "改行を<br/>タグに置き換える" do
      new_line("abc\nxyz\n123").should == "abc<br>xyz<br>123"
    end

↓ のように変更

    it "改行を<br/>タグに置き換える" do
      helper.new_line("abc\nxyz\n123").should == "abc<br>xyz<br>123"
    end

性能測定

だいたい動くようになったので、開発サーバーにデプロイし 2.3版と 3.2.2版の性能を測ってみました。環境をそろえるために、両方とも Passenger 3.0.11 で動かしました。

サーバーは さくら VPS 512 でメモリーは512Mbyte です。

メモリー使用量

ab コマンドでページをアクセスし、top でrubyプロセスを測定しました。

環境 VIRT
Ruby1.8.7 + Rails2.3.10 140m
Ruby1.9.3 + Rails3.2.2 185m
速度

Passengerの最大プロセス数(PassengerMaxPoolSize)は2なので、並行アクセス数は 1, 2 で測定してみました。

環境 Concurrency * Requests per second Time per request
Ruby1.8.7 + Rails2.3.10 1 2.35 425.2
Ruby1.9.3 + Rails3.2.2 1 2.46 405.9
Ruby1.8.7 + Rails2.3.10 2 5.56 359.5
Ruby1.9.3 + Rails3.2.2 2 4.96 403.2
今回の結論

Ruby1.8.7 + Rails2.3.10 から Ruby1.9.3 + Rails3.2.2 にアップグレードすると、メモリーの使用量は45Mbyte、32%も増加していますが、速度は +/-10%程度なので、ほぼ同じと考えられます。

Rails2.3アプリをRails3.2にアップグレードする、その1

以前Ruby on Rails 2.3 (Ruby1.8.7) で作ったアプリを、Rails3.2 (Ruby 1.9.3)にアップグレードしようと決意しました。

ただし、それほど性能の良いサーバーで稼働しているわけではないので、3.2にした時の性能(リソースの使用量)等も気になりますので早めに動かし性能を測定する事にしました。


やること

  1. git co -b rails3.2
  2. とりあえずRails3.2で動かす
  3. 性能測定
  4. RSpecを通す
  5. Cucumberを通す
  6. リファクタリング
  7. 入替

1. とりあえずRails3.2で動かす

Rails3.2環境に移行

現行のRails2.3のコードを使うのですが、app/assetsやconfig以下などは Rails3.2に合わせないと行きません。そこで Rails3.2.3のRailsコマンドの空のプロジェクトを作成し、そこから(元のconfigはリネームし) config やapp/assets などを持ってきて Rails3.2の環境に合わせる

準備
  • 必要なライブラリー、プラグインを Gemfileに登録。 fastercsv や will_paginate など止めたい物もありますが、まずは動かす為にぐっとこられました ^^;
  • Ruby1.9用にマジックコメントの追加、magic_encoding で一発
エラー、問題点をひたすらつぶす

問題になった点を上げておきます

  • RAILS_ENV → Rails.env 、 RAILS_ROOT → Rails.root
  • lib/ が読み込まれないので config/application.rb に config.autoload_paths += %W(#{Rails.root}/lib) を追加
  • named_scope → scope
  • filter_parameter_logging を消す
  • <% form_tag → <%= form_tag
  • app/assetsの作成
    • public/{images,javascripts,stylesheets} を app/assets へ移動
    • application.js , application.css の作成
  • def before_save のようなCallbackメソッドが廃止されたので before_save :before_save_.... に置き換え
  • 文字コード正規表現の一部変更 (Ruby 1.9)
  • htmlタグを戻すhelperメソッドに raw() を追加
  • [1,2,3].to_s → [1,2,3].join (Ruby 1.9)
  • Module#public_instance_methods の戻りが文字列の配列からシンボルの配列に変わった (Ruby 1.9)
  • f.error_messagesメソッドが無くなったことへの対応。以下のようなhelperを作り対応
  def form_error_messages!(f)
    if f.object.errors.any?
      errs = f.object.errors.full_messages.inject("") do |result, msg|
       result + "   <li>" + msg + "</li>\n"
      end
      raw(%!
<div id="error_explanation">
  <h2>#{t('activerecord.errors.title')}</h2>
  <ul>
#{errs}
  </ul>
</div>
!)
    else
      ""
    end
  end

感想

いろいろと問題がありましたが、思っていたよりは変更作業は少なかった気がします。皆さんも Rails2.3 アプリの Rails3.2対応にトライして下さい ! もちろんテスト(RSpec...) ありますよね :-)