Rails
Gem

Rails Engineメモ

More than 1 year has passed since last update.

背景

Rails Engineを触ってみたのでメモ。

Engineとは何かなどは http://guides.rubyonrails.org/engines.html

サンプル

適当なサンプルということで、発生した例外をDBに保存するような仕組みを考える。

Engineの役目は、エラーの詳細を見るための画面や、例外保存処理をホストアプリに提供することである。

生成

rails plugin new error_catcher -T --dummy-path=spec/dummy --mountable

-Ttest関連の生成をスキップ。使い慣れているのがrspecなもので。。。

--dummy-path=spec/dummyは、engineの動作確認用のdummyのrailsアプリが生成されるパス。rspecを使うので、dummyアプリの置き場所もspec以下にする。

--mountableで、独立した名前空間を持ったskeltonが生成される。(正直ここはふわっとした理解)

rspecの準備

以下を追記。

error_catcher.gemspec
s.test_files = Dir["spec/**/*"]
s.add_development_dependency "rspec-rails"
lib/error_cathcer/engine.rb
config.generators do |g|
  g.test_framework :rspec
end

helperファイルを生成

bundle install
bin/rails g rspec:install

spec_helper.rbとかrails_helper.rbが生成される。

enviromentの読み込み先をdummyアプリに変える

spec/rails_helper.rb
require File.expand_path('../dummy/config/environment', __FILE__)

モデルを作成

bin/rails g scaffold error name:string description:string stacktrace:text fired_at:datetime
bundle exec rake db:migrate

ダミーアプリを起動

cd spec/dummy
bin/rails s

http://localhost:3000/error_catcher/errors

よくあるIndex画面が出るはず。

specを書いてみる

scaffoldで生成していれば、spec/models/error_cathcer/error_spec.rbが存在するはず。これを以下のように修正してみる。

spec/models/error_catcher/error_spec.rb
require 'rails_helper'

describe ErrorCatcher::Error do
  describe 'Validation' do
    it 'nameは必須' do
      error = described_class.new
      expect(error.valid?).to be_falsey
    end
  end
end

実装も直す。

app/models/error_catcher/error.rb
module ErrorCatcher
  class Error < ActiveRecord::Base
    validates :name, presence: true
  end
end
bundle exec rspec spec/models/error_cathcer/error_spec.rb

でテストが通るはず。

ホストアプリ側から呼び出すメソッドを実装する

ホストアプリ側からは、直接Modelを呼ぶのではなく例外保存用のインタフェースを呼ぶ。モデルを直接ホストアプリから呼ぶこともできるが、アプリとgemの依存関係が強くなってしまうので、別途外向けのインタフェースを用意する。

lib/error_catcher/catcher.rb
module ErrorCatcher
  module Catcher
    def catch e
      error = ::ErrorCatcher::Error.new
      error.name = e.class.name
      error.description = e.message
      error.stacktrace = e.backtrace.join("\n")
      error.fired_at = Time.now
      error.save!
    end
  end
end

アプリへ組み込み

error_catcherを利用する適当なアプリを作る。今回はerror_catcherと同じ階層に作る。

rails g new error_thrower
cd error_thrower
bin/rails g scafold item name:string description:string price:integer
Gemfile
gem 'error_catcher', path: '../error_catcher'
bundle install

error_catcherが提供するmigrationファイルを取得&migration。

bundle exec rake error_catcher:install:migrations
bundle exec rake db:migrate

コントローラで予期せぬ例外が起きたら全てキャッチして保存する。

error_thrower/app/controller/application_controller.rb
include ErrorCatcher::Catcher
rescue_from Exception, with: :error_handler

def error_handler e
  catch(e)
  render file: "#{Rails.root}/public/500.html", layout: false, status: 500
end

エラーが起きた時の動作確認のために、Validationをかける。

error_thrower/app/models/item.rb
validates :name, presence: true

error_thrower/app/controller/items_controller内の@item.saveを全て@item.save!に変えて、画面から空入力で保存するとValidationErrorが起きるようにする。

bin/rails s

http://localhost:3000/items/new から空で保存すると、エラー画面が表示され、http://localhost:3000/error_catcher/errors を見れば発生したエラーが保存されている。

これで、例外保存、閲覧の仕組みをRailsアプリに組み込むEngineができたことになる

作ったものはこちら https://github.com/hokuma/error_catcher