sass-railsの5.0から、foo.css.sassfoo.sassに変えよというWARNINGが出るようになってました。(エラーが出たらとりあえずGoogle検索欄に放り込む人=俺用のエントリータイトル)
DEPRECATION WARNING: Extra .css in SASS file is unnecessary. Rename /Users/komagata/code/kowabana/app/assets/stylesheets/blocks/footer/_footer-pages-nav.css.sass to /Users/komagata/code/kowabana/app/assets/stylesheets/blocks/footer/_footer-pages-nav.sass
...
...
...

デザイナーの作業とコンフリクトする予感!

% ls app/assets/stylesheets/**/*.css.sass | wc -l
     107

すげーたくさんあってやんなりますよぉ~

% zmv app/assets/stylesheets/**/*.css.sass app/assets/stylesheets/**/*.sass
% zmv app/assets/stylesheets/*.css.sass app/assets/stylesheets/*.sass
% git add 'app/assets/stylesheets/**/*.sass'
% git add 'app/assets/stylesheets/*.sass'
% git rm 'app/assets/stylesheets/**/*.css.sass'
% git rm 'app/assets/stylesheets/*.css.sass'

zmv使うとちょっと楽。

参照:zsh の zmv を使って簡単に複数ファイルを一括リネームする - mollifier delta blog

こちらの記事の解決編です。

ActiveRecord::RecordNotFoundのエラーは放置すべきか? - komagata

@hiroshi3110さんからズバリな答えをいただきました。

怖話で実装

# lib/record_not_found_by_trustless_param.rb:
class RecordNotFoundByTrustlessParam < StandardError; end
# app/controllers/comics_controller.rb:
class ComicsController < ApplicationController
  before_action :set_comic, only: :show
  
  def show
  end

  private
  def set_comic
    unless @comic = Comic.find_by(id: params[:id])
      raise RecordNotFoundByTrustlessParam                                                                                                                                                                  
    end
  end
end
# app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
  rescue_from RecordNotFoundByTrustlessParam, with: :not_found
  
  private
  def not_found
    render file: "#{Rails.root}/public/404.html", layout: false, status: 404
  end
end

@hiroshi3110さんの言うとおり、信頼出来ない外部からのidを元にしたfindで見つからない場合はRecordNotFoundByTrustlessParamにし、rescue_fromで拾って404を出すようにしました。(404を動的テンプレートで出すのは良くないと思うのでpublic以下の静的ファイルを読む)

自分の中のベストプラクティスにしようと思いました。

Rollbarとかのエラー管理サービス使ってるとActiveRecord::RecordNotFound がWARNINGレベルのエラーでたくさん残るので気になる。

idに適当な数字入れれば必ず出るんだから。rollbarの無料プランは月のエラー数に上限があるのでもったいない。

しかし、特定の存在しないidが何度もアクセスされるのは問題の兆候だったりするから完全にmuteするのも気が引ける。皆さんどうしてるんでしょう?

追記:

ActiveRecord::RecordNotFoundエラーを防ぐ - komagata

teardownも同じくどっちでも変わらないと思ってた。しかし、前者は上書きだけど後者は親クラスのsetupも呼んでくれるので後者の方が良い。例えば下記みたいにintegrationテストだけは全部js実行できるブラウザでテストしたいけどいちいち書いてられない場合。

# test/test_helper.rb:
class ActionDispatch::IntegrationTest
  setup do
    Capybara.current_driver = Capybara.javascript_driver
  end
end
# test/integration/post_comment_test.rb:
class PostCommentTest < ActionDispatch::IntegrationTest
  setup do
    sign_in('foo@example.com', 'password')
  end

  test 'post a comment' do
    (...)
  end
end

superが要らないのは気持ちいい。

単純にテストの後処理としてのサインアウトだったらcookieを消す方が速い。

page.driver.browser.clear_cookies

サインアウト機能のテストでよくある。visitメソッドだとgetしか飛ばせない。

実際にサインアウトリンクをクリックするのはサインアウトのテストだったらいいけど、他のテストの後処理として使う場合はすごく遅くなりそうで嫌だ

deviseには下記のような設定ができるらしいけど、これテストしてることになんの?って気がするので却下。

config.sign_out_via = Rails.env.test? ? :get : :delete

これで行けた。

page.driver.submit :delete, '/users/sign_out', {}

「assertありゃええねん」

前回のdecoratorのテストのassertをminitest-power_assertに変えてみた。
# Gemfile:
group :test do
  gem 'minitest-power_assert'
end

わざと間違えてみる。

# test/decorators/user_decorator_test.rb:
require 'test_helper'

class UserDecoratorTest < ActiveSupport::TestCase
  def setup
    ActiveDecorator::ViewContext.current = controller.view_context
    @user = ActiveDecorator::Decorator.instance.decorate users(:jean)
  end

  test 'full_name' do
    assert { @user.full_name == 'Hugh Jackman' }
  end
end
$ rake test test/decorators/user_decorator_test.rb
Run options: --seed 52969

# Running:

F

Finished in 0.134274s, 7.4475 runs/s, 7.4475 assertions/s.

  1) Failure:
UserDecoratorTest#test_full_name [/Users/komagata/code/active_decorator-test_unit/test/decorators/user_decorator_test.rb:10]:

    assert { @user.full_name == 'Hugh Jackman' }
             |     |         |
             |     |         false
             |     "Jean Valjean"
             #<User id: 758109964, first_name: "Jean", last_name: "Valjean", website: "http://jeanvaljean.com", created_at: "2014-07-01 15:35:12", updated_at: "2014-07-01 15:35:12">

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

こりゃええ!

active_decoratorがtest_unitでもrspecと同じくそのままじゃhelperを使ったdecoratorのtestが書けないんだけど下記のようにすれば動く。(active_decoratorのREADMEに書いてあるdecoratorのテストの例)

# test/test_helper.rb:
class ActiveSupport::TestCase
  include ActionView::TestCase::Behavior
end
# test/decorators/user_decorater_test.rb:
require 'test_helper'

class UserDecoratorTest < ActiveSupport::TestCase
  def setup
    ActiveDecorator::ViewContext.current = controller.view_context
    @user = ActiveDecorator::Decorator.instance.decorate users(:jean)
  end 

  test 'full_name' do
    assert_equal 'Jean Valjean', @user.full_name
  end

  test 'link' do
    assert_equal '<a href="http://jeanvaljean.com">Jean Valjean</a>', @user.link
  end
end
$ rake test test/decorators/user_decorator_test.rb
Run options: --seed 37850

# Running:

..

Finished in 0.032828s, 60.9236 runs/s, 60.9236 assertions/s.

2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

全部のコードはこちら

もっときれいな書き方があったらぜひ知りたいです。

direnvを入れると.envrcってファイル置いておくとそのディレクトリに移動したとき実行してくれる。

zimbatm/direnv

direnvのインストール

$ brew install direnv
$ vi ~/.zshenv
eval "$(direnv hook $0)"

railsプロジェクトに下記のファイルを置く。

$ vi .envrc
export PATH=$PWD/bin:$PATH

このディレクトリでのdirenv実行を許可する。

$ direnv allow .

怖話はminitestに書き換えつつあるんですが、Bizerでは他の開発者に気を使ってrspecを使っています。

Bizerをrails4.1に上げたので、「ヒャッハー、デフォルトでtravel_toが使えるからtimecop削除できるぜー!」と思ったのですがrspecじゃそのままじゃ使えない。(クソがぁ

Before:

it 'coupon is expired.' do
  # FIXME: travel_to in rails 4.1
  Timecop.travel(2014, 5, 10, 0, 0, 0) do
    expect(expired_coupon_user.free_by_coupon?).to be_false
  end
end

After:

# spec/spec_helper.rb:
RSpec.configure do |config|
  config.include ActiveSupport::Testing::TimeHelpers
end
it 'coupon is expired.' do
  travel_to Time.new(2014, 5, 10, 0, 0, 0) do
    expect(expired_coupon_user.free_by_coupon?).to be_false
  end
end

今までTime.nowDate.todayを内部で使うメソッドはテストしやすいように

def metrix(now = Time.now); ... end

みたいに書いてたんですが、デフォルトで気軽にtravel_toが使えるならやらなくていいかな!