RailsEngineを利用した、社内Gem（ライブラリ）開発方法

# RailsEngineを利用して、社内Gem開発方法

現在の業務委託先で受託開発をしています。

受託開発で請け負うアプリは似通ったアプリを開発するケースが多いです。

以前は受託開発案件はフルスクラッチで開発していたのですが、似通った共通の機能はRailsEngine※１で機能開発、共通化して社内Gem(ライブラリ）にすることで開発スピードを上げることになりました。

考え方としてはDHHのDRYと同じです。

コア機能部分を共通化してコンテンツの投入だけで簡単にアプリを開発できるようにしたイメージです。

※１, RailsEngineとはホストとなるRailsアプリケーションに機能を提供するミニチュア版のRailsアプリケーションです。RailsEngineで作成されたミニチュアアプリケーションをホストアプリケーションにインストールするだけでホストアプリケーションにコア機能を拡張させることが可能になります。

## RailsEngine（社内gem)の作り方

Rails Engineを作成するには、`rails plugin new < engine_name > --mountable`コマンドを打ちます。

※ ホストアプリケーション（RailsEngineを取り込む側）から完全に独立して利用した場合は`--mountableオプション`を付ける

実行すると下記のようなディレクトリが生成されます。

このディレクトリに必要なコア機能の実装を追加していきます。

```
<engine_name>
|
|-app-|-assets
|     |-controllers
|     |-modeles
|     |-views
|-bin
|-config
|-lib
|-spec-dummy-|-assets
             |-controllers
             |-modeles
             |-views
```

## RailsEngine（社内gem)を作るときのコツ

RailsEngineを作るときはコア機能を書き出すことが大事です。

例えば日記アプリを開発する場合はコアになる機能を書き出してみます。

```
日記を書く
日記を読む
SNSシェアする
```

このような機能はどの日記アプリでも必ず必要な機能なので、こういった部分をコア機能としてRailsEngineで共通化します。

```
日記を書く　-> note_readというRailsEngineを作成
日記を読む　-> note_writeというRailsEngineを作成
SNSシェアする　-> note_share_snsというRailsEngineを作成 
```

上記の３つのRailsEngineをホストアプリケーションにインストールするだけで日記アプリの基本実装は完成になります。

## RailsEngine（社内gem)の取り込み方

開発したRailsEngineはGithub Packageにpublishし、gemとして取り込めるようにします。

[GitHub Packages](https://github.co.jp/features/packages)とは、コンテナや依存関係を含むソフトウェアパッケージをホスティングして管理するためのプラットフォームサービスです。

RubyGemsやnpmパッケージなどのパッケージとしてホスティングすれば、アプリ側で依存関係として簡単に取り込めるようになります。

■ 対応しているパッケージクライアント例

|  パッケージクライアント  |  パッケージフォーマット  |
| ---- | ---- |
|  npm (Node)  |  package.json  |
|  gem (Ruby)  |  Gemfile  |
|  mvn (Java)  |  pom.xml  |
|  docker  |  container  |

実際にpublishしたあとは、gemファイルに記載してbundle installするだけで、他のgemと同様に取り込めます。

実際の利用法は[こちらの記事](https://qiita.com/jrsyo/items/a23ab67f23e0693f56a1)が参考になると思います。


## RailsEngine（社内gem)を利用する上でのポイント
RailsEngineを使う上でのポイントをまとめます。

### RailsEngineの設定はConfigで行う
RailsEngine側の設定はconfigで定義することが出来ます。

Config アクセッサというものが用意されていて、config.user_class_nameみたいな感じで、定義を受けつけることで設定を行うことが出来ます。

例）エンジン側（取り込まれる側）

``` ruby
class MyGem::Configuration
  include ActiveSupport::Configurable

  config_accessor(:foo) { "use a block to set default value" }
  config_accessor(:bar) # no default (nil)
end
```

例） ホストアプリ側（取り込む側）

``` ruby
MyGem.configure do |config|
  config.foo = 'この値を変えるとカスタマイズ可能 '
  config.bar = 'hogehoge'
end
```



### ホストアプリ側の拡張は、専用モジュールを用意する。

ホスト側でコア機能を少し改造したい場合は専用モジュールを作成して利用することでカスタマイズが可能になります。

例えば、RailsEngine側のMessageクラスにメソッドを追加したい場合は、そのメソッドを専用のモジュールのなかに定義して、includeする形で拡張します。

そうすることで、コア機能部分のコードを書き換えることなくホストアプリケーション側のみカスタマイズすることが可能になります。

例） コア機能側にMessageCustomizePointモジュールを追加しておき、ホストアプリ側でそのモジュールを変更するだけでカスタマイズできる。

``` ruby
class Message < ApplicationRecord
  include MessageCustomizePoint
end
```

`MessageCustomizePointモジュール`を取り込んだホストアプリケーション側で改造することで、挙動を修正することができます。

### 

## RailsEngine（社内gem)を利用するデメリット
RailsEngineを利用して感じた個人的なデメリットを記載します。

### 1,RailsEngineを変更した時の影響範囲が大きい

RailsEngineを変更したときに、そのRailsEngineをインストールしているすべてのホストアプリケーションに影響するので気軽に変更できません。

例えば、RailsEngine側にあるAPIのレスポンス構造体を変更すると、全てのホストアプリケーションのレスポンスが変更されます。

その結果、思わぬ不具合につながるケースがあるので注意が必要になります。

### 2.設計が難しい

コア機能を切り分けて、RailsEngine化するときにどの機能をコア機能にするかの設計をミスをすると他のEngineに依存してしまいます。

例えば、日記を書くコア機能の中にSNSをシェアするという実装があるとします。

SNSシェアの機能は別RailsEngineにある場合はそのEngineに依存してしまいます。

依存関係を増やしてしまうと、一つの変更の影響範囲が別のEngineにまで影響するので思わぬバグにつながるケースがあります。

### 3.コードリーディングが面倒
コア機能をそれぞれ別のRailsEngineで管理しており、それに伴いレポジトリも別で保存しています。

ですので、それぞれのRailsEngineの中身を確認するときにわざわざレポジトリを別途開く必要があります。

また、挙動を確認したいときは別に環境構築する必要があるのも面倒です。

## 参考サイト


https://qiita.com/kidach1/items/565c2c077ae8d15fe3a8

https://qiita.com/jrsyo/items/a23ab67f23e0693f56a1