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を使いたい場合はこれ参照
よく使うカスタマイズ
exclude_tests
, 'exclude_fixtures' , 'exclude_factories' をtrueにする : specやfactoryにはannotateしないformat_markdown
をtrueにする : markdownにしておけばgithubのwikiとかにそのまま貼り付けられるのがよい
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の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 を使えばよろし。
公式wikiの Getting 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で紹介
GitHubのAPIを使ってごにょごにょするアプリの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
ブコメレス
不安定なテストはすぐになおしてくれ
アッハイ
全くもって正論なんですが、自分のメンテしてるライブラリだと下記の理由でrspec-retry使っています
- 再現性がない
- ローカルでは再現しない
- Jenkinsのビルドの傾向だと20分の1~30分の1くらいの確率でたまにテストがこけてる
- 1~2年前からこの問題に悩まされていてずっと調べているが原因不明
- エラーの内容的に古いデータが残ってUNIQUE INDEXの重複でエラーになってるようだが、テストケースの実行の度にDBを完全初期化してるので古いデータが残るというのは考えづらい
- 24000個のテストケースがあってたった1つのテストが偶然こけただけでビルドが全部失敗扱いになるのはきつい
- ビルド全部まわすのに10分かかる
- テスト結果を見ればいつもの不安定なテストかどうかは分かるけど、それを判断するのに時間を割かれるくらいならリトライした方が早い
リトライ系のgemは使わないに越したことはないので最終手段だと思います