事の始まり
( ^o^)「Sinatraでメモアプリ作るぞ〜〜」
( ^o^)「起動を確認して…大丈夫だな。Ctrl+Cで終了するぞ」
(; ^o^)「…あれ!?一回押しただけじゃ終わらない!?」
結論(原因)
- スーパークラスの指定を間違えていた
- READMEをしっかり読んでいなかった
- 思い込みでコードを書いていた
- GitHub(ソースコードやissue、使用例)を読んでいなかった
- 開発のためにrequireしていたものが影響を与えていた
開発環境
Ruby 3.1.2p20
Sinatra 2.2.0
Sinatraとは?
Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort:
引用元: Sinatra
簡単にRubyでWebアプリケーションを作ることができるDSLだそうです。
使用してみた感じは、「Railsより簡単に扱える」「シンプルなWebアプリケーションを作るのに向いている」という感触でした。
READMEの日本語版が用意されているので「英語わからん」って人でもすぐに触ることができます!
今回の問題点
タイトルにもあるとおり、Sinatraを終了させるのにCtrl+Cを一回押しても終わらない(2回押したら終了する。)という状態でした。
ログと画面
bundle exec ruby app.rb
でSinatraを起動してから終了するまでのログとスクリーンショットを貼ります。
いずれも localhost:4567
です。
起動直後
正常にアプリが起動します。
$ bundle exec ruby app.rb
[2022-07-04 15:32:57] INFO WEBrick 1.7.0
[2022-07-04 15:32:57] INFO ruby 3.1.2 (2022-04-12) [arm64-darwin21]
== Sinatra (v2.2.0) has taken the stage on 4567 for development with backup from WEBrick
[2022-07-04 15:32:57] INFO WEBrick::HTTPServer#start: pid=68679 port=4567
::1 - - [04/Jul/2022:15:32:58 +0900] "GET / HTTP/1.1" 200 3873 0.0281
::1 - - [04/Jul/2022:15:32:58 JST] "GET / HTTP/1.1" 200 3873
- -> /
Ctrl + Cを一度押した後
更新するとSinatraのエラー画面が表示されるようになりました。
^C== Sinatra has ended his set (crowd applauds)
[2022-07-04 15:35:49] INFO going to shutdown ...
[2022-07-04 15:35:49] INFO WEBrick::HTTPServer#start done.
[2022-07-04 15:35:49] INFO WEBrick 1.7.0
[2022-07-04 15:35:49] INFO ruby 3.1.2 (2022-04-12) [arm64-darwin21]
== Sinatra (v2.2.0) has taken the stage on 4567 for development with backup from WEBrick
[2022-07-04 15:35:49] INFO WEBrick::HTTPServer#start: pid=68679 port=4567
Ctrl + Cを二度押した後
ここでやっと終了しました。
^C== Sinatra has ended his set (crowd applauds)
[2022-07-04 15:36:51] INFO going to shutdown ...
[2022-07-04 15:36:51] INFO WEBrick::HTTPServer#start done.
コード
最初はクラシックスタイルで開発し、途中からRubocopのMixinUsageの指摘を受けないようにするためモジュラースタイルに切り替えました。
モジュラースタイルに切り替えてから今回の問題が発生するようになりました。
クラシックスタイル
# frozen_string_literal: true
require 'sinatra'
require 'sinatra/reloader'
include ERB::Util
# 省略
get '/' do
# 省略
end
モジュラースタイル
# frozen_string_literal: true
require 'sinatra'
require 'sinatra/reloader'
class App < Sinatra::Application
include ERB::Util
# 省略
get '/' do
# 省略
end
run! if app_file == $PROGRAM_NAME
end
解決策
Sinatra requires CTRL+C twice to shutdown にて同様の現象が報告されていたため確認をしました。モジュラースタイルで書くときには sinatra
ではなく sinatra/base
をrequireするようです。
Sinatra::Base - ミドルウェア、ライブラリおよびモジュラーアプリでもsinatra/base
をrequireしています。しっかり読むべきでした……
ただし、今回はrequire sinatra/base
を変更しただけでは終わりませんでした。
require sinatra/baseに修正してみる
先ほどのapp.rbを修正します。
# frozen_string_literal: true
require 'sinatra/base' # 修正
require 'sinatra/reloader'
class App < Sinatra::Application
include ERB::Util
# 省略
get '/' do
# 省略
end
run! if app_file == $PROGRAM_NAME
end
実行すると
$ bundle exec ruby app.rb
何も起きなくなりました。
sinatar/reloaderを消す
他に原因があるとするならばreloaderかもと思いコメントアウトしてみました。
# frozen_string_literal: true
require 'sinatra/base' # 修正
# require 'sinatra/reloader'
class App < Sinatra::Application
include ERB::Util
# 省略
get '/' do
# 省略
end
run! if app_file == $PROGRAM_NAME
end
実行!
$ bundle exec ruby app.rb
[2022-07-05 11:55:35] INFO WEBrick 1.7.0
[2022-07-05 11:55:35] INFO ruby 3.1.2 (2022-04-12) [arm64-darwin21]
== Sinatra (v2.2.0) has taken the stage on 4567 for development with backup from WEBrick
[2022-07-05 11:55:35] INFO WEBrick::HTTPServer#start: pid=4566 port=4567
動く!!
^C== Sinatra has ended his set (crowd applauds)
[2022-07-05 11:57:54] INFO going to shutdown ...
[2022-07-05 11:57:54] INFO WEBrick::HTTPServer#start done.
Ctrl+C一回で停止する!!!
理由はわからず…
Sinatra::Base白紙の状態です。組み込みサーバーを含め、ほとんどのオプションはデフォルトで無効になっています。使用可能なオプションとその動作の詳細については、設定の構成を参照してください。アプリをトップレベルで定義する場合(クラシックスタイルとも呼ばれます)に似た動作が必要な場合は、次のようにサブクラス化できますSinatra::Application。
引用元: README
GitHubを確認してみる
Sinatra::Applicaton
Execution context for classic style (top-level) applications. All DSL methods executed on main are delegated to this class.
The Application class should not be subclassed, unless you want to inherit all settings, routes, handlers, and error pages from the top-level. Subclassing Sinatra::Base is highly recommended for modular applications.
Google先生に翻訳してもらいました。
クラシックスタイル(トップレベル)アプリケーションの実行コンテキスト。 mainで実行されるすべてのDSLメソッドは、このクラスに委任されます。
トップレベルからすべての設定、ルート、ハンドラー、およびエラーページを継承する場合を除いて、Applicationクラスをサブクラス化しないでください。 Sinatra :: Baseのサブクラス化は、モジュラーアプリケーションに強くお勧めします。
READMEでSinatra::Application
をサブクラス化する方法も紹介してるから、絶対にBaseを継承する必要はなさそう
reloader
使用例のモジュラースタイルでは Sinatra::Base
を継承してて、Sinatra::Application
を継承する例は見つからず…。モジュラースタイルでSinatra::Application
を継承する場合は使わない方が良いのだろうか
最後に
今回の事象は一人で解決できませんでした。現在参加してるFjordBootCampで質問をして、メンターのShohei Umemotoさんに「GitHubのissueを確認してみよう!」とアドバイスをいただいた結果解決につながりました。
今回に限りませんが、自分のわからない!って状態を外に発信するのは非常に大事だなと感じます