コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

AWSにはコンテナやストレージなどの単位のサービス資料を検索できるページがある

小ネタ

紹介サイト

いいところ

「コンテナ」「データベース」など、ある程度ジャンルに分かれて資料がおいてあるので興味のあるものをつまみ食いしたいときに便利

関連リンク

Railsにてbefore_actionでredirect_toしてしまうとafter_actionなどは実行されない

actionに対しての実行なのでそのとおりといえばそのとおりなんですが備忘として。

環境

$ bin/rails --version
Rails 6.0.4.1

TL;DR

  • before_actionでredirect_toをするとafter_actionでは呼ばれない
  • 回避案はいくつかあるが、action内でredirect_toをするなどすれば回避できる

今回考えるケース

以下のように必ず出力してほしいロギングを after_action に設定し、特定の条件下でリダイレクトを行うようなケース

ケースに対してbefore_actionでredirect_toをし、after_actionでロギングする場合

コード

class RedirectTestsController < ApplicationController
  before_action :redirect_check, only: :show
  after_action :after_logging

  def index
    render json: { path: "index" }
  end

  def show
    render json: { id: params[:id] }
  end

  private

  def redirect_check
    if params[:id].to_i == 10
      logger.info("***** redirect *****")
      redirect_to "/redirect_tests/"
    end
  end

  def after_logging
    logger.info("***** must after_logging! time: #{Time.zone.now} *****")
  end

end

route.rbは以下

Rails.application.routes.draw do
  resources :redirect_tests
end

このケースにおける問題点

上記のコードで http://localhost:3000/redirect_tests/1 にアクセスすると以下のようなログが出る。

Started GET "/redirect_tests/1" for ::1 at 2022-04-23 22:46:31 +0900
Processing by RedirectTestsController#show as HTML
  Parameters: {"id"=>"1"}
***** must after_logging! time: 2022-04-23 13:46:31 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 335)

そして http://localhost:3000/redirect_tests/10 にアクセスすると、以下のようなログが出る

Started GET "/redirect_tests/10" for ::1 at 2022-04-23 22:48:05 +0900
Processing by RedirectTestsController#show as HTML
  Parameters: {"id"=>"10"}
***** redirect *****
Redirected to http://localhost:3000/redirect_tests/
Filter chain halted as :redirect_check rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms | Allocations: 308)


Started GET "/redirect_tests/" for ::1 at 2022-04-23 22:48:05 +0900
Processing by RedirectTestsController#index as HTML
***** must after_logging! time: 2022-04-23 13:48:05 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 317)

上記のようにbefore_actionでredirect処理が入ると、after_actionが実行されることがない。原理としては明確でactionの処理を実行することなく終わるため、actionが終わったあとに呼び出す after_action が実行されない。

もちろんaround_actioのaction実行後の処理も呼び出されない。

改善案

いくつか方法論はありますが、before_actionとafter_actionの使い方をなるべく踏襲する形だと「レンダリング処理をコールバックでやるのを避ける」というのがあるので、今回はそちらを改善案として紹介します。

アプローチとコード

before_actionでリダイレクト処理を挟むとactionに到達する前に違うURLに行ってしまうので、遷移を挟む処理はbefore_actionでさせないようにする

class RedirectTestsController < ApplicationController
  before_action :redirect_check, only: :show
  after_action :after_logging

  def index
    render json: { path: "index" }
  end

  def show
    redirect_to "/redirect_tests/" and return if @redirect_flag
    render json: { id: params[:id] }
  end

  private

  def redirect_check
    @redirect_flag = false
    if params[:id].to_i == 10
      logger.info("***** redirect flag on *****")
      @redirect_flag = true
    end
  end

  def after_logging
    logger.info("***** must after_logging! time: #{Time.zone.now} *****")
  end

end

このやり方で http://localhost:3000/redirect_tests/10 にアクセスすると、以下のようなログが出て、ロギングがリダイレクト時にもちゃんとでる。

Started GET "/redirect_tests/10" for ::1 at 2022-04-23 22:58:14 +0900
   (0.1ms)  SELECT sqlite_version(*)
Processing by RedirectTestsController#show as HTML
  Parameters: {"id"=>"10"}
***** redirect flag on *****
Redirected to http://localhost:3000/redirect_tests/
***** must after_logging! time: 2022-04-23 13:58:14 UTC *****
Completed 302 Found in 1ms (ActiveRecord: 0.0ms | Allocations: 512)


Started GET "/redirect_tests/" for ::1 at 2022-04-23 22:58:14 +0900
Processing by RedirectTestsController#index as HTML
***** must after_logging! time: 2022-04-23 13:58:14 UTC *****
Completed 200 OK in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 336)

参考リンク

変数名に省略形をつかっていいかの判断基準

自分なりの判断基準を整理したものです。

TL;DR

  • 「略語は必ず使ってはならない」と絶対的に言えるほど否定するものでもないので、チームで使い方を整理するのがベスト
  • その際に考えるべきことは以下
    • 狭い範囲でしか通じない略語は避けるべきだが、広く一般で使われている略語は問題ない
    • ただし略語が全プログラマには通じるとは限らないので気をつける
    • 一般的に使われる略語でも、コンテキストによっては誤読する略語になりうるのでその場合は利用を避ける

リーダブルコード曰く

リーダブルコードには以下のような文章がある。

ぼくたちの経験からすると、プロジェクト固有の省略形はダメだ。新しくプロジェクトに参加した人は、暗号みたいに見えて怖いと思うだろう。しばらくすると、それを書いた人ですら暗号みたいで怖いと思うようになる。 新しいチームメイトはその名前の意味を理解できるだろうか? 理解できるなら問題ない。 プログラマは、evaluationの代わりにevalを使う。documentの代わりにdocを使う。stringの代わりにstrを使う。だから、新しいチームメイトもFormatStr()の意味は理解できる。 - リーダブルコード 2章 名前に情報を埋め込む 頭文字と省略形

このように「プロジェクト固有の略語は避けるべき」ということと「一般的に知れ渡っている略語なら良い」ということが書かれている。

一般的に知れ渡っている略語

リーダブルコードでは「プログラマは、evaluationの代わりにevalを使う。documentの代わりにdocを使う。stringの代わりにstrを使う。だから、新しいチームメイトもFormatStr()の意味は理解できる。」とありその中で「プログラマは」かなり大きめの主語で書かれているが、ここではプログラマがドキュメントを読む上で読み違えのないような略語を指すと思われる。例えばある程度の量のドキュメントやソースコードに接してきたプログラマstrstranger とは誤読しないし、 varvariety と勘違いすることは少ないと思われる。

ただ、「connconnection だ」のような言葉のマッピングは経験値で培われるもので、網羅的な情報があるわけでもなく、コンテキストによっては全く知る機会がないこともあるので、前提として「知らない人がいるかもしれない」という心持ちでコードレビューなどには望んだほうが良いと思われる。

略語と誤読

一般的な略語であれば問題ないかと思われるが、例外として一般的な略語でも誤読が誘発されるものは避けるべきだと考える。

例えばプログラムの処理内容として「バラエティー番組の種類を選択させたい」というような内容を記述するときに varietyvariation という単語が登場する。そのときに一時的な変数名などに var を使うと、コードの作り方によっては「もしかしてバラエティー番組のことを指しているのか?」といらぬ懸念を抱かせてしまうので、このような状況下においては略語を避けて正しい言葉を当て込むほうがよいケースもあると思われる。

絶対的な線引はつくりにくい

前提が「一般的に知れ渡っているものなら略語でも大丈夫」としたいところだが、誤読を誘発するようなものなら避けるべきなので、何も考えない方向にいくなら「略語は原則使わない」だがそれも窮屈すぎる場面があると思われる。

そのため、やはりどの程度で抑えるかはそのコードに関わる人たちの認識次第なので、リーダブルコードの一節を起点に認識合わせをしてチームとしてどうあるべきかの方向性を話をすると良いのだと思います。

関連リンク

多くのブラウザはHTMLのheadにfaviconに関する記述がないと/favicon.icoに取得をしようとする

当たり前の話なんだけど、暗黙の仕様の様子なので結果をまとめる

TL;DR

  • どのブラウザもファビコンのデータが欲しいので明示的にない場合は取得しにいく
  • ブラウザごとにタイミングは異なるが、大体はページのHTMLを取得したあと
  • 最後にアクセスしたページをCookieに保存しようとすると、favicon.icoへのアクセスになる可能性があるので気をつける

favicon自体の挙動

2022年現在ではHTML Living Standardで、faviconの指定は <link rel="icon"> で指定できるようになっている(参考:faviconの仕様はHTML5から標準化されている - コード日進月歩

そのためブラウザに記述があればその指定されているパスのデータを取得する動作をする。

ただし、指定がない場合は / 配下にある favicon.ico を探し、ファイルがあればそれを使用する。これはfaviconの仕様の始祖であるIEがそういう動きだったことに由来すると思われる。(一種のデファクトスタンダード

詳しくは英語版のwikipediaにてまとめられているのでそちらを参照のこと。

faviconを取得するタイミング

前項で紹介したwikipediaにも記載があるが、多くのモダンブラウザでは「タブバー」もしくは「アドレスバー」にfaviconを出す仕様となっているので、ページを表示する場合にはほぼ確実にfaviconを取得する。そのため多くのブラウザでは閲覧するページに<link rel="icon">がない場合、次点として /favicon.ico にアクセスし表示できるデータがないかを探していく

気をつけないといけない「最後にアクセスしたページ」という観点

この場合気をつけないといけないのは「最後にアクセスしたページ」が favicon.ico になりうるということ。

昨今のWebアプリケーションは画像のアセットが別のドメインにあったりすることが多いので大体ページのgetリクエスト以降はアプリケーションサーバにアクセスすることが少なくなったので「最後にgetのあったリクエストを最後に閲覧したページとしてみなす」としても問題なく挙動してしまうシーンはあるが、このfavicon.icoが未指定の場合はfavicon.icoが最終アクセスページとして認識されてしまう可能性がある。

そもそも論Webブラウザの挙動を認識していれば最後にアクセスしたページ = 最後にgetリクエストがあったページという関連付けが成立しないことはわかるが、認識違いを起こすこともあるので気をつけたほうがいい。

関連リンク

Rails6でpublic配下を見れるようにする設定値はconfig.public_file_server.enabled

developmentでは見れたのに、productionじゃ見れないのはなぜ…となるときに見直す設定

環境

$ bin/rails -v
Rails 6.0.4.1

想定している状況

/public配下のファイルが、RAILS_ENVがdevelopmentだと見れて、productionのときに見れないときに考える設定

Railsガイド曰く

public/ディレクトリ内の静的アセットを配信するかどうかを指定します。デフォルトではtrueが設定されますが、production環境ではアプリケーションを実行するNginxやApacheなどのサーバーが静的アセットを扱う必要があるので、falseに設定されます。 - Rails アプリケーションを設定する - Railsガイド - 3.1.35 config.public_file_server.enabled

ソースコード上も初期値はtrue

どこで設定するのか

デフォルトのRailsであれば config/environmentsRAILS_ENV別の設定

productionに関しては以下のようになっている

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

関連リンク

GoogleChromeではhttpsやwwwを表示しないのは何故かをざっくり調べる

なぜ消えるのか、という話。

情報元

https://bugs.chromium.org/p/chromium/issues/detail?id=883038#c114

バグトラッキングに記載されていること

原文を転載すると以下

The Chrome team values the simplicity, usability, and security of UI surfaces. To make URLs easier to read and understand, and to remove distractions from the registrable domain, we will hide URL components that are irrelevant to most Chrome users. We plan to hide “httpsscheme and special-case subdomain “www” in Chrome omnibox on desktop and Android in M76.

Google翻訳を駆使して和訳すると以下

Chromeチームは、UI surfacesのシンプルさ、使いやすさ、セキュリティを高く評価しています。 URLを読みやすく理解しやすくし、登録可能なドメインから気を散らすものを取り除くために、ほとんどのChromeユーザーに関係のないURLコンポーネントを非表示にします。 デスクトップのChromeオムニボックスとM76のAndroidで「https」スキームと特殊なサブドメイン「www」を非表示にする予定です。

ということで、「https」などのURIスキーム部と「www」というサブドメインはユーザのために不要な情報なため消したという判断。

関連リンク

docker-composeのコンテナ名のデフォルト名は「プロジェクト名-サービス名-インデックス番号」

Version2からルールが変更になったので、そもそもどういう挙動か調べる

挙動確認したバージョン

$ docker-compose --version
Docker Compose version v2.3.3

TL;DR

  • docker-composeで立ち上がるコンテナ名を明示的に指定しない場合 {{プロジェクト名}}-{{サービス名}}-{{インデックス番号}} となる。
  • 「プロジェクト名」は docker-composeの起動オプションで指定可能だが、ない場合は COMPOSE_PROJECT_NAME環境変数が使われる
  • COMPOSE_PROJECT_NAME は格別指定がない場合は起動ディレクトリの文字列が設定される

コンテナ名決定を司るソースコード

デフォルトのコンテナ名は以下のロジックで書かれている

func getContainerName(projectName string, service types.ServiceConfig, number int) string {
    name := strings.Join([]string{projectName, service.Name, strconv.Itoa(number)}, Separator)
    if service.ContainerName != "" {
        name = service.ContainerName
    }
    return name
}

from https://github.com/docker/compose/blob/v2/pkg/compose/convergence.go

明示的に service.ContainerName があればそれが入るが、ない場合は Separator で「プロジェクト名」「サービス名」「引数の番号」となる。このとき引数の番号は1から自動的に採番されるようになっている。

プロジェクト名とは

プロジェクト名は docker-compose -p で指定できるもの。ヘルプでは以下のような記述がある。

-p, --project-name NAME Specify an alternate project name (default: directory name)

ディレクトリ名がデフォルトになるとのことだが、これは環境変数COMPOSE_PROJECT_NAME との兼ね合いで設定されているもので、以下のような説明が公式ドキュメントにある。

この変数を設定するのは任意です。 変数を設定しなかった場合 COMPOSE_PROJECT_NAME のデフォルトは、プロジェクトディレクトリの basename となります。 コマンドラインオプション の -p も参照してください。 - Compose CLI 環境変数 — Docker-docs-ja 19.03 ドキュメント

コンテナ名を指定したい場合

指定したい場合はドキュメントにあるとおり container-name を使うと指定できる。ただし重複したコンテナ名があると立ち上がらないので注意が必要。

container_name: sukina-container-mei

デフォルト名はdocker-composeのv2から変わった

デフォルトのコンテナ名だが、昔は区切りが - ではなく _ であったが、docker-composeのversinが2になるときに変更になっている。

define compose container names with a valid hostname by ndeloof · Pull Request #8655 · docker/compose

これはhostnameとしてコンテナ名が使われるため、hostnameは _ を許容していないのでhostnameとして正しくつかえるようにするための変更だった様子。

関連リンク