箱のプログラミング日記。

えんじにあ奮闘記

RailsEngineでRubyのバージョンを変えたい

f:id:y_hakoiri:20191120232646j:plain

RailsEngineでRubyのバージョンを変えて使えないのかな〜と思って色々と調べた結果をメモ。

RailsEngineとは

エンジン (engine) は、ホストとなるRailsアプリケーションに機能を提供するミニチュア版Railsアプリケーションとみなせます。

**

すなわち、エンジンとアプリケーションは、細かな違いを除けばほぼ同じものであると考えていただいてよいでしょう。(中略)エンジンとアプリケーションは、同じ構造を共有しています。

Rails エンジン入門 - Railsガイド

簡単に言うと、大元のアプリケーションとは別に独立したアプリケーションを作成することができるRailsの機能の一つのこと。

モデルやコントローラーの機能は継承して共有しつつも、大元のアプリの肥大化を防ぎ切り分けて管理することができる。

詳しくは下記の記事でわかりやすく書かれています。

[Rails4/5]はじめてのMountable Engine - Qiita

Rails Engineを使ってAPIと管理画面を分離する - blog.daich.org

今回の背景

今回、どうしてもRubyのバージョンを既存の2.4系から2.5系以上に引き上げなければいけないケースが発生し、

アプリケーション全体をバージョンアップ対応するにはリスクが大きい&数ヶ月ほど時間を有することを考えた時に、

  • 大元のアプリ(2.4系)とは別プロジェクトで2.5系のアプリケーションを一から作成して連携
  • 大元のアプリ配下にRailsEngineの機能を使いEngine内だけ2.5系で動かせないかどうか

の2つの案が上がった。

一からアプリを作る方が確実ではあるものの、サーバーを新たに用意したりリポジトリを分けて管理したりしなくてはいけないのが面倒だったので、元々導入経験のあるRailsEngineでどうにかならないかと思い調べてみた。

検証する前は、そういったニーズが一定数ありそうだからググれば事例が出てくるんじゃないかな〜とか思っていた。

結論から言うと、使えなかった。(TT)

試したこと

  • 大元のアプリ...hoge_app
  • Engine側のアプリ..fuga_app

とする。

fuga_appディレクトリで2.5系をインストール

rbenvがあればディレクトリごとに異なるRubyのバージョンを入れて動かすことができるので、これはすんなり実現できた。

$ pwd
/Users/hako/Project/hoge_app
$ ruby -v
ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin18]

大元のアプリhoge_appでは2.4.3を使っている。

$ cd fuga_app
$ rbenv versions
  system
* 2.4.3 (set by /Users/hako/Project/hoge_app/.ruby-version)

rbenv versionsで見るとまだ2.5系が入っていないのでインストール

$ rbenv install 2.5.0
Downloading ruby-2.5.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.bz2
Installing ruby-2.5.0...
Installed ruby-2.5.0 to /usr/local/rbenv/versions/2.5.0
$ rbenv versions
  system
* 2.4.3 (set by /Users/hako/Project/hoge_app/.ruby-version)
   2.5.0

インストールできたので、現在のfuga_appディレクトリのみ2.5系を使うよう設定

$ rbenv local 2.5.0
$ rbenv versions
  system
  2.4.3
* 2.5.0 (set by /Users/hako/Project/hoge_app/fuga_app/.ruby-version)

先ほどまではhoge_app/.ruby-versionを参照していたのに対し、localの指定を行ったことでhoge_app/fuga_app/.ruby-versionが生成され、そちらを読み込んで2.5系が使えるようになった。

Engine側でgemのインストール

今回、そもそも2.5系にバージョンアップしたかったのは、特定のgemのサポートが2.5以上で、そのgemをどうしても使う必要があるという結論に至ったからだった。

要するに、「Ruby2.5系でsupportedのgemがインストールできて、そのgemがRailsEngine内でだけでも動いて欲しい」というのが本来の目的。

Engine側のfuga_appディレクトリには通常のrailsアプリケーションのツリーが同様に存在しているので、fuga_appのgemfileにインストールしたいgemを追加して、fuga_appディレクトリでbundle installを実行すれば良い。

hoge_app/fuga_app/Gemfile

gemspec

↑Engine側のGemfileではgemspecというファイルを読み込んでいて、インストールしたいgemはgemspecに追記する

hoge_app/fuga_app/fuga_app.gemspec

s.add_dependency "rails", "~> 5.1.5"
s.add_dependency "google-ads-googleads"

s.add_development_dependency "sqlite3"

今回追加したいgemは「google-ads-googleads」。

テスト環境にのみインストールしたい場合はadd_development_dependency、そうでない場合はadd_dependencyで追記する。

これでfuga_appディレクトリでbundle installを実行するとGemfile.lockが生成され

google-ads-googleads (9.0.0)

インストールが実行できた。

大元のアプリケーション側でgemのインストール

ドキュメント等には書いていなかったのだけど、Engine側のディレクトリでgemを追加しbundle installを実行したら、大元のアプリ側でもbundle installを実行しなければいけない。

しなければいけないというか、bundle installしたタイミングでEngine側が依存しているgemを読み取って、大元のアプリ側のGemfile.lockに記載される。

ということで、大元のアプリ(hoge_app)に移動して

$ pwd
/Users/hako/Project/hoge_app/fuga_app
$ cd ../
$ bundle install

bundle installを実行したところ、

hoge_app/Gemfile.lock

PATH
  remote: fuga_app
  specs:
    fuga_app (0.1.0)
      google-ads-googleads
      rails (~> 5.1.5)
.
.
.
.
.
google-ads-googleads (3.2.0)

大元のアプリ側にはEngine側でlockされているものとは別バージョンがインストールされてしまった。

Ruby2.4.3でbundle installしているから当然といえば当然なような気もするけど、fuga_app側でlockされているから同じバージョンをインストールしてくれないかなとか、大元のアプリ側ではEngine側に入っているgemのインストールがスキップされるのかなとかいう淡い期待があった。。

bundlerの性質を考えると、特定のgemが依存するパッケージも合わせてinstallするはずであり、Engineの機能を使って作成したfuga_appも一つのgemとしてhoge_app/Gemfileに記載し読み込んでいるので、これもまた考えてみれば当然だった。

当然、大元のアプリ(hoge_app)で動いているサーバーではgoogle-ads-googleads (3.2.0)を使って動くことになるので、今回やりたかったことはできないという結論に至りました。

でも、どちらにせよhoge_app側でもインストールされるのであれば、fuga_appの中で使うgemだからといってわざわざfuga_app.gemspecから読み込む必要があるのか(パフォーマンス上何か違いがあるのか)というのは気になりました。

両方のディレクトリでbundle installしなくちゃいけないというのは面倒だし、Engine側のアプリも大元のアプリ側のサーバーで動いているとなると別途bundle installする必要すらないのでは?と思ったり。

単純に可読性のためというか、決まりとしてそういうことなんだろうとは思うけど。。

まとめ

  • RailsEngine側にインストールしたいgemはgemspecに追記
  • gemspecに追記したものはEngine側のディレクトリでbundle install
  • Engine内でインストールしたgemも大元のアプリ側にインストールされる

参考

Rails エンジン入門 - Railsガイド

bundle install | Bundler日本語ドキュメント | Ruby STUDIO

[Rails4/5]はじめてのMountable Engine - Qiita

Rails Engineを使ってAPIと管理画面を分離する - blog.daich.org