Rails
AWS
Rails5
Jets
ServerlessFramework

【現場で使える】サーバーレスフレームワーク「Ruby on Jets」でRails依存ライブラリを導入した際の追加設定


概要

4ヶ月ほどJetsを使って、Railsライクに使えてきたので布教活動してみます。

主にRailsで使ってよかったライブラリを、Jetsで動作させるまとめです。

(JetsにはMega ModeがあるのでRailsをそのまま動かせますが、眠っているLambdaの起動スピードを考慮すると、オススメできない認識です)


Railsの良いところの定義

下記記事がとても本質的なのでぜひ一読ください。


バックエンドからクライアントサイドまで一貫して作るイメージが強いRailsですが、Railsの真価とはそこにないと考えています。


参考: 「Railsは終わらない」と私が言う理由


Jetsとは?

私が触るきっかけになった、とても良い記事でしたので感謝です! ↓

参考: Ruby 製サーバーレスフレームワークの Jets を検証してみたら、Rails ライクに使えていい感じだった


動作環境

記載時のJetsはv1.9.16で動作確認しています。

ほとんど毎日リリースされているライブラリですが破壊的な変更には出会ってないですし、AWS上でも問題なく動作しています。

参考: Upgrading Guide

Serverless FrameworkがBetaだったころは、フォルダ構成がガラッと変わってアップデートが大変になりましたが、JetsはRailsのラッパーなので安定している印象です。Rails6対応でハマるかとは思います。

(今のServerless Frameworkはとても安定しています。昔はコードジェネレーターがとても強力だったのですが、方針転換により色んなサービスに対応した結果、デプロイツール&サービス設定ツールになった印象。それはそれで便利ですが!)


ライブラリ導入した際の追加で必要だった設定


Rails ERD

Rails同様のジェネレータは標準で動きます。jets generate model XXXXXなど。

schema運用していると、小規模ならRails ERDが便利です。

しかし、マイグレーション時に自動で動かなかったので下記ファイルを作成しました。


bin/erd.rb

# frozen_string_literal: true

# モデルに関するライブラリを読み込むように
require 'jets'
require 'rails'
require 'active_record'
# モデル内に`paginates_per`など定義していないならいらない
require 'kaminari'
require './app/models/application_record.rb'

def load_db_config
Jets::Dotenv.load!
database_yml = "#{Jets.root}/config/database.yml"
return unless File.exist?(database_yml)

text = Jets::Erb.result(database_yml)
# YAML.safe_loadだと動かないので
# rubocop:disable Security/YAMLLoad
db_config = YAML.load(text)
# rubocop:enable Security/YAMLLoad
db_config
end

current_config = load_db_config['development']
ActiveRecord::Base.establish_connection(current_config)

# ActiveRecord系モデルのみこのディレクトリに置いているので全部読み込んでいます
Dir["#{Jets.root}/app/models/*.rb"].each { |file| require file }

require 'rails_erd/diagram/graphviz'

# Jetsプロジェクト外に作らないとS3にアップロードされるので保存場所に注意
RailsERD::Diagram::Graphviz.create(filename: '../erb', filetype: 'png')


マイグレーション時に自動実行もできるはずですが ruby bin/erd.rb を叩く程度なので手動でやってます。


Jbuilderなどのテンプレート

Railsの良いところにも関わりますが、Lambdaの利点を生かすならやはりAPIです。 jets new api --mode apiならwebpackなどが入らない最低限のプロジェクトが作れます。


config/initializers/jbuilder.rb

# frozen_string_literal: true

require 'jbuilder/jbuilder_template'

# 参考 https://community.rubyonjets.com/t/rendering-as-xml-doesnt-work/80/2
ActiveSupport.on_load :action_view do
ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
require 'jbuilder/dependency_tracker'
end


参考: initializers

これでJbuilder.newを書く単体利用じゃなく。Railsのようにスマートに使えましたし、Jbuilder以外のテンプレートも増やせますね。

また、ソース内にコメント記載していますが、困ったらコミュニティで検索すると大概ヒントがあります。


RSpec

以前はRAILS_ENVが定義されていなかったので、rspec-railsが動かなかったのですが今は自動で追加されているので動くかもです。(未確認)

私はRSpecにて、リクエストテストっぽいものを書くためにヘルパーを用いました。


spec/spec_helper.rb


# frozen_string_literal: true

# 参考
# https://github.com/tongueroo/jets-example-api/tree/master/spec
# https://community.rubyonjets.com/t/rspec-controller-does-not-get-render-response/56/3

ENV['TEST'] = '1'
ENV['JETS_ENV'] ||= 'test'

require 'byebug'
require 'jets'
require 'dynomite'

abort('The Jets environment is running in production mode!') if Jets.env == 'production'
Jets.boot

require 'jets/spec_helpers'

FactoryBot.find_definitions

require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
Dir["#{Jets.root}/spec/supports/*.rb"].each { |file| require file }

# テスト用にメソッドを定義
module Helpers
# 例) UsersControllerのindexへのリクエスト
# describe UsersController, type: :request do
# it '#index' do
# request(params:{ page: 1 }, action: :index)
# expect(json['apiStatus']).to be true
# end
# end

def request(params: nil, headers: nil, action:)
event = { queryStringParameters: params, headers: headers }
instance = described_class.new(event.to_h.with_indifferent_access, {}, action)
response = instance.dispatch!

raise 'internal error!' if !response.is_a?(Array) || response.size != 3

@response = response[0].to_i
@json = JSON.parse(response[2].read).to_h.with_indifferent_access
p json
end

def response
@response
end

def json
@json
end

def login_header(user)
{ Authorization: user.create_login_token }
end
end

# Rails同様
RSpec.configure do |config|
config.include Helpers
config.include FactoryBot::Syntax::Methods

# DBのクリーンアップとシード実行
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)

load "#{Jets.root}/db/seeds.rb"
end

config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
end