ようへいの日々精進XP

よかろうもん

2018 年 04 月 02 日(月)

ジョギング

  • 香椎浜 x 2 周
  • ほぼほぼ 30min を切るくらい
  • 足の痛みは少し落ち着いた感

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

ダイレックス

朝からダイレックスに買い物, お昼も夕飯の買い出しでダイレックス.

一日に二回も行くと顔も覚えられているんだろうなあ.

今日のるびぃ ~ 最近, awspec に送ったプルリクエスト ~

awspec に送ったプルリクエストの振り返りと, そこで得た知見をメモる.

fix have_lifecycle_rule #360

github.com

s3_bucket リソースの have_lifecycle_rule のテストを走らせると, fail しないという issue の対応.

github.com

主に, 以下のような対応を行った.

ライフサイクルルールの取得は, lifecycle_configuration_rules というプライベートメソッドで行われていて, 以下のような情報を has_lifecycle_rule? で処理することになる. 以下, ドキュメントより抜粋.

resp.rules #=> Array
resp.rules[0].expiration.date #=> Time
resp.rules[0].expiration.days #=> Integer
resp.rules[0].expiration.expired_object_delete_marker #=> Boolean
resp.rules[0].id #=> String
resp.rules[0].prefix #=> String
resp.rules[0].status #=> String, one of "Enabled", "Disabled"
resp.rules[0].transition.date #=> Time
resp.rules[0].transition.days #=> Integer
resp.rules[0].transition.storage_class #=> String, one of "GLACIER", "STANDARD_IA"
resp.rules[0].noncurrent_version_transition.noncurrent_days #=> Integer
resp.rules[0].noncurrent_version_transition.storage_class #=> String, one of "GLACIER", "STANDARD_IA"
resp.rules[0].noncurrent_version_expiration.noncurrent_days #=> Integer
resp.rules[0].abort_incomplete_multipart_upload.days_after_initiation #=> Integer

この処理を実装する際に, 配列に入っている Types::NoncurrentVersionTransition のような構造体を丸っと Enumerable#map と &:to_h を併用して hash 化してから hash 同士を比較するという小技を覚えた.

          if value.is_a?(Array)
            return false if r[key].map(&:to_h) != value
          end

当初は, r[key] を each で回してとそれぞれの要素を比較するようなコードを書いていたけど, 汚いよなーと 2 時間くらい悩んでいたら何かが降りてきた感じで &:to_h でシンプルに書き直すことが出来た.

あと, 手元でテストを回した時には, PASS するものの, Travis でテストを回すとドキュメントのテストで必ずコケるという現象に遭遇してしまったので, 改めて Fork し直してからプルリクエストした.

extend matcher for have_db_parameter_group and have_option_group #361

github.com

RDS の DB パラメータグループとオプショングループのステータス (in-sync or pending-reboot) をテストしたい, リクエストが社内から上がっていたので, 既存の have_db_parameter_group と have_option_group マッチャを拡張した.

Rspec のマッチャは RSpec::Matchers.define を利用して, 以下のように,独自のマッチャを定義出来るが, 今のところ, この書き方についてちゃんと理解出来ていない... すんません.

require 'rspec/expectations'

RSpec::Matchers.define :have_string do |expected|
  match do |actual|
    actual.include?(expected)
    if @num.nil?
      actual.include?(expected)
    else
      actual.split('-').count(expected) == @num
    end
  end

  chain :count do |num|
    @num = num
  end
end

RSpec.describe 'foo-bar-bar' do
  it { is_expected.to have_string('foo') }
end

RSpec.describe 'foo-bar-bar' do
  it { is_expected.to have_string('foo').count(1) }
end

RSpec.describe 'foo-bar-bar' do
  it { is_expected.to have_string('bar') }
end

RSpec.describe 'foo-bar-bar' do
  it { is_expected.to have_string('bar').count(2) }
end

ざっくりと解説すると...

  • RSpec::Matchers.define でカスタムマッチャを定義することが出来る
  • RSpec::Matchers.define に続く, シンボルで書かれている :have_string がマッチャの名前となる
  • match ブロック内で実際の値 (actual) とあるべき値 (expected) を比較する処理を書く
  • chain ブロックを利用することで, チェーンするメソッドを定義することが出来て, have_string('foo').count(1) みたいな書き方をすることが出来る

フムフム