くりにっき

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

Railsアプリやgem作る時にいつも入れているgem一覧

id:joker1007 さんに触発されました。

Ginza.rb 21回の発表資料。rails_adminのつらみとオススメgem達。

1年間で10個以上アプリやgemを作っている中でよく使うgemをまとめてみます

Railsアプリ(rails new した直後に必ず入れる)

annotate

https://github.com/ctran/annotate_models

modelのソースの先頭にテーブルのスキーマ情報を付加してくれるgem。いちいちschema.rbを見に行く必要がなくなるので超ベンリ

こんな感じ

# == Schema Information
#
# Table name: plugins
#
#  id          :integer          not null, primary key
#  name        :string
#  title       :string
#  version     :string
#  wiki_url    :string
#  released_at :datetime         not null
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#
# Indexes
#
#  index_plugins_on_name         (name) UNIQUE
#  index_plugins_on_released_at  (released_at)
#

class Plugin < ActiveRecord::Base

rails g annotate:install すると rake db:migrate 時に自動的にannotateするためのtaskが lib/tasks/auto_annotate_models.rake 作られます。

annotateし忘れは結構あるしデフォルト設定も保存できるのでよく利用してます

# NOTE: only doing this in development as some production environments (Heroku)
# NOTE: are sensitive to local FS writes, and besides -- it's just not proper
# NOTE: to have a dev-mode tool do its thing in production.
if Rails.env.development?
  task :set_annotation_options do
    # You can override any of these by setting an environment variable of the
    # same name.
    Annotate.set_defaults({
      'position_in_routes'   => "before",
      'position_in_class'    => "before",
      'position_in_test'     => "before",
      'position_in_fixture'  => "before",
      'position_in_factory'  => "before",
      'show_indexes'         => "true",
      'simple_indexes'       => "false",
      'model_dir'            => "app/models",
      'include_version'      => "false",
      'require'              => "",
      'exclude_tests'        => "false",
      'exclude_fixtures'     => "false",
      'exclude_factories'    => "false",
      'ignore_model_sub_dir' => "false",
      'skip_on_db_migrate'   => "false",
      'format_bare'          => "true",
      'format_rdoc'          => "false",
      'format_markdown'      => "false",
      'sort'                 => "false",
      'force'                => "false",
      'trace'                => "false",
    })
  end

  Annotate.load_tasks
end

rails g annotate:installで作られるファイルは文字列key, 文字列のtrue, falseですが実際はシンボルkeyやbooleanのtrue/falseも使えます

    Annotate.set_defaults(
      position_in_routes:   "before",
      position_in_class:    "before",
      position_in_test:     "before",
      position_in_fixture:  "before",
      position_in_factory:  "before",
      show_indexes:         true,
      simple_indexes:       false,
      model_dir:            "app/models",
      include_version:      false,
      require:              "",
      exclude_tests:        true,
      exclude_fixtures:     true,
      exclude_factories:    true,
      ignore_model_sub_dir: false,
      skip_on_db_migrate:   false,
      format_bare:          true,
      format_rdoc:          false,
      format_markdown:      true,
      sort:                 false,
      force:                false,
      trace:                false,
    )
  end

padrinoでannotateを使いたい場合はこれ参照

sue445.hatenablog.com

よく使うカスタマイズ

  • exclude_tests , 'exclude_fixtures' , 'exclude_factories' をtrueにする : specやfactoryにはannotateしない
  • format_markdown をtrueにする : markdownにしておけばgithubwikiとかにそのまま貼り付けられるのがよい

global

https://github.com/railsware/global

Railsでいい感じの設定ファイルを使うためのgem

とても素晴らしいgem便利なんだけど「rails config」でググるrails_config の方が上に来るのが難点。あと、一般的な名詞すぎてググるのが大変なのでキラキラネームの必要性を感じる

普通にyamlで設定を書こうとすると

# config/host.yml
common: &common
  cache_memcache: '127.0.0.1:11211'
  session_memcache: '127.0.0.1:11211'
  redis:
    host: 127.0.0.1
    port: 6379

development:
  <<: * common

staging:
  <<: * common
  redis:
    host: xxx.xxx.xxx.xxx
    port: 6379

production:
  <<: * common
  redis:
    host: yyy.yyy.yyy.yyy
    port: 6379

になると思いますが、globalだと下記のようになります

# config/global/host.yml
default:
  cache_memcache: '127.0.0.1:11211'
  session_memcache: '127.0.0.1:11211'
  redis:
    host: 127.0.0.1
    port: 6379

staging:
  redis:
    host: xxx.xxx.xxx.xxx

production:
  redis:
    host: yyy.yyy.yyy.yyy

特徴1. 設定のメソッドライクなアクセス

上記の例だと Global.host.redis.hostみたいな感じで取得できます

特徴2. <<: * common が不要

yaml書く時によくやる

development:
  <<: * common

が不要。globalではdefault書いておくだけでよくなります。defaultを書いておけばstagingやproductionなどの各environmentも書かなくていいのですが、他の人が見た時に書き忘れだと思うのを防止するために空要素で書いておくことが多いです

特徴3. deep merge対応

# config/host.yml
common: &common
  cache_memcache: '127.0.0.1:11211'
  session_memcache: '127.0.0.1:11211'
  redis:
    host: 127.0.0.1
    port: 6379

staging:
  <<: * common
  redis:
    host: xxx.xxx.xxx.xxx
    # port: 6379  ←ここをうっかり書き忘れる

上記のような書き方だと普通のyamlだとredis(というHash)をそのまま上書きしているため、commonの {host: 127.0.0.1, port: 6379} に対して {host: xxx.xxx.xxx.xxx} を上書きしてportがなかったことにされてしまいます。

globalだとdefaultの {host: 127.0.0.1, port: 6379} に対して staging の {host: xxx.xxx.xxx.xxx} をdeep mergeするため {host: xxx.xxx.xxx.xxx, port: 6379}が取得できます

slim-rails

https://github.com/slim-template/slim-rails

viewのテンプレート

haml派かslim派か分かれますがslimの方が記述量が少ないのでslimを使ってます

Rails以外だと https://github.com/slim-template/slim の方使えばok

view_source_map

https://github.com/r7kamura/view_source_map

viewのソースにHTMLコメントでpartialのパスを埋め込んでくれるgem

view_source_map

viewのtypoとか修正したい時にどのソースを修正したらいいか一発で分かります

database_rewinder

https://github.com/amatsuda/database_rewinder

テストの時にDBを毎回クリアしてくれるgem

database_cleaner よりも高速なのが特徴

factory_girl_rails

https://github.com/thoughtbot/factory_girl_rails

テストデータを生成するためのgem

fabrication 派かfactory_girl派か分かれるところですが、僕は表現が柔軟なfactory_girl派です

Rails以外で使う場合は factory_girl を使えばよろし。

公式wikiGetting Started のサンプルを読めばだいたい使い方分かるはず

よくあるハマりどころとして group :test だけだと rails g model した時にfactoryが生成されません。

group :test do
  gem "factory_girl_rails"
end

RAILS_ENV省略時はdevelopmentなため。 ((RAILS_ENV=test rails g model すればfactoryも生成されますがRAILS_ENVをつけてrails gするのは見たことない。。。))

下記のように development もいれる必要があります

group :test, :development do
  gem "factory_girl_rails"
end

Railsアプリ(必要に応じていれる)

komachi_heartbeat

https://github.com/mitaku/komachi_heartbeat

Railsアプリを外部から監視するためのエンドポイントを提供するためのgem

会社だとZabbixからkomachi_heartbeatのエンドポイントをたたいて監視しています

その他Rackアプリやgem開発(真っ先に入れる)

rubocop + guard-rubocop

rubocopが静的解析のgemで、それをguardで自動実行するためのgemがguard-rubocop

社内だと最近この設定をベースにして開発してます

https://gist.github.com/onk/38bfbd78899d892e0e83

rspec-power_assert

https://github.com/joker1007/rspec-power_assert

rspecでpower assert使うためのgem。

it { should eq 10}

みたいなワンライナーshouldで書けないケースで、expectだと括弧が多くなるのでシンプルなpower_assert形式で書いています

it { expect(5 * 2).to eq 10 }

it_is_asserted_by { 5 * 2 == 10 }

faker

https://github.com/stympy/faker

テストデータ作成時にいい感じにランダムデータを作成するためのgem。factory_girlとセットで使うことが多いけど単独でも使える

プリキュアの名前でテストデータを生成したい場合には faker-precure をお使いください

pry-byebug

https://github.com/deivid-rodriguez/pry-byebug

デバッグには必須。 binding.pry で止めた後にnextやstepで1行ずつ実行することが多い

Twitter Bootstrap

デザインセンスがない僕のようなエンジニアでもそれなりのウェブアプリ作ることができる素晴らしいフレームワーク

sassで使いたいなら bootstrap-sass でlessがいいなら twitter-bootstrap-rails

bootstrap-sassはbootstrap本家がメンテしているので安心感がある

bootstrap_form

https://github.com/bootstrap-ruby/rails-bootstrap-forms

railsのform_forを使って bootstrap準拠のフォームを作ろうとすると

= form_for(@user) do |f|
  .form-group
    = f.label :password
    = f.password_field :password, class: "form-control", placeholder: "Password"

のようになり記述が冗長になりますが、bootstrap_formを使うと

= bootstrap_form_for(@user) do |f|
  = f.password_field :password, placeholder: "Password"

でよくなるので記述量がぐっと減ります

その他Rackアプリやgem開発(必要に応じていれる)

webmock

https://github.com/bblimke/webmock

httpの通信をmockにするためのgem

これを入れていると外部のAPI実行時などに明示的に通信を許可しないとエラーになるので、不要な通信を防ぐことができます。(破壊的なAPIをテストで実行すると一大事なので。。。)

timecop

https://github.com/travisjeffery/timecop

テスト実行時に現在時間を操作してタイムトラベルするためのgem

これもtimecop派と delorean派が分かれるところではありますが、deloreanだと Delorean.time_travel_to(3.days.ago) でジャンプした後にそのまま時間が進み続けますが、timecopだと Timecop.freeze(3.days.ago) で時間を完全に固定することができるので、現在時間に対して境界値テストをすることができるので僕はtimecop使うことが多いです。

個人的にはdeloreanの back_to_1985(タイムトラベルを解除)メソッドは好きなんだけどなーw

rspec-parameterized

https://github.com/tomykaira/rspec-parameterized

パラメタライズテストしたくなったら真っ先に入れるGroovyのspockみたいな形でパラメタライズテストできるのが特徴

rspec-its

https://github.com/rspec/rspec-its

rspec 2系時代のitsを3系でも使うためのgem。ワンライナーでテスト書く時にits重宝するんですよ

rspec-temp_dir

https://github.com/sue445/rspec-temp_dir

メソッドの実行結果が戻り値で返ってくる場合はテストも楽でですが、ファイルを出力するメソッドだと毎回テストデータを消す必要があり結構面倒です。

このgemを使えばテスト実行時に毎回tmpディレクトリを作成するのでそこにファイルを出力するようにすれば後始末のことは考えなくてよくなります(熱い自画自賛)

activerecord-import

https://github.com/zdennis/activerecord-import

大量のレコードをBULK INSERTする時の定番

activesupport

https://github.com/rails/rails/tree/master/activesupport

Railsの便利なユーティリティを使うためのgem

Railsアプリでもactivesupportの機能を使いたくなることはよくあることだと思います

rspec-retry

https://github.com/y310/rspec-retry

テストが失敗した時に何回かリトライするためのgem

たまに失敗する&リトライすれば成功するって場合に不安定なテストを直すまでのお茶濁しとして使うことが多いです

小ネタですが spec_helper.rb

def with_retry
  { retry: 3, retry_wait: 10 }
end

こういうメソッドを定義しておけば

it "不安定なテスト", with_retry do
end

みたいに書けてシンプルです。(テスト個別でリトライ回数やウェイト秒数を制御したいってことはあまりないはず)

おまけ:最近のお仕事をGemfileで紹介

GitHubAPIを使ってごにょごにょするアプリのGemfileです*1

source "https://rubygems.org"

ruby "2.2.1"

gem "rails", "4.2.0"

gem "mysql2", group: :mysql

gem "bootstrap-sass", "~> 3.3.3"
gem "cancancan"
gem "coffee-rails", "~> 4.1.0"
gem "devise"
gem "devise_ldap_authenticatable"
gem "global"
gem "jbuilder", "~> 2.0"
gem "jquery-rails"
gem "octokit"
gem "omniauth-github"
gem "bootstrap_form"
gem "sass-rails", "~> 5.0"
gem "slim-rails"
gem "uglifier", ">= 1.3.0"
gem "unicorn-rails"

group :sdoc do
  gem "sdoc", "~> 0.4.0"
end

group :development do
  gem "annotate"
  gem "byebug", group: :test
  gem "guard"
  gem "guard-rspec"
  gem "guard-rubocop"
  gem "guard-shell"
  gem "pry-byebug", group: :test
  gem "rubocop"
  gem "rubygems-socksproxy"
  gem "spring", group: :test
  gem "view_source_map"
  gem "web-console", "~> 2.0", group: :test
end

group :test do
  gem "database_rewinder"
  gem "factory_girl_rails", group: :development
  gem "faker"
  gem "faker-precure"
  gem "rspec-rails", group: :development
  gem "rspec-parameterized"
  gem "rspec-power_assert"
  gem "timecop"
  gem "webmock"
end

group :production do
end

ブコメレス

id:uemasaharaki

不安定なテストはすぐになおしてくれ

アッハイ

全くもって正論なんですが、自分のメンテしてるライブラリだと下記の理由でrspec-retry使っています

  • 再現性がない
    • ローカルでは再現しない
    • Jenkinsのビルドの傾向だと20分の1~30分の1くらいの確率でたまにテストがこけてる
  • 1~2年前からこの問題に悩まされていてずっと調べているが原因不明
    • エラーの内容的に古いデータが残ってUNIQUE INDEXの重複でエラーになってるようだが、テストケースの実行の度にDBを完全初期化してるので古いデータが残るというのは考えづらい
  • 24000個のテストケースがあってたった1つのテストが偶然こけただけでビルドが全部失敗扱いになるのはきつい
    • ビルド全部まわすのに10分かかる
  • テスト結果を見ればいつもの不安定なテストかどうかは分かるけど、それを判断するのに時間を割かれるくらいならリトライした方が早い

リトライ系のgemは使わないに越したことはないので最終手段だと思います

*1:まだRails 4.2.1に上げていないのがバレる。。。