2014/01/26

rails+MySQLでdouble型カラムを扱うモンキーパッチ

背景

ruby2.1とrails4を使ってアプリを作っている。DBはMySQLを使っていて、わけあってあるテーブルの列をdouble型にしたいと思って。 カラムの列を変更するmigrationを作ろうと思った時に、そもそもdouble型のカラムを作るにはどうするんだっけ、ってなって、 Webを調べてみて、解がみつかったので試したところ、ちょっと不都合が生じて、それじゃあモンキーパッチを書いてみよう、という話です。

MySQLのdouble型を利用するmigration

まず、MySQLのdouble型の列を使うには、

add_column :table_name, :column, :float, limit: 53
のようなmigrationを書くとできる。(これは、既存のテーブルにカラムを追加するmigration)

型に:floatを指定して、:limitに53を指定する。参考にしたのは、

limit: 53は、MySQLのfloat型に精度を指定して定義する際に、精度が24〜53の場合は倍精度な浮動小数点型として扱われ、すなわちdouble型になるから、という風に理解している。(MySQL :: MySQL 5.6 Reference Manual :: 11.2.3 Floating-Point Types (Approximate Value) - FLOAT, DOUBLE)

問題点

上述のmigrationでカラムを追加すると、確かにdouble型のカラムになる。が、問題点が1つ。bundle exec rake db:migrateした時に、db/schema.rbが更新されるんだけど、このファイル内の当該カラムには、limit: 53が指定されない。結果、bundle exec rake db:test:prepareで、テスト環境のスキーマを更新した時に、当該カラムの型はfloatとして作成されてしまい、期待どおりにデータが扱えなくなってしまう。

ついでにいうと、本来double型を使いたいのに、:float, limit: 53って書くのもの、ちょっと違和感ありますね。

モンキーパッチ

db/schema.rbを手で直すってのも、あれなんで、なんとかならないものか、思って調べ始めたら、次のようなページを発見。

このページのハックを適用すると、
#add_column :table_name, :column, :float, limit: 53
# が↓のように書けるようになる。
add_column :table_name, :column, :double
となり、問題点の2つ目に対する解になりそう。が、db/schema.rbの問題は解決しないなぁ。もうちょっと調べるか、と思ってrailsのソースをみてたら、ActiveRecord::ConnectionAdapters::Columnというクラスのextract_limit(sql_type)というメソッドをみつけた。このメソッドは、どうやらSQLの型をみて、db/schema.rbを吐き出すときに:limitに何を指定するかを判断するものっぽい。各RDBMS用のアダプターでこれらをオーバーライドしているし。

ということで、次のようなモンキーパッチをconfig/initializers/add_double_to_activerecord.rbとして、作っておくことに。

module CustomColumnTypes 
  def double(*args)
    if args.last.is_a? Hash
      args.last[:limit] = 53
      args.last[:null]  = true
    end
    float *args
  end
end
module ColumnWithDouble
  def extract_limit(sql_type)
    case sql_type
    when /double/i
      53
    else
      $1.to_i if sql_type =~ /\((.*)\)/
    end
  end
end
module ActiveRecord
  module ConnectionAdapters
    class Table
      include CustomColumnTypes
    end
    class TableDefinition
      include CustomColumnTypes
    end
    class Column
      prepend ColumnWithDouble
    end
  end
end

これでdb/schema.rbにも無事にlimit: 53が出力されるようになった。

課題

上のパッチでは、ActiveRecord::ConnectionAdapters::Columnに対して振る舞いを変えているんだけど、僕がdoubleをサポートしたいのはMySQLの場合のだけだから、実はうまくない。なので、MySQL用のアダプタをイジる方法に変えるべきだなぁ、と思ってます。

2014/01/12

railsでREST Web APIをつくってrspecでテストする。

railsでRESTfulなAPIを書いていて、テストはrspecを使うと決めていたのだが、対象がRESTful APIということで、 何か気にすることはあるのだろうか。テストというか、設計というか、これまで実践していたことと、 今回調べて新たに発見したことを合わせて、纏めておく。

テスト対象

テスト対象は、rails4で作ったRESTfulなAPI。全てのAPIはJSONフォーマットのみ対応。 つまり、htmlとかxml形式の応答はしない。今回試したのはRails4。

rspecとその他のgem

テスト環境にいくつかgemを追加して、テストの準備をする。 追加するのは、

の各gem。それぞれのgemの用途と設定をちょっと書きます。

rspec

テストを記述する。rspec、railsアプリのrspecでのテストの仕方は、

が参考になる。

railsアプリのテストでrspecを利用するための手順

-T付きでrails newする。

デフォルトのrailsアプリのテストは、Test::Unitが使われ、test用のディレクトリが生成される。-TオプションをつけるとTest::Unitが無効になり、testというディレクトリが生成されない(rspecのテストコードは、specディレクトリに入れる。)

$ rails new application_name -T

Gemfileの記述

Gemfileに以下を追加する。

group :development, :test do
  gem 'rspec-rails'
end

bundle installの実行

bundlerを使ってrpsecをインストールする。

$ bundle install

rspecの初期設定

rspecをrailsアプリに組み込むと、railsコマンドのgeneratorにrspec:installが追加される。 追加されているかは、rails g -hを実行すれば確認できる。

$ rails g -h
(略)
Rspec:
  rspec:controller
  rspec:helper
  rspec:install
  rspec:integration
  rspec:mailer
  rspec:model
  rspec:observer
  rspec:scaffold
  rspec:view
(略)
rspec:installを実行して、specディレクトリを作るとともに、spec_helper.rbを生成する。
$ rails g rspec:install
ここまでが、rails上でrspecを使うためのベーシックな手順。

simplecov

simplecovは、ruby用のコードカバレッジ計測ツールで、railsでも使える。 rspecとも簡単に連動できるし、生成されるレポートも見やすい。

Gemfileの記述

Gemfileに以下を追加する。

group :development, :test do
  gem 'simplecov'
end

bundle installの実行

bundlerを使ってsimplecovをインストールする。

$ bundle install

rspecとsimplecovの連携

spec/spec_helper.rbの先頭に次のコードを追加するだけで、rspecが動くたびにカバレッジを計測してくるようになる。 計測結果は、デフォルトではcoverageディレクトリにhtmlファイルとして出力される。

require 'simplecov'
SimpleCov.start 'rails'

guard-rspec

rspecを使ってテストを書いている場合、コードを直してspecを直して、rake specで全てのテストが問題なく通ることを確認して、というサイクルで開発していくことになる(このサイクル自体はrspecを使う場合に限らないけど。)。この時に修正したモジュールに対するspecの実行とか、全てのspecの実行とか、いちいちコマンドを叩いているのが面倒になる。そんなとき、guardを使うとspecの実行を自動化できるようになる。guardは、プロジェクト内のファイルの変更を監視して、変更が発生すると、ある動作を実行する、というもの。なので、rubyファイルやspecファイルをguardに監視させて、変更が発生したらテストを実行する、というように使う。


に詳しい説明があってとても参考になる。

Gemfileの記述

Gemfileに以下を追加する。

group :development, :test do
  gem 'guard-rspec'
end

bundle installの実行

bundlerを使ってgurad-rspecをインストールする。

$ bundle install

guardの設定

guardは、Guardfileの記述に従って動作する。Guardfileは、

$ bundle exec guard init rspec
で生成する。Guardfileに監視対象と、実行するspecの組み合わせを追加していくのだけど、これについては後述する。

guardの実行

guardを実行するには、

$ bundle exec guard
とする。すると、
13:36:21 - INFO - Guard is using NotifySend to send notifications.
13:36:21 - INFO - Guard is using TerminalTitle to send notifications.
13:36:21 - INFO - Guard::RSpec is running
13:36:21 - INFO - Guard is now watching at '監視対象パス'
[1] guard(main)> 
と表示され、監視が始まる。guardのプロンプトで、"all"(またはEnter)と打つと、全てのspecが実行される。

spring

guardを使ってrspecを動かす手間がかなり省けるようになると、次に気になるのがspecの実行時間。specの量が増えてくると、実行時間が長くなるし、そもそも起動時間も長くなって時間が勿体無く感じてくる。 springを使うと、railsの環境を先読みしてくれるようになるので、様々なプロセスの起動が速くなる。これをguardと組み合わせて使うことで、guardが実行するrspecを高速化することができる。

Gemfileの記述

Gemfileに以下を追加する。

group :development, :test do
  gem 'spring'
end

bundle installの実行

bundlerを使ってspringをインストールする。

$ bundle install

guardとspringの連携

guardとspringを合わせて使うには、Guardfileをちょっと修正するだけでよい。

guard :rspec, spring: true do # spring: true を追加する。
  :(略)
end

factory_girl

railsでrspecを使うときのテストデータに関して面倒なことがある。デフォルトだと、spec/fixturesディレクトリにテーブル(model)単位にymlファイルを作ってテスト時にロードして、っていうやり方になるけど、これだとモデル間のリレーションを考慮したテストデータづくりが非常に面倒くさいし、データが増えると管理できなくなる。この問題は、facotyr_girlを使うことで対応できる。factory_girlは、fixturesに定義していたymlをrubyで記述できるDSLを提供してくれている。そのため、データの定義やデータ間の関連がスッキリ書けるようになる。

などが参考になる。

Gemfileの記述

Gemfileに以下を追加する。

group :development, :test do
  gem 'factory_girl_rails'
end

bundle installの実行

bundlerを使ってfactory_girlをインストールする。

$ bundle install

JSONを返すRESTなAPI

ここまではどちらかというと、あまりJSONとかRESTとかとは直接関係しない、rspecを使ったテストを効率良く進めるための話題。 ここからはJSONを返すRESTなAPIのテストに関する話題。

にもろに影響を受けている。

APIのバージョニングを考慮する。

実際に経験したことでは、スマートフォンアプリをクライアントとするAPIを作っていて、アプリの機能を追加・変更して行く過程で、 API自体にバージョンの概念が必要になって、APIのリソースのURLにバージョン番号を埋めた、ってことがある。具体的には、例えばユーザ情報を取得するAPIが、

https://api.exapmle.com/users/id.json?id=xxxx
のように定義していて、ある時点から応答するデータの内容が大幅に変える必要がある、という場合。やったのは、
https://api.exapmle.com/v2/users/id.json?id=xxxx
という新しいURLを定義して、元々のAPIと並行して運用するということをした。このやり方(URLにバージョンを埋める)は、Twitter APIやFoursquare APIでも同じようなやり方をしているので、結構メジャーなソリューションなのかと思っている。railsでの実現方法は、

が参考になる。

APIのフォーマットをJSONに限定する。

railsのコントローラをgenerateコマンドで定義すると、デフォルトではhtmlを応答するアクションが生成されるけど、APIを作っている場合、htmlを応答することはまず無いし、一度JSONを返すと決めたら、後々xmlだとか他のフォーマットに対応させる、という変更が発生することも少ないのではないか、と思っている。なので、始めからJSONのみに限定したAPIにしてしまえば、設計もテストも楽になると考えた。

railsであるURLがデフォルトでJSONのみに対応するようにするには、

  • routes.rb
  • controller
  • view
を意識的にイジる必要がある。

config/routes.rb

例えば、ユーザ情報に関するAPIをrailsのresourcesを使いつつ、JSONに限定する場合は、

namespace :v1, defaults: { format: 'json' } do
  resources :users
end
としてあげると、通常必要となるURLの最後の".json"が不要となる。

controller

コントローラ側でJSONのみに対応させるには、

module V1
  class UsersController < ApplicationController
    respond_to :json

    def show
      @user = User.find(id: params[:id])
    end
  end
end
のように、先頭でrespond_toを読んであげれば個々のアクションでrespond_toを書く必要がなくなる。

view

JSON形式の応答も、独立したviewを作成することができる(知らなかった・・)。rails3.2から導入されたjbuilderというgemを使って、例えば上述のusers#showに対するviewは、app/views/v1/users/show.json.jbuilder というファイルを作ればよい。

jbuilderに関する解説もRailsCastの、


がわかりやすい。簡単にJSONのviewが作れるので感動的。

ここまでは、JSONを返すAPIの設計の話だったけど、次はテストの話。

テストは、Request Specに書く。

前述の「Rails API Testing Best Practices With RSpec」によると、APIのテストは、(Controller Specではなく、)Request Specに書くべき、ということらしい。 APIの呼び出しと応答(JSON)をテストする場合は、コントローラ単体というよりは、ルーティング、コントローラ、モデルなど各スタック間のIntegration Testに近いイメージで、rspecのRequest Specは、それがテストできるように設計されているから、ということっぽい。

JSON Helperをこしらえる。

JSONを返すアクションのテストを書くと、確かに

JSON.parse(response.body)
が頻発する。これをテストごとにいちいち書かずに、モジュールを作ってDRYに行こうぜ、と。
# spec/support/request_helpers.rb
module Requests
  module JsonHelpers
    def json
      @json ||= JSON.parse(response.body)
    end
  end
end
というモジュールを作っておいて、spec_helperで、
RSpec.configure do |config|

  config.include Requests::JsonHelpers, type: :request

end
としておけば、簡単にパースされたJSONオブジェクトにアクセスできるようになる。

まとめ

railsでREST Web APIをつくってrspecでテストする、という場合に、テスト関連ライブラリの準備や、APIの設計方法、テストのポイントをグワーっと書いてみました。中身が薄かったり、言葉足らずだったり、まとめきれてない感じもあります。更に精進して、改善していきたいと思います。

2014/01/09

railsでテストデータの管理にfactory_girlを使うのが良さそう。

RSpecを使ってテストを書いていて、 コードの量が増えるとテストで使うデータの管理が煩わしくなるなぁ、と思っていた。 何しろfixturesをymlで書くので、1対多とか多対多の関連を持つデータを定義、管理するのが面倒。

そこで今更ながら知ったのが factory_girl というgemです。

A library for setting up Ruby objects as test data.
とあり、RSpecのfixtures地獄から解放してくれるスグレモノっぽい。

factory_girl導入

Gemfileに、

group :test do
  gem 'factory_girl_rails'
end
を足して、
$ bundle
すると利用可能に。

データの定義

例えば、ymlに、

default:
  id: 1
  email: test@test.com
  password: password
  password_confirmation: password
と書いて定義していたレコードが、
FactoryGirl.define do
  factory :user do
    id 1
    email 'test@test.com'
    password 'password'
    password_confirmation 'password'
  end
end
という具合にRubyコードで定義できるようになる。

データの利用

ymlによるfixturesを利用する場合は、各specで、

fixtures :users
でテストデータを投入して、
before(:each) do
  @user = users :default
end
とか書いて、データを読み込んでいたが、factory_girlを使うと、
before(:each) do
  @user = FactoryGirl.create(:user)
end
ってするだけで、データが読み込める。このcreateは、DBにレコードをinsertし、そのレコードをfindして返すみたいな動作をするけど、 他にも、
  • create
  • build
  • attributes_for
  • build_stubbed
というメソッドが用意されていて、テストケースに応じて使い分けることができる。

詳しいドキュメントは、githubのGETTING_STARTED.mdにあるので、よく読んで使い方をマスターしたい。

2014/01/07

express.jsで自作faviconを使用する。

express.js

express.js では、デフォルトでexpress.js自身が提供するfaviconを配信するよう、コードが自動生成される。具体的には、express.jsが提供するアプリケーションのひな形を生成するコマンドを、

 $ express 
のように実行すると、
  • app.js
というファイルが生成され、
:(略)

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(require('less-middleware')({ src: path.join(__dirname, 'public') }));
app.use(express.static(path.join(__dirname, 'public')));

:(略)
というコードが出力される。上記の、
express.favicon()
が、デフォルトのfaviconの使用を定義するコード。

express.favicon()

express.favicon()は、nodeアプリのためのミドルウェアで、connect というのがあって、こいつが提供する機能の1つ。詳しくは、ここにドキュメントがあった。読むと、

By default serves the connect favicon, or the favicon located by the given path.
とあるので、デフォルトだとconnectのfaviconを配信するし、パスが与えられればそいつを配信するよ、ってことだね。(同じページにソースも載っている。)

自作のfaviconを配信する。

なので、次のように自作favicon.icoへのパスをfavicon()関数に渡してあげればよさそう。

app.use(express.favicon(__dirname + '/public/images/favicon.ico', {
  maxAge: 2592000000 // キャッシュの有効期限
}));

2014/01/05

three.js + tween.js でMatrixのアレっぽいものを作ってみた。

「three.jsでカレンダーを3Dにしてみた。」でthree.jsとtween.jsを使ったカレンダーを作ってみたけど、もうちょっと凝ったカレンダーが作れそうとか思っていて、実験として何か作ってみようということで、映画「マットリックス」に出てくるアレっぽいものを作ってみた。

tween.jsのonComplete関数を使って、落下するコードを3Dオブジェクトとしても、DOM要素としても消滅させて、新たなコードを生成して、tweenのアニメーション対象として追加する、というコードにしてみたけど、それでいいのかどうか。とりあえず、http://simalabs.com/labs/3d/matrixで動かしています(chromeで動作確認済み)。リクエストがあれば、コードを載せますので、コメントいただければと思います。