行動すれば次の現実

テック中心の個人ブログ

エンジニアは普段身につけるモノにもっと投資するべき

YOUTUBEやTwitterを見ているとリモートワークの機会が増えたこともあり、自宅の作業環境をアップグレードする人が多いように感じます。

PC、デスク、椅子、照明などに投資していくのが一般的だと思うのですが、実はそれと同じくらい普段身につけているアイテムにも投資したほうが良いと筆者は考えます。

身につけるモノをアップグレードしたことで生産性が上がったと実感しています。

リモートワーク歴4年の私が、オススメしたい生活用品を4種類紹介したいと思います。

メガネ

目が悪い人は一日中メガネを付けているのではないでしょうか。 特にリモートワークだと一日の中でメガネを付ける時間がかなり長くなるので、メガネは真っ先に投資するアイテムの一つです。

  • 身に付けていて疲れないか
  • ブルーライトカット仕様であるか
  • 軽量なフレームか
  • デザインが古くないか

という観点で選定すると良いでしょう。

ちなみに筆者は眼鏡市場で購入したこちらのメガネにブルーライトカット加工オプションを付けて使用しています。 流行りのボストン型で、しかも軽量、価格も絶妙ですのでオススメです。

www.meganeichiba.jp

部屋着

部屋着もかなり重要です。寝間着のままスウェットやパジャマで作業するのも良いのですが、服を着替えて仕事モードに心を切り替えてることが大事だと考えます。

  • 作業するのにストレスが感じないか
  • 着脱がしやすいか
  • 温度調整が可能か
  • そのままの格好で外に出ても平気か

という観点で選定すると良いでしょう。

特に「そのままの格好で外に出ても平気か」というのは地味に重要です。気軽に外出できることで時間短縮になりますし、突然の配達などで人に合う場合も恥ずかしくないというメリットがあります。

筆者のオススメ部屋着は、冬場であればユニクロの「ファーリーフリースフルジップジャケット」は欠かせません。

インナーには無印の「脇に縫い目がない 天竺編みTシャツ」などのオーガニックコットン素材のモノがオススメです。

スボンは動きやすさや通気性を重視すると良いと思います。 「ウルトラストレッチドライスウェットパンツ」などがオススメです。

ルームシューズ(スリッパ)

スリッパは履かないという人もいるかもしれませんが、冬場であればスリッパの使用をおすすめします。 座って作業するのが中心になると足への血流が悪くなり、足裏が冷えてしまいます。 スリッパを履くと足が冷えることを防げますので、集中力が向上します。

素材によって好き嫌いがあると思うので、自分の好みで選ぶと良いでしょう。

ちなみに筆者はニトリで購入した「あったかスリッパ」を使用しています。

タンブラー

アイスコーヒーやホットコーヒーを飲んでいてすぐにぬるくなってしまうのは非常に残念です。 いつでも適温な状態で飲み物を口にできるとリッラクス効果が高まり、生産性も上がるのではないでしょうか。

  • 保温性が高い
  • 倒れてもこぼれない
  • デザインがシンプル

という観点で選定すると良いでしょう。

例えばサーモスのタンブラーなどがオススメです。

www.amazon.co.jp

その際、タンブラーの蓋も同時に購入するのは必須です。 保温性が高まりますし、転倒した場合の被害もある程度防げますので買って損はありません。

タンブラー用フタ(S)/JDA Lid(S) | マグカップ・タンブラー・ジョッキ・保冷缶ホルダー・食器 | サーモス 魔法びんのパイオニア

良いエンドポイントとは何か

良いエンドポイントを意識すると使用者にとって理解しやすく、使いやすいAPIを提供できます。

また、実装者にとってもメンテナンスしやすく、不具合の温床を防ぐことにも繋がります。 そのようなAPIを実装するためには、「どのようなエンドポイントが良いのか」を理解する必要があります。

短くて入力しやすい

短くて入力しやすいということはシンプルで覚えやすいことに繋がります。使用者にとって使いやすいAPIと言えるでしょう。

x https://api.example.com/service/api/search
o https://api.example.com/search

人間が読んで理解できる

そのURLを見ただけで、それ以外の情報がなくてもそれが何を目的としたものなのかがある程度わかるようにしておくことが良いでしょう。 そのためには単語を無闇矢鱈に省略したりしないことです。

x https://api.example.com/p/12345
o https://api.example.com/products/12345

大文字小文字が混在していない

大文字小文字が混在するとAPIがわかりづらくなり、間違いのもとに繋がります。デファクトスタンダードである小文字に統一することが良いでしょう。

x https://api.example.com/USERS/12345
o https://api.example.com/users/12345

サーバー側のアーキテクチャが反映されていない

サーバー側がどのようなアーキテクチャを使用してアプリケーションを実装しているかの情報は使用者には全く関係のない情報ですのでURIには反映させないほうが良いでしょう。

また、アーキテクチャが変更された際にURIも変更することになり、使用者・実装者ともに手間が増えてしまいます。

x https://api.example.com/cgi-bin/get_user.php?user=100

ルールが統一されている

URIの構造などのルールが統一されていないと、使用者にとっては誤解を招きやすくトラブルの温床となってしまいます。

例えばid情報をクエリパラメータに載せるのか、URIに載せるのか、単数形なのか、複数形なのかなどのルールを統一させておく必要があります。

# ルールが統一されていない例
https://api.example.com/products?id=100
https://api.example.com/products/100/reservation

ActionMailerでOpenSSL::SSL::SSLErrorが発生してメールが送れない

ActionMailerで別ドメインのSMTPサーバー経由でメール送信しようとしたらOpenSSL::SSL::SSLErrorが発生してエラーになりました。

※接続情報等は架空の値になっております。

ERROR -- : OpenSSL::SSL::SSLError
ERROR -- : hostname "smtp.example.com" does not match the server certificate

ActionMailer側の設定は以下の通りです。

config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  user_name: 'test@example.com',
  password: 'hogehoge',
  address: 'smtp.example.com',
  domain: 'smtp.example.com',
  authentication: :plain,
  port: 587,
}

エラーの内容は「証明書と送信元ドメインが一致しない」ということです。

別ドメインからのメール送信ですので、本来はDKIM認証/SPF設定をするのが望ましいのですが、 今回はシステム要件の都合上、別の方法を試したいと思います。

証明書チェックを省いて強引にメールを送信する

今回は強引に送りたいので、STARTTLSを有効にしつつ不正な証明書を受けるけるように設定する方法を取ります。

config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  user_name: 'test@example.com',
  password: 'hogehoge',
  address: 'smtp.example.com',
  domain: 'smtp.example.com',
  authentication: :plain,
  enable_starttls_auto: true, # STARTTLSに自動接続する
  openssl_verify_mode: 'none', # 証明書の妥当性はチェックしない
  port: 587
}

authenticationがloginかplainはSMTPサーバーによって違いますので、それぞれで試して使用できる方を採用します。

それ以外の接続エラー

上記の設定をしてもサーバーによっては別のエラーで送信できないことがありました。

No route to host - connect(2) for "smtp.example.com" port 587

「宛先ホストへの送信経路が存在しない」という内容ですので、ファイアウォールで弾かれている可能性が高いです。

$ telnet smtp.example.com 587
telnet: Unable to connect to remote host: No route to host

telnet等で疎通確認をして、接続出来ない場合は宛先ホストのファイアウォール設定を見直すことで解決するかと思います。

国内レンタルサーバーやVPSを使用している場合、海外IPからの接続が拒否されているケースがありますので、全て許可するようにするか、ホワイトリストで対応するなどの考慮が必要になります。

参考

rails/configuring.md at 481343ed91a66a112f7d929a73214f42ffb38dfc · rails/rails · GitHub

ruby - rails email error - 530-5.5.1 Authentication Required. - Stack Overflow

Sidekiqで実装したWorkerをRSpecでテストする

Sidekiqで実装したWorkerクラスのRSpecでテストする方法をまとめました。

SidekiqをRSpecでテストする方法

Sidekiqのテスト手法には主に以下の2種類があります。

① Sidekiq::Testing.fake!を使用したテスト

キューへのジョブ登録(エンキュー)から、ジョブの実行までの流れをテストしたい場合などに適しています。 非同期処理を見立てたテストケースを実行することができます。

② Sidekiq::Testing.inline!を使用したテスト

キューには登録せずにジョブを即時実行します。 同期処理のような動きになりますので、ジョブを実行した結果をassertしたい場合などに適しています。

Sidekiq::Testing.fake!のテストコード

Sidekiq::Testing.fake!はジョブをエンキューする際に、redisを使用するのはなく、redisに見立てた仮想的なキューとして配列(Array)を使用します。

# ワンライナーでの記法
expect {
  SampleWorker.perform_async(10)
}.to change(SampleWorker.jobs, :size).by(1)

# 上記と同じ内容のテストです
assert_equal 0, SampleWorker.jobs.size
SampleWorker.perform_async(10)
assert_equal 1, SampleWorker.jobs.size

上記はジョブをエンキューするまでのテストになります。 ジョブ自体はまだ実行されていません。

drainでキューに積まれたジョブを実行することができます。

assert_equal 0, SampleWorker.jobs.size
SampleWorker.perform_async(10)
assert_equal 1, SampleWorker.jobs.size
SampleWorker.drain # ジョブが実行されます
assert_equal 0, SampleWorker.jobs.size

SampleWorkerが実際に実行されますので、必要に応じてテストデータを用意したりmock化したりする必要があります。

Sidekiq::Testing.fake!を使用していると、キューに未実行のジョブが残ってしまうことがあります。 そのためテスト毎に下記のようにキューをクリアする必要があります。

Sidekiq::Worker.clear_all

以上を踏まえると、RSpecの全体像はこのような形になるかと思います。

require 'rails_helper'
require 'sidekiq/testing'

RSpec.describe SampleWorker, type: :worker do
  describe 'testing worker' do
    before do
      Sidekiq::Worker.clear_all
    end
    example 'エンキューされたジョブが実行されること' do
      Sidekiq::Testing.fake!
      expect { SampleWorker.perform_async }.to change { SampleWorker.jobs.size }.by(1)
      SampleWorker.drain # ジョブの実行
      assert_equal 0, SampleWorker.jobs.size
      # 実行結果の確認などあれば適宜記述
    end
  end
end

Sidekiq::Testing.inline!のテストコード

Sidekiq::Testing.inline!はキューには登録しないため即座に実行されます。

require 'sidekiq/testing'
require 'rails_helper'

RSpec.describe SampleWorker, type: :worker do
  describe 'testing worker' do
    example 'ジョブの結果が正しいこと' do
      Sidekiq::Testing.inline! do
        SampleWorker.perform_async
        # ここに想定結果を記述します。
      end
    end
  end
end

同期処理のような振る舞いになりますので、ジョブが実行された結果をassertしたい場合などに適しています。

参考

Testing · mperham/sidekiq Wiki · GitHub

Turbolinksとcookieは相性が悪い

Javascriptでcookieに値を設定する処理を実装していたのですが、タイミングによって同じcookie名で重複してvalueが登録される事案が発生しました。 調査するとどうやらTurbolinksが原因であることがわかりました。

Turbolinksを有効にするとcookieのpathに適切な値が設定されないことがある

/customersというページで以下のようなJavascriptを実行していたとします。

<script type="text/javascript">
  $(function () {
    document.cookie = 'key=value'
  });
</script>

Developerツールで確認するとCookieは以下のように設定されます。

Name:   key
Value:  value
Domain: localhost
Path:   /

その後、/customers/newというページにアクセスした後に、画面内に配置されたリンクから/customersに遷移するとどうなるでしょうか?

先程と同じ結果になるかと思いきや、Cookieの中身は以下のように設定されていました。

Name:   key
Value:  value
Domain: localhost
Path:   /

# 異なるpathでcookieが重複登録されてしまう
Name:   key
Value:  value
Domain: localhost
Path:   /customers/new

これは、Turbolinksによる画面遷移時には、Cookieに設定するpathが前の画面URLのままであることが原因です。 そのため、Turbolinksの有効時はcookieにpathを設定する必要があります。

<script type="text/javascript">
  $(function () {
    document.cookie = 'key=value; path=/'
  });
</script>

このようにすることで異なるpathでcookeが重複登録されてしまうという事態を避けることができます。

HerokuアプリのStackを18から20にアップグレード

Herokuのstack-18からstack-20へのアップグレードしました。その際に行ったことをまとめましたので、ぜひ参考にしてみてください。

stackのアップグレードとは?

stackのアップグレードとはOSのバージョンアップのようなものです。 stackとは、Herokuで使用されるOSのイメージのことであり、Ubuntuなどの既存のオープンソースのLinux ディストリビューションが基になっています。

stackをアップグレードする前に必ずテストしよう

いきなりstackをアップグレードするのではなく、以下のいずれかの方法で動作確認してからアップグレードすることをオススメします。 私はステージングでアップグレードして動作確認を行いました。

  1. 既存のステージング環境または開発環境でアップグレードする
  2. 既存アプリを複製したテスト用アプリを作成してアップグレードする

アップグレード手順

1.アプリのstackにheroku-20を設定します。次のデプロイから適用されるようになります。

$ heroku stack:set heroku-20 -a <アプリ名>

2.デプロイ用の空コミットを作成します。

$ git commit --allow-empty -m "Upgrading to heroku-20"

3.デプロイします。

$ git push heroku master

stackをアップグレードしたら動作確認も忘れずに

アップグレード後は全ての機能が正常に動作するか一通り動作確認しましょう。

万が一、問題が発生した場合は以下の手順でロールバックを行います。

$ heroku releases -a <アプリ名>
v335  Deploy 0040f033
v334  Deploy 6448f534
v333  Deploy e424e234

以前のリリースバージョンを指定してロールバックします

heroku rollback v335

参考

Upgrading to the Latest Stack | Heroku Dev Center

Rubyのヒアドキュメント記法

一番ノーマルなヒアドキュメント

終端を示す識別子にはスペースなどの文字列を記述してはいけないので、階層が深いと見づらく感じることがあります。

query = <<SQL
  select *
    from users;
SQL

p query #=> "  select *\n    from users;\n"
=> 

階層が深い場合のヒアドキュメント

識別子の頭にハイフン「-」をつけることで終端を行頭に置くことが可能です。

def get_user
  <<-SQL
    select *
      from users;
  SQL
end

p get_user #=> "  select *\n    from users;\n"