Edited at

Rails Engineメモ

More than 3 years have 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