こんにちは,@onk です。

これはドリコム Advent Calendar 2017 の8日目です。

7日目は桂田さんによる,『わがままボディを目指すマスターデータをなんとかしたい』です。

この記事ではドリコムにおける Gemfile, gems.rb の記載ルールについてまとめています。

元々はノールールでしたが,2014-04-28 に Idobata Gemfile プレゼント が公開されたことから輸入されました。輸入したのち,一部を社内向けに改訂しています。

では本題。

1. gem の並び順は ascii 順

新しく追加するgemをどこに入れるのか悩む時間を減らします。

独自ルール

例外は rails のみ。

1
if defined?(Rails)

require を切り替えている gem が複数あるため,一番上に書いて最初に読み込みます。

余談ですが、このルールを自動化しようと https://github.com/sue445/rubocop-gemfile@sue445 が作ったのとほぼ同時に RuboCop 本体にも Bundler/OrderedGems が入った のでした。(どちらも 2016-11 頭)

2. ライブラリのバージョンはできるだけ指定しない

バージョンを指定するのは、最新のバージョンでアプリケーションが動かなくなる場合のみ。
ライブラリ側を直す必要があるならPull Request。

バージョンを指定する際には、バージョンを指定した理由を添えることを心がけます
(とはいえ、Railsやらbootstrapのメジャーバージョンは指定しております triumph)。

独自ルール

コメントを付けなくても良い条件はバージョン間に差異があることの認知度なんだけど,例えば bootstrap v3 -> v4 にどれぐらいの差があるかは見る人の前提知識に依存してしまっているので「必ず」に振ります。

3. 複数 group に所属させる gem の記述

:test用の目的で使う意図で追加してるんだけど、:developmentでも動作の都合上必要なgemは
次のように、ハッシュで「サブグループ」的にグループを指定します。

1
2
3
4
5
group :test do
  ...
  gem 'rspec-rails', '~> 3.0.0.beta', group: 'development'
  ...
end

同様に:development用なのだけれども、:testでも必要になるものは:

1
2
3
4
5
group :development do
  ...
  gem 'tapp-awesome_print', group: 'test'
  ...
end

といった形式で指定しています。

4. source block

独自ルール

社内 gem は source block で指定する。

また,特定 group 向けの社内 gem は group のブロック内に source block を記述する。
group 指定を間違える方が影響大きいので,group を先に指定したい。

1
2
3
4
5
6
7
8
9
10
  gem "some-original-gem-for-runtime"
end
 
group :development do
  ...
  source "http://gem.example.com" do
    gem "some-original-gem-for-development", require: false
  end
end

これは rubygems.org と社内 gem とで同名 gem が存在してしまったときの対策です。ドリコムでは

のように 200 弱の社内 gem が存在しているので,年 1 回ぐらいは gem 名衝突の憂き目に遭います。

1
2
3
4
5
6
7
8
Warning: the gem 'foo' was found in multiple sources.
Installed from: https://rubygems.org/
Also found in:
You should add a source requirement to restrict this gem to your preferred source.
For example:
    gem 'foo', :source => 'https://rubygems.org/'
Then uninstall the gem 'foo' (or delete all bundled gems) and then install again.

と warning が出るので気づくようにはなっているはずですが,人力での検知は危険なので。

5. require

独自ルール

アプリケーションから使わない gem は require: false を付ける。

例えば capistranorubocopunicorn 等,主に bundle exec [command] で実行するものが該当。

  • 読み込まないようにして起動を少しでも早くするため
  • 想定外の挙動を減らすため

※そもそも group を分けてしまうのが良いのかもしれないと悩んでいる。

Gemfile サンプル

以上のルールで運用している Gemfile がこちらです。社内向けのあまり大きくない Rails プロダクトのものですが,参考にしてください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 
gem "rails", "5.1.3"
 
gem "activerecord-quiet_schema_version"
gem "activerecord-simple_index_name"
gem "acts_as_footprintable"
gem "ancestry"
gem "auditable"
gem "carrierwave"
gem "coffee-rails"
gem "counter_culture"
gem "dalli"
gem "devise"
gem "devise_ldap_authenticatable"
gem "diffy"
gem "elasticsearch-model"
gem "elasticsearch-rails"
gem "faraday_middleware"
gem "faraday_middleware-aws-signers-v4"
gem "fastimage"
gem "gemoji"
gem "global"
gem "gretel"
gem "jbuilder"
gem "jemalloc", require: false
gem "kaminari"
gem "komachi_heartbeat"
gem "mini_magick"
gem "mysql2"
gem "net-ldap"
gem "nokogiri"
gem "redcarpet"
gem "redis-namespace"
gem "sanitize"
gem "sass-rails"
gem "sentry-raven"
gem "shibaraku"
gem "sidekiq"
gem "sidekiq-cron"
gem "slim-rails"
gem "uglifier"
gem "unicorn"
 
# original gems
  # snip...
end
 
# bundled engines
gem "admin", path: "admin"
 
group :development do
  gem "annotate"
  gem "byebug", platform: :mri, group: :test
  gem "listen"
  gem "onkcop", require: false
  gem "pre-commit", require: false
  gem "pry", group: :test
  gem "pry-byebug", group: :test
  gem "pry-doc", group: :test
  gem "pry-rails", group: :test
  gem "rubocop", require: false
  gem "spring"
  gem "spring-watcher-listen"
  gem "web-console"
end
 
group :test do
  gem "factory_bot_rails", group: :development
  gem "faker"
  gem "rspec-rails", group: :development
  gem "rspec-request_describer"
  gem "webmock"
end
 
group :deploy do
  gem "capistrano", require: false
  gem "capistrano-npm", require: false
  gem "capistrano-rails", require: false
 
  source "http://gem.example.com/" do
    # snip...
  end
end

そうそう,deploy 用の group を切っている点ですが,これは真っ新な環境からデプロイしなければいけないハメに陥ったときに

1
bundle install --without default

を使う目的です。group に入っていない gem すら除外して deploy group のみインストールできるので,高速に cap コマンドを叩ける状態を作れます。

通常のフローではないということは非常時ですし,そんなときに nokogiri や libv8 のビルドを待つのはとても精神へのダメージが大きいので,こういった group を用意しておくと極々稀に役に立ちます。