「学びを結果に変えるアウトプット大全」感想

www.amazon.co.jp

動機

  • アウトプットが全然できていないという悩み
  • ハードルを低くして習慣化したい。そのためのきっかけとなるような情報が欲しい

自分にとっての要点

  • アウトプット前提のインプットを行う
    • インプット/アウトプットの比率は 3:7 が目標
    • アウトプットをインプットの2倍にする
  • フィードバックも重要
    • フィードバック = アウトプットを評価して、次のインプットを調整し、より良いものにする
    • フィードバックを受けると言う目的でも「公開」した方が良い
    • 「誰にも読まれず、誰からも批判されない文章を書いても、文章が上達することはありません。「読まれる」という緊張感が、集中力を高め、より良い文章を書こうとする最高の刺激になります」
  • アウトプットのやり方
    • 全般
      • 期限を決めて集中して行う
        • 5分とか15分とか
        • 15分の隙間時間でアウトプット
      • 構造を決めてから書く -手段
      • 本や映画の感想をTwitter等で呟いて要約力を高める
      • 本の感想
        • before + 気づき + Todo のフォーマットが書きやすい
          • 自分だと、 動機 + 自分にとっての要点 + 気づき + Todo がしっくりくるかも
        • ブログに書くか。この記事みたいな感じで。
      • 日記
        • ポジティブなこと(今日あった嬉しいこと、楽しいことなど)を書く
        • 3行5分とかで良い
  • その他
    • ちょっと頑張ればできそうなことを繰り返す
      • これはアウトプットそのものの習慣化に関しても同様
    • イデア出しのための100均(に売ってる)カード

気づき

  • 「アウトプットを習慣化する」こと自体を目標にしてみる
    • どうすれば楽しく続けられるか?が重要
    • 今までうまくいっていなかった理由は多分…
      • 無理をしていた。良いものを出そうとしていた。
      • ジャンルを技術に絞っていた(他にもアウトプットできることはある)
      • 「アウトプットを前提とする」インプットをしていなかった
  • 極力「公開」しよう
    • このハードルをいかに下げるか?が自分にとっては重要
    • これはTwitterでハードル低く始める、というのが良いかな

Todo

  • 新しくTwitterのアカウントを作る
  • アウトプットを習慣化するために以下のうち、どれか1つを達成する。
    • Tweet1日1回
      • 公開アウトプットのハードルを下げることが目的
    • 3行日記を週3回書く
      • ポジティブ&健康のやつ
    • ブログ1ヶ月に1回更新
      • 読んだ本、Youtube、参加したイベント、とかの感想
      • コーヒーとか登山とかでも良い気がする

Rails7 + tailwindcss-railsの環境で、privateなgithubリポジトリ上の自作gemをGemfile上で参照した場合、bin/devからのアプリケーション起動で'xxx is not yet checked out. Please run `bundle install`' のエラー

かなり謎な内容なのだが...

タイトルの通り Rails7 + tailwindcss-railsの環境で、
privateなgithubリポジトリ上の自作gemをGemfile上で参照した場合に、
bin/dev(foreman)からのアプリケーション起動が 'xxx is not yet checked out. Please run bundle install' というエラーが出て失敗する。

一応回避できたが全く理由は分からない。一度回避した後は再現もしなくなってしまった。 記録のためにやったことを記載しておく。

前提

Rails7 + tailwindcss-rails

Rails7でcssをtailwindを使用する場合 tailwindcss-rails gemの利用が推奨されている。

インストールすると rails serverとbin/rails tailwindcss:watch(tailwind cssの変更監視&再コンパイル用コマンド)を別プロセスで起動するために以下のような bin/dev が作成される。

開発環境においてはbin/devを用いてアプリケーションを立ち上げることになる。
(手動で2つ立ち上げても同じことではある)

# bin/dev
exec foreman start -f Procfile.dev "$@"

# Procfile.dev
web: bundle install; bin/rails server -p 3000
css: bundle install; bin/rails tailwindcss:watch

privateなgithubリポジトリの自作gemを利用

privateなgithubリポジトリのgemを利用する方法はいくつかあるが、ここでは簡易的にsshの利用することにする。 Gemfileに対して以下のように記載する。

gem 'your_gem_name', git: 'git@github.com:your_username/your_repo.git', branch: 'your_branch'
# 事前に自分のSSHキーをGitHubに登録しておく必要がある

参考 obel.hatenablog.jp qiita.com

現象

上記2つの前提を満たした上で

つまり

の状況において、

bin/dev を用いて Railsアプリケーション(& tailwind cssの変更監視&再コンパイル)を起動しようとすると以下のようなエラーが発生した。

https://xxxxxx@github.com/XXX/my_gem.git is not yet checked out. Please run `bundle install`

この事象は以下のような前提条件のもと起きている。

  • bundle installは実施済み
  • bin/rails s と tailwindcss:watch をterminal上で個別に実行すると問題なく起動する。

自分から見ると bundle install はちゃんと出来ているはずだが...? という感じ。

回避方法

少し調べてみたが...内容が特殊なせいか今ひとつ情報が出てこない。 ChatGPTに聞いても残念ながら的を射た回答は得られなかった(質問の仕方が悪いのかもしれないが...)

ただ エラーメッセージ的には foremanの環境とターミナルの環境が別になっているように感じられる。

そこで以下のようにProcfileを修正した上でbin/devを実行することで、現象を回避することができた。

web: bundle install; bin/rails server -p 3000
css: bundle install; bin/rails tailwindcss:watch

それぞれのコマンドの前にbundle installをつけることで、foremanの環境(?)においてもbundle installの実行を保証している。

なお、一度回避した後は、Procfileから bundle installを抜いても問題なく起動するようになった。 やはり何かしら環境は存在しているように見える。foremanについて調べればこの辺わかるのかな...

オブジェクト指向設計でよく使われるちょっとしたテクニックを自分の言葉で(from オブジェクト指向設計実践ガイド)

「オブジェクト指向設計実践ガイド」の最初の方をあらためて読む中で目についたテクニックを、自分なりの言葉で(あえて言葉だけで)表現してみる。大体意識せずやってるような内容だが、言葉だけで表現しようとすると理解の細かさが問われなかなか難しいものがある。

当然ながらこれらは有効な場面とそうでない場面がある。文脈に応じて適用するか否かは判断する必要はある。 あとこれだけ読んでも前提が無いのでなんのこっちゃかもしれない。

インスタンス変数の隠蔽

  • インスタンス変数はアクセサで包み直接参照しないようにする。同一クラス内での参照であっても同様。
  • 直接参照の場合「データ」という詳細に依存し、アクセサの場合は「ふるまい」という抽象に依存する。そのためアクセサを利用する方が詳細の変化の影響を受けなくてすみ、クライアント(= オブジェクトの利用者)への影響を抑制できる。

依存性の注入

  • 依存するオブジェクトそのものを外から渡すことで依存の程度を小さくできる。
  • 例えば「具象クラスが何か?」や「生成のための手順」といった依存先の詳細を知る必要が無くなる。
  • 結果として依存先の変更による影響を受けにくくなる。

インスタンス作成の分離

  • 依存性を注入できない場合、つまり別クラスへの依存を内部で保持しなくてはならないような場合は、その箇所をクラス内で分離する。
  • 分かりやすい例としては依存先インスタンスの作成処理が挙げられる。
  • 実現方法としては2つ。1つはinitializeメソッド内で作成するもので、もう1つは依存先インスタンス用のアクセサメソッドを用意しその内でインスタンスを作成する。
  • 前者はインスタンス作成と同時に依存インスタンスも作成する。依存インスタンスの作成タイミングは固定されるが、要不要に関わらず必ず依存インスタンスを作成することになる。
  • 後者は必要になったタイミングで初めて依存インスタンスを作成するが、そのタイミングは固定化されないため、メモ化 (外部インスタンスの作成時に ||= を用いて インスタンス変数に代入する)のような考慮も必要になる

脆い外部メッセージの隔離

  • 外部メッセージはそれ自体をメソッドとして隔離する。
  • 他のテクニックでも言えることだがこれは必ずしも必要な対処ではない。
  • 例えば外部メッセージの利用が処理の大部分を占めるようなシンプルなメソッドにおいては、依存性が明確であるため隔離する必要性は薄い。
  • 逆に複雑なメソッドにおいては、そのメソッド自体が行いたいロジックと外部メッセージの利用が混在し、依存性が埋め込まれて見えにくくなってしまう。この状態では修正が意図しない範囲に悪影響を及ぼす可能性があるため、外部メッセージは隔離する方が良くなる。

どうやら自分が興味があるのは「ソフトウェア設計」というより「変更容易性」のようだ

「ソフトウェア設計」に強くなりてぇ... と思ってここの所その手の書籍に改めて色々手を出していたのだが1つのことに気がついた。

これ「ソフトウェア設計」の話じゃなくて「変更容易性」の話だ...

正確には「ソフトウェア設計」がカバーする要素の1つであるところの「変更容易性」に焦点を合わせているものが多い。 この辺りはそれぞれの書籍において最初の方に記載されている。 例えば以下のような感じ。

考えにくいことですが、一度書かれたら、このアプリケーションは永久に変化しないとしましょう。この場合、設計を気にする必要はありません。
...
変更の必要性こそが、設計を重要にするのです。
(オブジェクト指向設計実践ガイド)

ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えることである
(Clean Architecture 達人に学ぶソフトウェアの構造と設計)

それは「設計」に問題があるからです。設計とは、ソフトウェア全体をすっきりした形に整えることです。どこに何が書いてあるかわかりやすくし、修正や拡張が楽で安全になるコードを生み出すのが設計です。
オブジェクト指向でソフトウェアを設計する目的は、こういう変更の大変さを減らすことです。どこに何が書いてあるかをわかりやすくし、変更の影響を狭い範囲に閉じ込め、安定して動作する部品を柔軟に組み合わせながらソフトウェアを構築する技法がオブジェクト指向設計です。
(現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法)

これらの記載だけ抜き出してみると「ソフトウェア設計の目的 = 変更容易性」であるように錯覚してしまう。 が当然それは違う。要件/パフォーマンス/セキュリティ等、ソフトウェア設計には他にも重要な観点が多く存在する。

似たようなことは書籍にも記載が存在する。

本書では、「ソフトウェア設計」のうち、どんなアプリケーション機能を提 供するかと、具体的にどんなミドルウェアアルゴリズムで機能を実現するか を除いた領域、ソフトウェアのアーキテクチャ作りに着目します。
(ちょうぜつソフトウェア設計入門――PHPで理解するオブジェクト指向の活用)

ソフトウェアにおける設計とは、なんらかのソフトウェア品質特性の 向上を促進するためのしくみをつくることです。たとえば性能効率性はパフォー マンス性能を表す品質特性であり、性能効率性を上げるにはパフォーマンス設計 をします。 では本書での設計は、主にどの品質特性の向上を狙ったものでしょうか。 ... 保守性の中でも、特に 変更容易性の向上を目的にした設計手法 なのです。
(良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方)

どうもソフトウェア設計について学ぼうとすると、この手の「変更容易性」に焦点を当てた内容に行き当たりやすいように思う。 これはある種のバグというか適切でない情報の偏りのようにも感じてしまうが、まぁ認識していれば大きな問題ではないか。 (もしくは私が「変更容易性」に興味があるため、そちらに引き付けられているだけかもしれない)

いずれにせよ私が興味を持っている分野は、「ソフトウェア設計」というより「変更容易性」と表現するほうが適切そうだ。 これまで個人的に学んできた内容も結構そちらに偏っている。 (ソフトウェア設計原則とかDDDとか可読性の向上とかデザインパターンとか結構違うけどモデリングとか、RailsでいうところのFat Model問題とか、ドメインロジックどこに置くねんどう構成すんねん話とか去年ぶっ刺さりのSustainable Railsの本とかAPoSDとか...)

今後は意識的に「変更容易性重視ビルド」を組んでで戦っていきたい、という決意表明。 はぁ、たったこれだけの内容を吐き出すのに本当に時間がかかってしまったなぁ...

Safariにおけるiframeのキャッシュの扱いと「Failed to load resource: フレームの読み込みが中断しました。」の回避

Safariにおいて、iframeが存在するページを何度かリロードすると「Failed to load resource: フレームの読み込みが中断しました。」というエラーが発生した。 (他にも複合的な要因の組み合わせである可能性は高いが細かいところは不明)

その他細かい事象として、

  • Chromeでは起きない
  • cmd + option + eでキャッシュをクリアすると、事象は解消する

という様子。 どうもSafariのiframeのキャッシュの扱いが特殊なように見える。

調査してもこの仕様(?)自体の詳細は分からなかったが、 回避策は色々と判明したので記録。

1. iframeのsrcの末尾にクエリパラメーターとしてランダムな値を挿入

これを行うとSafari側が同一のリソースと判断しない(?)のか、リロードしてもキャッシュが用いられない様子。

Railsでやると例えば以下の形

content_tag(:iframe, '', src: some_path + '?' + rand)

実動作上問題は無さそうだが、不要なクエリパラメーターが含まれてしまうのは正直ちょっと気になる。

2. iframeをreloadする

window.onload = () => {
  document.getElementsByTagName("iframe")[0].contentWindow.location.reload();
}

のような形でiframeの中だけリロードする

分かりやすいが、不要にリロードが行われるのでやや見た目に影響もある(一瞬再リロードされるなど)

HTMLIFrameElement.contentWindow で HTMLIFrameElement が所属する Window オブジェクトを取得できるのは他の何かにも使えそう https://developer.mozilla.org/ja/docs/Web/API/HTMLIFrameElement/contentWindow

3. metaタグを使う

metaタグでキャッシュを残さないようにする。

<meta http-equiv="Cache-Control" content="no-store">

これも1.と同様にキャッシュを残さないようにするやり方。 一番スマートな気はするが、影響範囲が広くなりがちで気楽には採用できなさそう(試していない)

参考サイト

shinimae.hatenablog.com gene-pinefield.blogspot.com takanamishi.hatenablog.jp web-camp.io

Rails: フォームデータのparseとParameterクラスへの変換に関して

前置き

html form の フォームコントロールにおけるname属性に関して、 複数のチェックボックスラジオボタンに同じname属性を指定した場合、 それらは同一のグループに属するものとみなされる。 (HTML解体新書 CHAPTER 3-10 フォームコントロールの共通属性 name属性 より)

例えばこんな感じのcheckbox

またhttpのbody上では、まとめられることなく個別に送信される。

この値をRailsにおいてController等でActionController::Parametersのインスタンスとして受け取った際には、配列の形に変換されている。

ただしこれは input の name属性の最後に [ ] をつけた場合であり、 [ ] がない場合には1つの属性として捉えられ上書きされてしまうようだ。

どうもRailsは最後に[]がついた属性のみ配列としてparseしているらしい。

これがRailsのコード上のどこでこれが行われているのか調べてみた。

どこで行われているか

とりあえずAction Pack...?

とりあえず...Action Packかな? https://github.com/rails/rails/tree/main/actionpack

Action Pack is a framework for handling and responding to web requests. It provides mechanisms for routing (mapping request URLs to actions), defining controllers that implement actions, and generating responses. In short, Action Pack provides the controller layer in the MVC paradigm.

action_dispatch/http あたりにリクエストを処理していそうなやつがいる。 https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/http/request.rb とか。

controllerから

controllerで実行できるrequest.paramsから必要なパラメーターが取得できる。 request.class を実行すると、これがActionDispatch::Requestクラスのものだと分かる。 前項の内容とも一致する。

ActionDispatch::Requestクラスを生成している箇所を見れば、 「Railsは最後に[]がついた属性を配列としてparseしている」 がどこで行われているか判断できそうだ。

Rack

いうてそれがどこか分からない... とりあえずバックトレースを辿ってみることにする。

controllerからcallerを実行すると以下のようなバックトレースが得られた

["lib/ruby/gems/xxx/gems/byebug-xxx/lib/byebug/helpers/eval.rb:61:in `eval'",
 ...
 "lib/ruby/gems/xxx/gems/rack-proxy-xxx/lib/rack/proxy.rb:78:in `call'",
 "lib/ruby/gems/xxx/gems/railties-xxx/lib/rails/engine.rb:539:in `call'",
 "lib/ruby/gems/xxx/gems/puma-xxx/lib/puma/configuration.rb:225:in `call'",
 "lib/ruby/gems/xxx/gems/puma-xxx/lib/puma/server.rb:658:in `handle_request'",
 "lib/ruby/gems/xxx/gems/puma-xxx/lib/puma/server.rb:472:in `process_client'",
 "lib/ruby/gems/xxx/gems/puma-xxx/lib/puma/server.rb:332:in `block in run'",
 "lib/ruby/gems/xxx/gems/puma-xxx/lib/puma/thread_pool.rb:133:in `block in spawn_thread'"]

下の方から見ていくと、

"lib/ruby/gems/2.7.0/gems/railties-6.1.6/lib/rails/engine.rb:539:in `call'",

というものを発見。 pumaから直接呼び出されているrails周りのコードはここ...? 見てみると、

見事にActionDispatch::Requestを作ってる。これかな...?

req.paramsで既にActionController::Parametersが見れたので、 ActionDispatch::Request.new env の処理で作られているんだろう、多分。

POSTメソッドとRack

ActionDispatch::Request の初期化処理周りを見るがよくわからず。 ただ調べているうちにPOST というメソッドがどうもhttpのリクエストのパラメータをparseしているっぽい雰囲気。

parse_formatted_parameters あたりの処理を追っていくと、 最終的にはブロック内のsuperを呼び出しているらしい。 コメントを見る限りRackのPOSTをoverrideしているとのことなので、 Rackに同様のメソッドがあるのか...?

あった。 set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&') のform_varsがURLエンコーディングされたhttpリクエストを保持しており、 parse_query(form_vars, '&')で配列に変わっている

parse_queryを追うと に辿り着く。 細かくは見ていないが[]をチェックしてparamsにhashの形で入れ込んでいる様子。 ここでparamsが形作られているのだろう。

結論

httpリクエストパラメータのparseとActionController::Parameters(or 普通のhash?)への変換は、 どうやらRackがやっているらしい。多分...

所感

まともにRailsのコード追ったのそういえば初めて

texta.fm 1. Software Development in 2003 と エリック・エヴァンスのドメイン駆動設計 の話

open.spotify.com

の32:40あたりの話が印象的だったので整理。

エリック・エヴァンスのドメイン駆動設計 の話 の実装面と、今読むにあたり注意しておくべき事項の話。

該当箇所の整理

意訳な部分はあるので注意

この本が不幸なのは、この本の中盤は技術の部門。デザインパターンの粒度の設計や実装。
この本が出たのは2003年。当然ながらこの頃の技術や実装は古い。(特にデザインパターン)
この本の前半(1部)と後半(4部)はとても重要で現在でも読む意義がある。
が、中盤(2,3部)は引き算して読む必要があるが、この引き算は当時を知っている人でないと難しい部分がある。

しかしこの本の技術的な要素(中盤のデザインパターンの話)をきちんと映し取ることがより良い設計であると勘違いする人が増えた。
-> いわゆる軽量DDD(?)。パターンを利用するのは1,4部より簡単なのでそうなりがち。

プログラマはサンプルコードから技術を学ぼうとするが、この本はサンプルコードが古い(当たり前の話)
Howの部分は当然2003年以降変わっているのに、2003年に出た内容を聖書のように映し取ってしまうのは不幸。

この本に書いてあるもの(実装面)をそのまま実装しようとすると現代的な設計にはなりにくい。
他の本も読んで勉強しましょう

所感

過去の本に書かれている実装なりサンプルコードなり方法論が古いというのは、
大変当たり前の話だが正直その点は意識できていなかったな...

では最近書かれたDDDに関する日本語の書籍は、この古い点がブラッシュアップされているのだろうか? この間読んだ、 ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 は果たしてこの古さが解消されているのだろうか? 自分では判断つかない。理由は簡単で代替の位置付けになる新しい設計がどういったものであるのか、 よく理解していないから。

現代的な設計論とはなんぞや...何にアクセスすればいいのか? さらにいうとこの時代の設計論とのつながりとは...? ソフトウェアの設計と言っても範囲が広すぎる。 「ドメイン駆動設計」はソフトウェアの設計のうちどのような領域の本で、 他にどんな仲間がいることになるのだろうか? 薄ぼんやりとすら全体が見えていない。どれも全く分からない。

どうやらこの辺りに興味があるらしい。 この書籍の設計方針はどういった点が古く、現代においてはどう発展しているのか? もっと大きな視点でいえば、ソフトウェアの設計に関して過去と現在をつなぐ歴史や見取りが知りたい。