くりにっき

フルスタックキュアエンジニアです

gemを作る時に気をつけていること

公私含めて2年間でたぶん30個以上はgemを作ってますが、なんとなく体得はしたもののこういうことは誰も教えてくれなかった気がするので残しておきます

アンダースコアとハイフンを使い分ける

gemを作る第一歩は

bundle gem <作りたいgemの名前>

ってやると思いますが、単語区切りであればアンスコ、ネームスペースの区切りだったらハイフンを使います

アンダースコア区切り

$ bundle gem go_princess_precure
Creating gem 'go_princess_precure'...
      create  go_princess_precure/Gemfile
      create  go_princess_precure/.gitignore
      create  go_princess_precure/lib/go_princess_precure.rb
      create  go_princess_precure/lib/go_princess_precure/version.rb
      create  go_princess_precure/go_princess_precure.gemspec
      create  go_princess_precure/Rakefile
      create  go_princess_precure/README.md
      create  go_princess_precure/bin/console
      create  go_princess_precure/bin/setup
      create  go_princess_precure/.travis.yml
      create  go_princess_precure/.rspec
      create  go_princess_precure/spec/spec_helper.rb
      create  go_princess_precure/spec/go_princess_precure_spec.rb
Initializing git repo in /Users/sue445/tmp/go_princess_precure

go_princess_precureで1階層

ハイフン区切り

$ bundle gem go-princess-precure
Creating gem 'go-princess-precure'...
      create  go-princess-precure/Gemfile
      create  go-princess-precure/.gitignore
      create  go-princess-precure/lib/go/princess/precure.rb
      create  go-princess-precure/lib/go/princess/precure/version.rb
      create  go-princess-precure/go-princess-precure.gemspec
      create  go-princess-precure/Rakefile
      create  go-princess-precure/README.md
      create  go-princess-precure/bin/console
      create  go-princess-precure/bin/setup
      create  go-princess-precure/.travis.yml
      create  go-princess-precure/.rspec
      create  go-princess-precure/spec/spec_helper.rb
      create  go-princess-precure/spec/go/princess/precure_spec.rb
Initializing git repo in /Users/sue445/tmp/go-princess-precure

go, princess, precureで計3階層

分かりやすい説明

ハイフンとアンダースコアの使い分け - 暁 [stfuawsc]

runtime dependency と development dependencyは使い分ける

こういうの

  spec.add_dependency "activesupport", ">= 4.0.0"

  spec.add_development_dependency "rspec"

add_dependencyadd_runtime_dependency へのaliasなので同じもの)

  • gemの実行に必要なものは add_dependency に書く(runtime dependency
    • 例:activesupport, activerecordなど
    • 何をするgemなのかによってruntime dependencyは変わる。( 他のgemに依存していなければadd_dependency が無いこともある)
  • そのgemの開発だけに必要なものは add_development_dependency に書く (development dependency
    • 例:rspec, pry など
    • 別のgemを作っていてもテストやデバッグ系のgemはだいたい同じになる

依存性は最小限にする(gem)

例えばactivesupportの機能を使いたくてgemspecに

# bad example
spec.add_dependency "rails"

と書いていると他のフレームワークでは使えないのかなと思ってしまうので

# good example
spec.add_dependency "activesupport"

のように本当に必要なgemのみを書いた方がいいです

依存性は最小限にする(バージョン)

# bad example
spec.add_dependency "activesupport", "4.2.1"

みたいにバージョンを固定してしまうとアプリ側で他のバージョンと組み合わせて使うことができないので、add_dependency に関しては基本的にはバージョンは書かなくていいと思います。*1

# good example
spec.add_dependency "activesupport"

また、「このバージョンで追加された機能に依存しているんだ!」という場合にはバージョンの下限を書くといいです

# good example
spec.add_dependency "activesupport", ">= 4.2.0"

テストを書く

OSSとして公開するのであれば可能な限りユニットテストは必須だと思います。*2

理由:gemの正しい振る舞いを他の人が確認できるようにするため

  • 人のリポジトリをforkして機能を追加したい時に、テストが無いと自分の修正でgemの既存の機能を壊してしまう(デグレ)のではないかと不安になります
  • 全くテストがないリポジトリに対して整備されたPullRequestしてくれるって可能性も0ではないですが、PullRequest送ってくれる人がよっぽどのテストマニア*3じゃないとやってくれないと思います。
  • ユニットテストという形で手軽に動作確認できる環境を用意しておくのは個人的には「おもてなし」だと思います

デファクトスタンダードに従う

さっきの「テストを書く」とも関連。

  • specディレクトリやtestディレクトリにテストコードがある
  • rake test を叩けばテストを実行できる
  • binディレクトリの実行ファイルを --help をつけて実行すればコマンド一覧が表示される

よくある構成ですが、こういうよくある構成にすることで他の人がforkした時の学習コストが抑えられます。

以前見たとあるリポジトリだと、exampleディレクトリの中に動作確認用のスクリプトがあってそれを実行することで目視で動作確認を行っている形跡がありましたが、こういう特殊な構成だと誰もforkできないのではないかと思います・・・w

エントリ冒頭で紹介した bundle gem コマンドで作っていればだいたいデファクトスタンダートな構成になるので、 bundle gem コマンドを使ってgemを作ることが「デファクトスタンダートに従う」の第一歩だと思います。(うまくまとまった)

*1:本当は下限を指定した方がいいのかもしれないけど、古いバージョンまでさかのぼって動作確認するのは大変なので下限を書くのは問題が起こった時でいいと思う

*2:社内gemとかで利用者全員でドッグフーディングできるのであればテスト無くてもいいのではと思うw

*3:例:自分