モンモンブログ

Ruby, Python, PHP, JavaScript/jQuery などなど気分に応じて

Rails + Grape 構成で Grape API ファイルを自動再読み込みさせるには

RESTful な API が楽ちんに書ける graperails との組み合わせで使う場合も README に従うだけで簡単に出来ますが、なぜか自動再読み込み (auto reloading) されないって問題にぶち当たります。

development 環境だと model や controller への修正はブラウザをリロードするだけで即反映されますよね。でも grape の api ファイルはなぜか再読み込みされない。いちいち rails サーバを再起動しないといけなくて、非常に困っちゃんです。

(よく理解してませんが rails 上で別の rack フレームワーク(この場合 grape)を動かそうとするとrails の自動再読み込みの仕組みがうまく動かないみたいです)

で、解決方法ですが、grape api ファイルの更新を検知して、無理やり再読み込みさせてやります。

環境

古くてごめんなさい

MacOSX Mavericks
ruby 1.9.3p547 (2014-05-14 revision 45962) [x86_64-darwin13.3.0]
rails-3.2.11
grape-0.9.0

セットアップ

今回は既存の rails アプリに grape を追加しました。手順をざくっと書いておきます。

Gemfile に追記して、

gem 'grape'

インストール

bundle install

grape api ファイルを置くディレクトリを作って

mkdir app/api

Hello World なやつを書いた。(app/api/skimatalk_api.rb)

require 'grape' # この行は grape 単体で動かす場合に必要。rails との組み合わせでは不要
class SkimatalkApi < Grape::API
  format :json

  resource :ehehe do
    desc 'this is a test'
    get :hoe do
      {a: 1, b: 2}
    end
  end
end

rails と組み合わせて実験する前に grape 単体で動かしてみたかったので、同じディレクトリにテスト用の config.ru を作成 (app/api/config.ru)

# coding: utf-8
require './skimatalk_api.rb'
run SkimatalkApi

起動

$ cd app/api
$ rackup
Thin web server (v1.6.2 codename Doc Brown)
Maximum connections set to 1024
Listening on 0.0.0.0:9292, CTRL+C to stop
127.0.0.1 - - [22/Nov/2014 19:54:05] "GET /ehehe/hoe HTTP/1.1" 200 13 0.0971

http://localhost:9292/ehehe/hoe にアクセスすると JSON が表示された。わーい。

f:id:ymdsmn:20141122201157p:plain

(さっき作った config.ru はもう不要なので削除してよいです)

今度はこれを rails 上で動かします。 config/routes.rb に以下を追加して /api/* へのリクエストを grape へ渡すようにします

  mount SkimatalkApi => '/api'

サーバ再起動してから、

rails s

http://localhost:3000/api/ehehe/hoe にアクセス。JSON が表示された。やったね。

f:id:ymdsmn:20141122201201p:plain

でも(さっきも言ったとおり)app/api/skimatalk_api.rb を修正してリロードしても変更が反映されません。困ったね。

解決方法

ここからが本題。 config/initializers/reload_api.rb を作って以下のように書きます

if Rails.env.development?
  api_reloader = ActiveSupport::FileUpdateChecker.new(Dir['app/api/*']) do # app/api/* は環境にあわせて変更すること
    Rails.application.reload_routes! # or do something better here
  end

  ActionDispatch::Callbacks.to_prepare do
    api_reloader.execute_if_updated
  end
end

これだけで、 app/api/* への修正を検知して自動再読み込みしてくれるようになりました。

仕組みは読めばなんとなく分かるよね。分かんなくてもいいよね。

参考

正直このエントリは stackoverflow のこれ↓を解説しただけです。ググって見つけるのに苦労したので書きました…
Ruby on Rails 3 - Reload lib directory for each request - Stack Overflow