このカテゴリの記事
Tech
例示用・テスト用ドメインについて知る
こんにちは。豊田です。 前回は、例示用IP・テスト用IPアドレスについてブログを書きました。 例示用IPアドレスについて知る 上記ブログでも触れていますが、私たちは職業柄IPアドレスよりもドメインを使うケースが多いのですが、実はドメインについても代表格である"example.com"(とその他少し)しか知らないのでは?と疑問に思ったので、調べてみました。 はじめに結論 テスト用・例示用のドメインが用意されていることを知りました。(RFC2606 , RFC6761) 特に以下は、今回気になっていた例示用・テスト用のドメインです。 ドキュメント例示用途向け .example TLD例示用 .example.com / .example.org / .example.net セカンドレベルドメイン例示用 .jpドメイン .example.jp / .example.co.jp / .example.ne.jp 以下のバリエーションもある .example1.jp ~ .example9.jp .example1.co.jp ~ .example9.co.jp .example1.ne.jp ~ .example9.ne.jp ドメイン名例.jp 日本語ドメイン版 xn--eckwd4c7cu47r2wf.jp "ドメイン名例.jp"のpunycode テスト用途向け .test DNSやネットワークのテスト用途として予約されている テスト用途で予約はされているものの、本当にネットワークを介したテストを実施するのであれば、当然DNSの設定は必要になるので注意が必要 他にも用途別に多くのドメインが予約されています。 気になる方は調べてみてはどうでしょうか?(Wikipediaにも一覧が載っていました) ドキュメント例示用途向けドメイン まずは例示用向けに予約されているドメインを列挙していきます。 .example IANAのSpecial-Use Domain Namesとして定義されているトップレベルの例示用ドメインです。 明示的にトップレベルドメインを示したいときは、こちらを使うのが良さそうです。 このドメインは知りませんでした。覚えておこうと思います。 .example.com / .example.org / .example.net 同じくIANAのSpecial-Use Domain Namesに定義されている、セカンドレベルドメインです。 多くの人は.example.comに馴染みがあると思いますが、.org, .netも予約されていたのは初耳でした。 通常は.example.comを使いつつ、別のセカンドレベルドメインも例示するときには、こちらを使うのが良さそうです。 jpドメイン .example.jp / .example.co.jp / .example.ne.jp .jpドメインにおける例示用に予約されているドメインです。 .example.jp、.example.co.jpは使えると知っていましたが、.example.ne.jpも使えるとは知りませんでした。 .ドメイン名例.jp(punycode: xn--eckwd4c7cu47r2wf.jp) こちらも知りませんでした... マルチバイト文字の例示用ドメインです。 こちらも、もしかしたら必要になるケースがあるかもしれないので、覚えておいても良さそうです。 テスト用途向けドメイン 次はテスト用途で予約されているドメインです。 こちらは一つだけです。 .test DNSやネットワークのテスト用途として予約されたドメインです。 ネットワークを介するテストをする(独自にDNSなどを設定して運用するような)場合は、こちらを使うことが推奨されています。 というのも.testドメインは、その名前解決リクエストに対して基本的にエラー応答を返すよう、DNSの管理・運用者に要求しています。(RFC6761の6.2.4) そのため、誤ってグローバルネットワークに出てしまうようなテストを書いたとしても、より上位のDNSからは「そんなドメイン知らん」と言われるだけで、ネットワークに不要な負荷をかけないような仕組みになっているからです。 限られたネットワーク内で完結することを想定したテストであれば、.testをテスト用途で使って良い。ということのようです。(しつこいですが、DNSの設定等は独自にちゃんとやっておく前提です) 専用ドメインを取得していない(これから取得する)ようなケースなどでは、非常に有効な手段となりそうです。 おまけ その他にも色々とドメインが予約されていますが、その中で .invalidというドメインは興味深かったです。 こちらは「無効なドメイン」を示すドメインであり 誤って外部にリクエストを送ってしまう可能性のある設定の初期値 名前解決に失敗することを期待するテスト 無効なURLの例示 ダミーメールアドレス といった場面で利用できます。 まとめ 例示用・テスト用のドメインについても知らないドメインがいくつかありました。(他の用途で言えば、もっとたくさん専用ドメインが予約されていました) 多くの場合.example.comで事足りてしまうことが多いですが、知識の引き出しとして覚えておくと、助かる場面があるかもしれません。
例示用IPアドレスについて知る
TL;DR IPアドレス(v4, v6)にはドキュメントへの記載用のアドレスが予約されています。(RFC5737 / RFC3849) ドキュメントに記載するアドレス際は、適切な専用のIPアドレスを使いましょう。 素朴な疑問 弊社ではwebアプリケーションの開発をおこなっています。 そんな業務の中で、先日「IPアドレスでの入力はinvalidにする(ドメイン名での入力を求める)」というケースの実装をすることがありました。 実装を終え、いざテストを書こうとしたときに、テストで使えるIPアドレスを知らないことに気づきました。(職業柄、ドメイン名(example.comなど)を扱うことはあってもIPアドレスを扱うケースは多くないので...) その時に調べた例示用IPアドレスについて、まとめておこうと思います。 IPv4 RFC1166 例示用IPv4アドレスはRFC1166で登場しました。 当初は192.0.2.0/24のみでした。 また、「例示用」という明確な定義はなく、"TEST"というネットワーク名が付けられている。というくらいの曖昧な位置づけだったようです。 ※ 一応、研究用途であることや、インターネットでは利用されない前提であることは決まっていたようです。 RFC5737 例示用のIPv4アドレスに関しては、RFC5737が最新です。 こちらでは、以下の3つアドレスブロックを例示用と位置づけています。 192.0.2.0/24 (TEST-NET-1) 198.51.100.0/24 (TEST-NET-2) 203.0.113.0/24 (TEST-NET-3) 上記のアドレスブロックは、明確に「例示用」とされており、ルーターやファイアウォールなどのネットワーク機器で「外部ネットワークに出さない/内部ネットワークに入れないIPアドレス」とすることをネットワーク運用者に求めています。 つまり、(誤って)実際のプログラムで使われてしまっても、外部ネットワークに出ていかない安全なIPアドレスということになります。 「例示用」の位置づけですが、IPアドレスとして機能しないものであり、テストでの利用も問題ありません。 128.66.0.0/16 少し脱線しますが、RFC5737の5章では、128.66.0.0/16というIPアドレスが過去に例示用に使われたことに言及しています。 このIPアドレスは現在、特別な意味を割り当てられていないため通常のIPアドレスとして(注意すれば)使って良い。とされています。 あまり気にする必要は無さそうですが、古い設定などが残ってる環境などは、意図しない挙動になる可能性があるので、少し注意しておく必要はあるかもしれません。 IPv6 RFC3849 IPv6の例示用アドレスはRFC3849で定義されています。 2001:db8::/32 です。 IPv4同様、インターネット上にパケットが流通しないように、ネットワーク運用者が管理することが求められています。 RFC9637 比較的最近(2024年)、RFC3849を拡張する形でRFC9637が定義されました。 3fff::/20 です。 RFC3849で定義されている2001:db8::/32はネットワーク部で32bit使っており、より大規模なネットワークを例示するユースケースで使えない。という問題があったようです。 なぜネットワーク部を20bitとしたのかについては、3章で具体的に説明されていました。(現在の利用状況から20bitとしておけば、将来的にも困らないだろう...ということのようです) まとめ 長年この業界にいますが、example.comだけで困らなかったので、久々に「へーちゃんと用意されてるんだ」と感じました。 テストやドキュメントを書くときは、RFCに従って書いていきましょう!
「「技術書」の読書術 達人が教える選び方・読み方・情報発信&共有のコツとテクニック」を読んで印象に残ったこと
こんにちは。山屋です。 お盆も過ぎたというのに暑すぎですね。早く秋が来てほしいものです。 さて、今回は「技術書」の読書術 達人が教える選び方・読み方・情報発信&共有のコツとテクニック という本を読みました。 技術書の選び方、効率的な読み方、より内容を定着させる方法等について書いてある本で、印象に残ったトピックを紹介させていただきます。 『3』の発想 本に限らず『3』の発想が大切であるという話があるそうです。2つではその間の関係しか見えないが、3つならば発想がその先に広がっていくとのことです。これを技術書の選び方に適用すると、以下の読み方ができそうです。 入門書、専門書、逆引きの3通り読む 入門書から学習を始めて専門書を読んでさらに知識を深めるだけでなく、学校の勉強で使っていたようにドリルなどがあればより知識を定着させることができます。ドリルに対応する本として、目的から実現方法を紹介する本である「逆引き」が挙げられます。 この3種類の本を読めば実務で使えるレベルになるでしょう、とのことです。 入門書を3冊見比べる 同じ難易度のを複数見比べるというのも1つの方法だそうです。 3冊ともに同じことが書いてあった場合、それはとても重要な部分であるとわかります。逆に1冊にしか書かれていなかった場合、そこまで重要ではない知識であったり、新しい技術であったりするかもしれません。 比較することで知識の重要度を知ることができます。 時間制限読書法 定めた時間内に1冊を読了することを目標とする読書法です。最初は90分に設定し、慣れてきたら短くしていくと良いでしょう。この方法には以下のメリットがあります。 集中して読書できる この時間だけ、と決めれば周囲の誘惑にも負けず集中して読める可能性が高まるでしょう。加えてパーキンソンの法則(人間は与えられた時間を全て使ってしまう傾向がある)によって、制限時間内に終わらせようという意識が働きやすくなるようです。 悪書のリスクを軽減できる たとえ自分に合わない本を読んでしまったとしても、90分の損失だけで済みます。素直に最初から読んで、後半になってから自分にあまり必要な本ではないと判明すると時間の無駄ですね。(私は最近これをやらかしてしまい、とてもがっかりしました) 成長のチャンスはアウトプットにあり 学んだことをアウトプットすることが成長に繋がります。 理由の一つとして、もしも本の内容を誤って理解してしまった場合に気づくことができるというものが挙げられます。(学校の勉強でも練習問題やテストがないと間違いに気づけませんよね) アウトプットの例は以下です。 コミュニティに参加する 他のエンジニアと議論できるだけでなく、人脈を広げることができるというメリットがあります。会社の中では出会えないようなスキルを持っている人とも関われるかもしれないので刺激になるでしょう。 LTに挑戦する コミュニティや勉強会で、5分程度のLT(Lighting Talk)会が開催されていることがあるので、参加してみるのも良いでしょう。 カンファレンス等に登壇する場合は準備が大変だと思いますが、5分ならば比較的気軽に話せるでしょう。自分がどんなことを勉強しているのか、興味を持っているのかといったことを発信すると周囲の人からアドバイスをもらえるかもしれません。 また、5分程度のLTといえども資料の準備は必要です。そのときにどうやったら相手にわかりやすく伝えられるのかと、話す順番等を考えることになります。ある内容について話す際に前提知識も説明しなければいけないということに気づくでしょう。考える中で、自分の中では当たり前のことであっても他の人にとってはそうではないことや、自分の知識の抜けや漏れに気づくといったことがあると思います。これを繰り返すことでより理解が深まるのです。 ブログに書く 発表するのはハードルが高いと感じる場合におすすめの方法です。 発表資料を作るときのように知識の整理ができますし、記録として残るというメリットがあります。 私もこの項目を読んだので、今まさにブログを書いているというわけです。ブログを書くにあたって、今の自分に特に有用な項目は何だろう?自分はこの本に何を求めているのだろう?と考えながら読み返して、より有益な情報が得られたと感じています。 感想 エンジニアとして生きていく上では避けられない「技術書」をより良く読める方法について知ることができて良かったです。 私は「技術書を読む速度を上げたい」「技術書の理解度を高めたい」というモチベーションでこの本を手に取りました。その観点で印象に残ったことを今回ブログとしてアウトプットしました。 私が今回紹介したのは本のごく一部で、他にも「本の選び方」「1年で1,000冊を読破する超多読法」など興味深い内容が紹介されているので、もし興味がありましたらぜひ読んでみてください! 最後までお読みいただきありがとうございました。
JRRF2025に参加してきました
みなさん、こんにちは。 最近、自宅に3Dプリンタを購入してしまった小林です。 去年はRWC2024に持って行ったノベルティを展示するための台を作成したりと順調にのめり込んでいます。 さて、今回はJRRF2025(Japan RepRap Festival)という3Dプリンタ関連のイベントに参加してきたので、紹介したいと思います。 https://www.japanreprapfestival.com/ はじめに 3Dプリンタ関連のイベントといえば、どちらかというと業務用途向けが中心で、個人ユーザー向けイベントは少ない印象だったのですが、X(@JapanRepRapFes)でこのイベントを知り、「これは行かねば」と参加しました。 そもそもRepRapとは? そもそもRepRapという言葉を私自身もこのイベントに参加することで知りました。 RepRapとはReplicating Rapid Prototyperの略で、「自分の部品を自分でプリントして複製する」というOSSプロジェクトで、要は3Dプリンタで3Dプリンタの部品を印刷して複製しようというものです。 https://reprap.org/wiki/RepRap/ja RepRap関連のイベントは海外では盛んに行われているようですが、日本での開催は今回が初とのことです。 参加してみて 雰囲気 私は開場の30分前くらいに到着したのですが、その時点ですでに100〜200人ほどの待機列が出来上がっていました。 会場は東京流通センターで1000人ほどが収容できるスペースだったのですが、大勢が参加されていて、かなりの熱気がありました。(1日目の来場者数は650人ほどとのこと) 展示ブースが占有する面積を考えると大盛況と言えたと思います。 親子連れが多くて、「3Dプリンタが一家に一台」と言われる未来もそう遠くないのかも、と思わされました。 特に気になったブース インスタリム株式会社 3Dプリンタで義肢を作成しており、通常の義肢よりも安価、かつカスタマイズされたものを作成できるとのことでした。 さらには、専用のプリンタやフィラメントの制作も行なっているとのことでした。 ※写真がなくて、すみません。興味がある方はぜひホームページをご覧ください。 https://www.instalimb.com/en ぺんほり(@PENHOLI1)さん トラックボールマウスの傾斜スタンドを作成されている方で、私自身トラックボールユーザーなので参加前から絶対に訪れたいと思っていました。 触らせていただいたのですが、スタンドがカチッとシンデレラフィットする瞬間はとても気持ちよかったです。 https://x.com/PENHOLI1 gear(@_gear_geek_)さん この方はまさにRepRapをされている方でした。 プレートに軸(確か6軸とのこと)を追加して、傾けることでサポートがあっても印刷が難しいものもサポートなしで印刷できるようになるとのことでした。 RepRapといってもパーツ、制御処理等々と多岐にわたるようなので、本当に興味深い分野だなと改めて思わされました。 https://x.com/_gear_geek_ MakerChip文化 直径40mm厚さ3mmのコイン状のマーカーを印刷して、名刺代わりに交換するのが流行っているようです。 SNSで「自身の持てる技術の全てを詰め込む」と投稿されている方もいらっしゃって、私も持てる全てを注ぎ込んで印刷してきました。 25枚ほど印刷して持って行ったのですが、全て交換することができました。 真ん中の部分が回転するのですが、意外と好評で嬉しかったです。 マーカーの中にLEDを入れて、Lチカさせている方や歯車を動かせるようにしたりさせている方など面白い作品が多く面白かったです。 感想 思ったよりも来場者が多く、しかも子供連れが多く、3Dプリンタの普及の可能性が見ることができ、参加してとても良かったです。 私自身はRepRap自体には興味はなかったのですが、実際にやられている方のプリンタやソフトウェアを見ると、自分でもやってみたくなってきてしました。 残念だったのは、一般参加している方々との交流がほとんどできなかったことです。 せっかく一般参加している方たちもMakerChipを作成して参加されているので話題には事欠かなかったはずなのですが、、、 次回参加する時はもっと積極的に交流したいと思います。
サードパーティークッキーについて -問題点と今後の展望-
こんにちは。山屋です。 みなさんはどんなクッキーがお好きですか?私はカントリーマアムが好きです。という話は置いておいて… Webサイトを閲覧していると、以下の画像のように「クッキーを受け入れますか?」といったダイアログが表示されたことはないでしょうか。 これが一体何なのか前々から気になったので、今回はいろいろと調べてみました。 そもそも「クッキー」とは何か 簡単に説明すると、Webサイトがユーザーのブラウザに保存する小さなデータのことです。 初めて訪れたWebサイトでは、Webサイトがクッキーの値を含めたレスポンスを返す→ブラウザはそれを保存しておく→ブラウザが次に同じWebサイトにアクセスするときにその値を送り返す、ということをしています。これがどういうことなのかというと、クッキーによって過去にアクセスしたことがあるWebサイトかどうかを判別できるということです。 これを応用してWebサイトがレスポンスに一意なIDを含めたクッキーを送信すればユーザーを区別することができ、ログイン状態となるわけです。 このクッキーは「ファーストパーティークッキー」と呼ばれ、先ほどの画像に書かれているクッキーとは別物です。画像のクッキーは「サードパーティークッキー」と呼ばれるものです。 サードパーティークッキーとは 一言で言えば「訪問したwebサイトとは異なるドメインから設定されたクッキー」のことです。 主な用途としては広告で、複数サイトにまたがったユーザーの行動の追跡を行う際に用いられます。 Webサイト(画像の「Aのブログ」)には「広告配信会社」のスクリプトが埋め込まれており、広告配信会社の「広告表示枠」があります。広告配信会社のWebサーバは広告画像と共にクッキーを発行し、そこにユーザーを識別する情報や、現在のページを訪れたことを示す情報を含めます。これが「サードパーティークッキー」です。 そして別のサイトを訪問し、もし同じ広告配信会社の広告表示枠があるならば先ほどのサードパーティークッキーが送信され、先ほどのページを訪問した人と同一人物であると判断するのです。 (こちらの記事にも詳しく書いてありますので、興味があればぜひ読んでみてください) サードパーティクッキーの問題点 仕組みを知らないユーザーからすれば自分がさっき調べていたことに関する広告があちこちのサイトで表示されてしまい、監視されていると感じても仕方ないでしょう。(完全に余談ですが、昔私がネットサーフィンをしていたときにあちこちのサイトでキングクリムゾンの「クリムゾン・キングの宮殿」のCDジャケット画像が大量に表示されていたのはこれのせいだったということですね。怖かったです) Webサイトと広告主のサイトが訪問したことを認識しているのなら理解できますが、広告配信会社というよくわからない第三者が勝手にユーザーの個人情報を収集しているということになります。これはプライバシーの侵害に当たるのでは?という議論がなされています。詳しくは後述しますが、昨今はサードパーティークッキーの使用をやめようという傾向にあります。 なぜサードパーティクッキーを廃止しないのか? 広告をパーソナライズできないため トラッキングができなくなってしまうため、ユーザーの好みを反映した広告を表示できなくなってしまうでしょう。それに伴って広告をクリックされる頻度が下がり、広告料で運営しているWebサイトにとっては大打撃となると思われます。 一部のシングルサインオン(SSO)に影響が出る可能性があるため iframeを用いてログインが必要なページを埋め込んでいる場合は影響が出る可能性があります。 例えばWebサイトにFacebookのページが埋め込まれているとしましょう。この場合htmlのiframeの中にFacebookのログイン画面のurlが記述されています。訪問した際にFacebookのサーバーにアクセスし、「facebook.comに保存されているクッキー」も一緒に送信するのです。これがサードパーティークッキーです。これによってログイン状態でFacebookが埋め込まれます。 サードパーティクッキーの現在の対応 国内の法律 法律では規制の方向に進んでいます。(法律については明るくないので、詳しくはこちらの記事や総務省のページをお読みください) 2022年4月に「改正個人情報保護法」が施行され、サードパーティクッキーは規制の対象となりました。 加えて2023年6月に施行された「電気通信事業法の改正法」によりCookieを含む情報を第三者に提供する場合、事前にユーザーの同意を得ることなど3つの条件のうちいずれかに対応することが義務付けられることになりました。これが最初の画像の画面ですね。 ブラウザ ブラウザごとに対応方針が大きく異なっています。ここではシェアの大きいブラウザのみ紹介します。 Safari 2020年3月から、サードパーティクッキーをデフォルトでは完全にブロックしています。 Appleはプライバシーを非常に重要視しているようです。 Chrome 2025年をめどにサードパーティクッキーを段階的に廃止することを目指すと発表していましたが、2024年7月に廃止を撤回しました。 そして最新の発表(2025年4月22日)でも、「サードパーティクッキーの選択肢を提供する現在のアプローチを維持する」との方針を示しました。 今後の展望 法律、ブラウザ共にサードパーティクッキーは規制の方向に進んでいくのだろうと思われます。 代替技術として、プライバシーの保護と広告効果の両立を目指し、Googleが提唱しているプライバシーサンドボックスというものが業界全体で取り組まれています。現在はまだ開発中のようです。(こちらの記事で解説されています) まだChromeではサードパーティクッキーは提供されていますが、開発が進めばプライバシー保護と広告効果が両立できるのでしょうし、定期的にウォッチしようかなと思います。 感想 たまに表示されているダイアログが気になっていただけなのに、ずいぶん遠いところまで調べてしまいました。法律やプライバシーサンドボックスなど、普段業務では触れることのない分野について知ることができて良い勉強になりました。 個人的には、広告で自分が調べていたものがあちこちで表示されるのは監視されているように感じてしまうので、規制してほしいという気持ちはあります。しかし、広告がなければ成り立たないサービスは確実にあって、何か良い落とし所はないものか…と思うのでプライバシーサンドボックスには期待しています! 最後までお読みいただきありがとうございました。 参考資料 3rd Party Cookie Advent Calendar 2023 改訂新版 プロになるためのWeb技術入門 6.5.5 セッション管理不備の危険性 コラム「サードパーティクッキーと個人情報保護」
Rails(ActiveSupport)のHash#slice!の罠
ご無沙汰してます。豊田です。 随分長いことブログを書いていなかった気がしますが、きっと気のせいですよね。 今回はタイトルにもあるように Rails(ActiveSupport)が提供する Hash#slice! の挙動について、書いていきたいと思います。 いきなりですが問題です こちらのコードの実行結果、わかりますか? hash1 = { a: 'hoge', b: 'fuga', c: 'piyo' } hash2 = hash1.slice!(:b) hash2 # => ??? この問題がすぐに(正しく)解ける方は、このブログは読まなくで大丈夫ですw まずは他の類似メソッドでおさらい 「いきなりそんなこと言われても...」という方もいらっしゃると思いますので、類似のメソッドの挙動をおさらいしてみましょう。 Array#slice(Ruby) Rubyの組み込みライブラリ、Arrayクラスが提供する #slice メソッドです。(公式) array1 = [100, 200, 300] array2 = array1.slice(1) array1 # => [100, 200, 300] array2 # => 200 引数で指定したindexに該当する要素を返してくれます。 ※他にも異なるパラメータを使う方法がありますが、基本的な動作は変わらないので割愛します Array#slice!(Ruby) 同じくArrayクラスが提供する #slice! メソッドです。(公式) array1 = [100, 200, 300] array2 = array1.slice!(1) array1 # => [100, 300] array2 # => 200 レシーバーを破壊的に変更する点は異なりますが、指定した箇所の要素を返すという点では #slice と同じですね。 Hash#slice(Ruby) 続いて同じく組み込みライブラリが提供する Hashクラスの #slice メソッドです。(公式) hash1 = { a: 'hoge', b: 'fuga', c: 'piyo' } hash2 = hash1.slice(:b) hash1 # => {:a=>'hoge', :b=>'fuga', :c=>'piyo'} hash2 # => {:b=>'fuga'} 指定したkeyと、対応するvalueのペアを返します。 指定した要素を取り出す。という点でArrayクラスの #slice メソッドと似ていますね。 冒頭の問題 それでは、冒頭で出した問題を改めて見てみます。 hash1 = { a: 'hoge', b: 'fuga', c: 'piyo' } hash2 = hash1.slice!(:b) hash2 # => ??? ! をつけることでレシーバーを破壊的に変更する違いはあるものの、直感的には Hash#slice と同じ結果になりそうです。 実行してみる 早速実行してみました ちょっと見づらいので、整形します hash1 = { a: 'hoge', b: 'fuga', c: 'piyo' } hash2 = hash1.slice!(:b) hash1 # => {:b=>'fuga'} # 上記では実行していませんが、実際にはこうなる hash2 # => {:a=>'hoge', :c=>'piyo'} どうでしょうか。なんか思ってたのと違いませんか? 指定したkey, valueのペアがレシーバー側に残り、それ以外のkey, valueのペアが削除されています。(そして削除されたペアは、戻り値になっています) ActiveSupportが提供するHash#slice! 気になったので調べてみたところ、Railsの公式にちゃんと書かれていました。 Replaces the hash with only the given keys. Returns a hash containing the removed key/value pairs. なるほど。そういう仕様なんですね。 この仕様の違いを私なりに解釈してみると、視点の違いなのかなと思いました。 Rubyが提供する #slice は、レシーバーから特定の要素を「切り出す」イメージ( => 切り出した側に注目している) ActiveSupportが提供する #slice! は、レシーバーから特定の要素以外を「切り落とす」イメージ( => レシーバー側に注目している) Hash#extract!というメソッドがある ActiveSupportには Hash#extract! というメソッドが用意されています。 私が Hash#slice! に期待していた挙動は、実はこちらだったりします。 Removes and returns the key/value pairs matching the given keys. hash = { a: 1, b: 2, c: 3, d: 4 } hash.extract!(:a, :b) # => {:a=>1, :b=>2} hash # => {:c=>3, :d=>4} なるほど、extract(抽出する)であれば、レシーバーから要素を抽出(取り出す)するというイメージで違和感がありませんね。(抽出した側に注目している) つまり Hash#slice の破壊的メソッドは Hash#extract! ということになります。 まとめ ActiveSupportが提供している Hash#slice! は、その他の #slice, #slice! とは挙動が(直感的に)異なる Hash#extract! が(直感的に)他の #slice, #slice! に近い挙動になる 普段Rubyのコードを書いているとき 「破壊的変更だから ! をつければ良い」と考えていると逆の挙動になるので事故る いかがだったでしょうか。 私はActiveRecordのenumの実装を読んでいるときに Hash#slice! の挙動を見て違和感を感じ、この仕様を知りました。(もしかしたら、これまでにも無意識に使って事故ってたのかもしれませんが...) オープンソースのプログラムを読むのは勉強になりますね。
応用情報技術者試験に合格しました
こんにちは。山屋です。 先日、応用情報技術者試験に合格しました!今回は勉強法や受験して良かったこと等を書こうと思います。 応用情報技術者試験とは こちらにある通り、ワンランク上のITエンジニアのための試験です。 (他に詳しく説明している記事がたくさんあると思うので、ここでは簡単に説明します) 午前試験はマークシート式で広く知識を問う問題が、午後試験は分野を選択して記述式で深い知識を問う問題が出題されます。それぞれ60点以上が合格ラインです。 午後試験では、「情報セキュリティ」に加えて以下の分野から4つ選択して回答します。 経営戦略 プログラミング システムアーキテクチャ ネットワーク データベース 組み込みシステム開発 情報システム開発 プロジェクトマネジメント サービスマネジメント システム監査 私は午前試験が68.75点、 午後試験が63点でした。(ギリギリ!) なぜ受験したのか 業務中にわからない用語やイメージしにくい事柄がたくさんあると感じていました。具体的にはデータベース、SQLについてです。 ITに関する幅広い知識が問われるこの試験の勉強をすれば解消できるかも、と考えて受験しました。 加えて、セキュリティ分野に興味があるのでその勉強もできればと考えていました。 勉強の進め方 学習は試験の約半年前から始めました。ただし途中で1ヶ月ほど学習をストップしたり、ほぼ学習しない期間があったりしたので実質3,4ヶ月ほどだと思います。 (私の場合は合格点ギリギリになってしまったので、もう少し学習量を増やした方が良かったですね…) 午前試験 教本(キタミ式イラストIT塾 応用情報技術者)を一通りざっと読む→過去問道場で過去問10年分を解く→理解が浅い分野を把握したらもう一度教本を読んで理解する→過去問道場を解く… という流れで学習を進めました。 人によると思いますが、私の場合は過去問が6割程度解けるようになったら午後試験の勉強を開始しました。ただし、時間経過で内容を忘れてしまうので午後試験の勉強を始めてからも午前試験の勉強を並行して行うようにしました。 午後試験 午後試験は選択制なので、まず回答する分野を考えました。 問題集(応用情報技術者 午後問題の重点対策)をざっと読んでみて、勉強したいまたは得意そうな分野を6分野ほどピックアップしました。(本番で難しい問題が出題された時に備えて多めの分野にしました) あとは問題集をひたすら解くだけです。収録されている過去問分が理解できるよう、繰り返し解きました。 勉強して良かったこと 受験するきっかけになった、業務中のわからなかった単語の意味を知ることができました。 具体的にはデータベース関連での「第○正規化」といった単語は聞いたことはあったものの実際にどんな表だったっけ…となっていたり、SQLの理解が全体的になんとなくだったりしていたのですが、理解が深まったと感じています。 まだ業務で試験で勉強したことがそのまま役に立ったという場面はないのですが、つい先日、認証に関する実装を検討する際にやることのイメージが掴みやすくなったと感じました。(具体的にはRSAといった暗号アルゴリズムの話は試験勉強で出てきたなと思いました) 応用情報技術者試験のように幅広い分野を学習することは、「全く知らない」ということを減らして初めてやることへの抵抗感を薄め、理解を早めてくれるのではないかなと個人的には考えています。幅広いことの単語を知ることができるので、調べることすらできないという状態になるのを避けられるかなと思います。 もう一つの受験の目的であったセキュリティ学習については、不正アクセスの手法や適切なネットワーク構成、OSI基本参照モデルなど基礎的なことが学べて良かったです。 以前の記事でTryHackMeでセキュリティ学習をしていて応用情報技術者試験の内容と被っているところがあると書きました。 TryHackMeは日本語版がないというデメリットはあるものの、アニメーションを用いてわかりやすい・ゲーム感覚で学習を進められるというメリットがありました。 セキュリティ学習という観点で見ると、日本語で他の分野も幅広く学べる応用情報技術者試験と、英語ができる人にとってはわかりやすくセキュリティに特化して学べるTryHackMeのどちらでも基本的なことは学べるのではないかなと思います。(私は英語が苦手なので前者の方が勉強になりました) 終わりに 何はともあれ一回で合格できて良かったです。 資格試験というお金がかかり制限時間もあるという環境で勉強することはプレッシャーではありますが、その分効率的に勉強できる面もあります。もし幅広い知識を身につけたいのであれば、(基本or応用)技術者試験を受験するのは選択肢に入れて良いと思います。 最後までお読みいただきありがとうございました。
7つの設計原理について 〜『プリンシプルオブプログラミング』を読んで〜
みなさんこんにちは!山屋です。2025年もよろしくお願いいたします。 さて、プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則という本を読んで印象に残った「7つの設計原理」について書いていきます。 『プリンシプルオブプログラミング』の概要 プログラミングに関するプリンシプル(原理、原則)を101個紹介している本です。プリンシプルごとに概要と、守るためにやるべきこと等が説明されています。 どのようなプリンシプルが紹介されているかは目次をご覧ください。 7つの設計原理の内容 実際の開発現場の分析から導出された、品質のよいコードのための「経験則」です。 この原理は主にコードレビュー時のチェック観点として使われます。 メンバーやシチュエーションによってコードレビューの観点がブレないようにするという目的があります。 1. 単純原理 概要 シンプルにこだわること。初心者でも読めるような一貫して単純なコードを書くようにする。 やるべきこと 自然なコードを心がける。高級なテクニックを使わず、単純なやり方で通すこと。 2. 同型原理 概要 形にこだわること。同じことは同じように扱い、例外を設けないようにする。例えば、モジュールにて数値の単位を統一したり、使用順序を統一したりすること。 やるべきこと 一貫性のあるコードを書くようにする。実力をアピールするために、独創的なコードを書くことはやめましょう。 3. 対称原理 概要 形の対称性にこだわること。「アクティブ」があれば「パッシブ」があるように、対になるものを持つようにする。 やるべきこと 「条件」がある場合、「反条件」の整合性も検討すること。加えて例外的な状況を極力排除すること。 4. 階層原理 概要 構造が階層であることにこだわること。物事の主従関係、前後関係などの階層関係を意識し、整理された関係性を構築すること。 やるべきこと コードの抽象レベルを意識して、1つの階層は同じ抽象レベルのものだけで構成すること。上位レベルから見たときに、下位レベルは「それを外部から見ている」ような視点で記述すること。 5. 線形原理 概要 処理の流れを直線にすること。スイッチでコードを制御したり、状態の数をむやみに増やしたりせずにコードの見通しを良くする。 やるべきこと 処理の流れをあちこちにジャンプさせず、上から下にコードが流れていくようにすること。処理の分岐を少なくすること。 6. 明証原理 概要 ロジックの明証性にこだわること。一見して正しい、と思えるようなコードを書く。 やるべきこと ロジックが明瞭なコードを書くようにする。そのために、ロジックが直感的でわかりやすいものにすること。また、コードだけで説明できない場合は別途ドキュメントや図、コメントを書くこと。 7. 安全原理 概要 安全性にこだわること。ありえないと思える条件についても考慮してコードを書く。 やるべきこと 全ての動作を洗い出して、それぞれが安全になるように考慮する。要件と機能を理解し、場合分けをコードに正しく落とし込むこと。 設計原理についての感想 業務でコードレビューをすることがあるのですが、どういう点を意識してレビューをすれば良いのか私の中で明確なルールがなかったため今回知ることができて良かったです。 今まではレビューでどんな処理なのかを追うことがメインになってしまっていて、あまり効果的な指摘ができていませんでした。(処理がわかりにくいところに問題が隠れがち、ということは知っていたのですが…) 設計原理の中で特に印象に残ったのは「6. 明証原理」です。 この本では設計原理は主にレビュー観点として紹介されていますが、コードを書く側でも意識すべきことでもあります。レビューにて「コードが何をやっているのかよくわからない」という指摘をもらうことがあり、これから意識しようと思います。 (他のプリンシプルについても触れますが)、わかりやすいコードを書くために「シンプルであること」や「名前重要」をまずは意識していこうと考えています。 終わりに 今回の記事では「7つの設計原理」について書かせていただきました。 この本ではあえて具体的なコード例は書かない方針で、実際にどうすべきかは自分で考える必要があります。ただ読んだだけではプリンシプルは身についていないので、つまりこれからが本番です。これらの原理をレビューや実装の際に意識して、少しずつ身につけていこうと思います。 最後までお読みいただきありがとうございました。
Rackについて学んだこと 〜Kaigi on Railsの「Rackを理解しRailsアプリケーション開発の足腰を鍛えよう」に参加〜
こんにちは、山屋です! 先日のKaigi on RailsにてRackを理解しRailsアプリケーション開発の足腰を鍛えようというワークショップに参加しました! ワークショップについてと、Rackについて学んだことをまとめました。 ワークショップ参加のきっかけ 業務でRackミドルウェアに関するプルリクエストのレビューをする機会がありました。 Rackについてうっすら知識はあったものの、「なんかピンと来ていない…」という印象をRackに抱いてしまいました。 そんな折にRackのワークショップが開催されるということを知ったので参加を決意しました。 ワークショップの内容 こちらがワークショップの内容です。 1日目にRackアプリケーションとRackミドルウェアの作成を、2日目にRackサーバーの作成を行いました。 私はワークショップ中にRackサーバーの作成までは終了できなかったのですが、実際に手を動かしたことでRackの理解が深まりました。 終始和やかな雰囲気で、スピーカーのhogelogさんは「ここにいる全員と話すつもりで来ました」と仰っており、楽しく進めることができました。もちろん私もお話しさせていただきました! Rackについて学んだこと Rackの概要 RackはWebアプリケーションサーバーとWebアプリケーションフレームワーク間の標準仕様です。 Rackがなかった頃は特定のアプリケーションサーバーとフレームワークの組み合わせでないと動作しない…ということが発生していたようです。RailsはRackアプリであるのでUnicornやPumaなど様々なWebアプリケーションサーバー上で動作します。 Rackアプリケーションの基礎 callメソッドを定義しているものが最低限のRackアプリケーションです。 callメソッドでは[status, headers, body]の形式でレスポンスを返す、という決まりごとになっています。 レスポンスは必ずこの形式なので、役割がはっきりしていますね。 コード例はこちらです。 Rackミドルウェア 資料からRackミドルウェアの概要を引用します。 Rackミドルウェアは、Rackアプリケーションのリクエストとレスポンスの間に介在し、リクエストを加工したり、レスポンスに処理を加えたりするコンポーネントです。ミドルウェアをチェーンのようにつなげることで、複雑な処理をシンプルに組み立てることができます。 すなわち、自身のcallメソッドで加工を行いつつ、他のRackミドルウェアやRackアプリケーションのcallメソッドを呼び出して処理を続けていくことができます。 用意する必要があるメソッドはcallとinitializeメソッドの2つです。 コード例では、initializeメソッドで引数を受け取り、ミドルウェアのcallメソッドの「status, headers, body = @app.call(env)」でAppクラスのcallメソッドを呼び出しています。 その後、callメソッド内でリクエストやレスポンスを加工することができます。 (ちなみに私がレビューしたコードではcallメソッドでUser Agent文字列の加工を行っていました) Rackサーバー 重ねてですが、資料からRackサーバーの役割を引用します。 大まかに言うとクライアントからHTTPリクエストを受け取り、Rackプロトコルで定まった値を詰め込んだ env ハッシュを作成してRackアプリケーションに渡し、Rackアプリケーションから帰ってきた配列を元にクライアントにHTTPレスポンスを返すのがRackサーバの仕事です。 Rackミドルウェアにstartメソッドを追加して、サーバーの処理を記述しているようです。 ソケット通信などあまり知識がないものも多く、正直なところ、サーバーはこんなことをしているんだという大雑把な理解となってしまいました。 ただこの内容はRailsを理解する上で重要なことだと思うので、機会を作ってまた深掘りしていきたいなと思いました。 学んでみてどうだったか 今まではRackについて「サーバーと何かしらやりとりをしている。ミドルウェアを作れる。RailsはRackアプリである」くらいの認識しかなかったのですが、解像度が上がったと感じました。 改めてレビューしたプルリクエストを見返してみたところ、どんな処理なのかスッと頭に入ってきました。 手を動かして学んだことにより、理解が深まったなと感じました。 自分から積極的に学ぶにはハードルが高い分野だったので、ワークショップを開催していただけてとてもありがたかったです! 同時に知識が足りていない部分も浮き彫りになったと思います。(サーバーが何をしているのか、サーバーの違いは何なのか、Railsに他にどんなライブラリが使われているのか、などなど…) Kaigi on Railsで「Rails wayに乗りましょう」という話をいろんな方が仰っていましたし、少しずつRailsが何をしているのか学んでいこうと思います。 最後になりましたが、ワークショップを開催してくださったhogelogさん、本当にありがとうございました! 参考資料 Railsの練習帳 Chapter 09 [Rails基礎] RackとRackミドルウェア パーフェクトRuby on Rails 【増補改訂版】 3-2 RackとRailsの関係
Kaigi on Rails 2024に参加してみた〜当日編Day1〜
みなさん、こんにちは。 小林です。 10/25〜26に開催されたKaigi on Ruby2024に参加して来ましたので、前回に引き続き、そのことについて書いていこうと思います。 前回の記事はこちら↓ Kaigi on Rails 2024に参加してみた〜事前イベント編〜 残念ながら、私は26日は予定がすでに入っていたため、Day1のみの参加になります。 会期中に使えるWebアプリケーションがあったのですが、運営スタッフたちがRailsで開発したとのことでした。 しかも最近、Rails8にアップデートしたとのことで、こういうのはエンジニアとしてはニヤッとしてしまいますよね。(実際に会場は大盛り上がりでした。) アプリケーション上でプロフィールを作成して、交換をしたり(する機会はありませんでしたが)、運営からの連絡を受信できるようにしてありました。 https://github.com/kaigionrails/conference-app あとでゆっくりコードを読んでみようと思います。 それでは、イベントの内容に関して紹介していきたいと思います。 セッション 私が参加したセッションは以下ですが、 RailsのPull requestsのレビューの時に私が考えていること そのカラム追加、ちょっと待って!カラム追加で増えるActiveRecordのメモリサイズ、イメージできますか? モノリスでも使える!OpenTelemetryでRailsアプリのパフォーマンス分析を始めてみよう Rails APIモードのためのシンプルで効果的なCSRF対策 現実のRuby/Railsアップグレード Hotwire or React? 〜Reactの録画機能をHotwireに置き換えて得られた知見〜 この中でも特に気になっていた講演を紹介します。 RailsのPull requestsのレビューの時に私が考えていること 私が一番聴きたかったセッションがこちらです。 yahondaさんがRailsのコミッターとしてrails/railsプロジェクトのレビュー時に意識していることの紹介でした。 railsプロジェクト固有の話でありましたが、その中にも業務に活かせるものはないかと気になっていました。 https://speakerdeck.com/yahonda/railsnopull-requestsnorebiyunoshi-nisi-gakao-eteirukoto 特に活かせそうだなと思ったのが、バグなどの報告時の書き方に関してです。 Issues: Expected behavior あなたの望む動作を書く 過去のバージョンでどう動いていたか 私は「望む動作」は記載しますが、「過去にどうだったか」を書くことがほとんどありませんでした。 しかし、これを書くことによって、過去の事実として客観的な情報を伝えることができるので、実践していきたいと思います。 Issues: Actual behavior 再現ケースで実際の動作を肯定文で書く これは「肯定文」で記載することが大切だそうです。 言われてみればその通りで、以下の2点を踏まえると「〇〇(望む動作)をしない」と記載してしまうと情報が一切増えないので、書く意味がありません。 (ISSUEを起票している時点で)問題が起きていること 「望む動作」を記載していること 私自身、このような書き方はしていない(つもり)ですが、改めて意識していこうと思いました。 懇親会 懇親会では、小さなテーブルがたくさん用意されており、4〜5人集まり、親睦を深めるという感じでした。 みなさん、頻繁にテーブルを移動するようで、多くの方と会話することができました。 会話の内容としては、業務を絡めたRailsの話題だったり、今日のセッションの深掘りだったり、最近のトレンド等だったりと、かなり盛り上がり楽しむことができました。 私はもともとJavaで仕事をしていたのですが、そんな境遇の方が何人かおられて、 最近のJava界隈の話をしたり、Javaで使用するフレームワークとRailsの違いや共通点の議論をしたりしてきました。 運営スタッフさんとも同じテーブルになったのですが、その際にも技術的な話題で盛り上がってしまいました。 冒頭のWebアプリケーションのことやスピーカーの選定に関する裏話を聞く貴重なチャンスだったのにそれを活かすことができませんでした。 次回、参加する機会がありましたら、忘れずに聞いてみようと思います。 まとめ 参加してみての感想は、 初心者に対しては、登壇しているスピーカーの方々に経験年数の少ない方も多く励みとなり 中級者に対しては、理解はできるがまだまだ学ばなければならないことが多いことを自覚させられ 上級者に対しては、より深い領域で刺激しあえる イベントの趣旨にある通り、だれでも楽しめるイベントであったと思います。 私自身は中級者と自負しておりますが、上級者になるためにはまだまだ先は長いことを再認識させられ、改めて努力し続けるモチベーションを得ることができました!! 講演の内容はYouTubeで公開されていますので、興味があれば見てみてください。 https://www.youtube.com/c/kaigionrails
Kaigi on Rails 2024に参加してみた〜事前イベント編〜
※本記事はKaigi on Rails2024の当日までに執筆したものです。 みなさん、こんにちは小林です。 いきなりですが、Kaigi on Railsというイベントをご存知ですか?「初学者から上級者までが楽しめるWeb系の技術カンファレンス」というコンセプトで開催されているイベントになります。 実はこれまでに参加したことがなかったのですが、今年は機会に恵まれ、参加してみることにしました。 しかし、社内にも参加したことがある方がいなかったので、どのようなイベントなのかがわからず、少し不安も感じていました。そんな折にちょうど良い事前イベントを見つけたので、まずはそちらに参加してみました。 https://andpad.connpass.com/event/332751/ こちらはアンドパッド様とAsakusa.rb様の主催でタイムテーブルを解説してくださるイベントでした。運営の方々がスピーカーとして各セッションの見どころ等の解説と、どのようにスピーカーを選定したのかという小話があり、とても面白かったです。 選定は運営の方々で応募があった内容を点数化し、議論して決めるとのことでした。その中でも特に採用したいものは、採用したい人が他のメンバーに熱く語って説得させるというもので、かなり熱がこもったイベントだなと感じました。 個人的に一番ためになったのは、「有明近辺に詳しくない人向けの歩き方ガイド(仮)」でした。これがなければ当日迷子になっていたかもしれません(笑)。 実はこのイベントはKaigi on Railsの公式ホームページに掲載されていました。公式がこのようなイベントを紹介してくれるのは、初参加の人たちにはとてもありがたいことです!! あとはどのセッションに参加するかを決めて、当日に臨むだけとなります。次回は参加した感想をまとめますので、そちらもご期待ください。
TryHackMeでハッキング入門してみた
初めまして!昨年の10月に入社しました山屋と申します。 ご挨拶が遅くなりましたが、これからよろしくお願いいたします。 今回はセキュリティ初学者がサイバーセキュリティ学習サイトのTryHackMeを利用したので、その紹介をさせていただこうかと思います。 TryHackMeとは? サイバーセキュリティやハッキングについて学べるwebサイトです。 https://tryhackme.com/ その名の通り、テスト環境で実際にハッキングしながらサイバーセキュリティの学習ができるというのが特徴です。 学習者のコミュニティがあったり、利用日数に応じて特典がもらえるというゲーム的要素があったりと、飽きさせない工夫がされています。 無料で利用できますが、一部有料版限定の学習コンテンツなどがあります。金額は2024年9月現在では14ドル/月です。 学べること 以下のようなことが学べますが、まだまだたくさんあります! サイバーセキュリティ Linux ネットワーク OS サイバーディフェンス ハッキング手法 ハッキングツールの使い方 学習の流れ 何から学べば良いのか?となった場合でも、登録時にアンケートに答えるとおすすめのコース(Learning path)を表示してくれるので心配はいりません。例えば、画像のようなLearning pathが用意されています。 Learning path内にはRoomというものが用意されています。Roomでは説明を読み、ときにはブラウザ上のマシンを動かしながら問題演習ができます。このRoomをクリアしていくという流れです。 例えばこのようなRoomがあります。 https://tryhackme.com/r/room/howwebsiteswork 実際に学んでみての感想 良かったところ 実際に手を動かして学習できる これが一番良かったことかなと思います。 特に実際にハッキングできたのは楽しかったです。(言うまでもないことだと思いますが、他のサイトに対して行なったら犯罪です!) 脆弱性のあるportを発見して侵入し、管理者権限を入手する…といった一連の流れを体験できます。 ゲーム感覚で楽しく学習できるのが推しポイントですね。 環境構築不要 ブラウザ上でマシンを起動してハッキングが行えるので、自分で環境を用意する必要はありません。お手軽ですね。 もちろんVPNを使って自分のマシンから学習を進めることもできます。 分かりやすく、網羅的に学べる アニメーションなどを使って、インタラクティブに学ぶこともできます。 例えばどういった流れで通信が行われているのか、というのをアニメーションで学ぶことができたのでわかりやすかったです。ネットワーク機器の有無で通信がどう変わるのか?というのを見ることができます。 また個人的な話になりますが、私は今応用情報技術者試験の学習をしています。 勉強していると、「これTryHackMeでやったところだ!」となることがよくあります。(例えばOSI参照モデルやネットワーク構成、TCP/IPなどですね)これらが実際にどのように使われているかということを知った状態で学ぶことができるのは大きなメリットだと感じました。 Learning pathに沿って学んでいけばサイバーセキュリティに関する知識を得ることができると思います。セキュリティそのものだけではなく、ネットワーク等の知識も基礎から網羅的に学べるので、一番勉強になったと感じました。 気になるところ 英語のみである 英語が苦手な人だと苦戦するかもしれません。(私は英語があまり得意ではないので大変でした…) 場合によっては自動翻訳を使うことも検討した方が良いと思います。 マシンが遅い ブラウザで使えるマシンの動作が遅いことが気になりました。 私は試しませんでしたが、自分のマシンを使って学習を進めることもできるので、そちらで解消できるかもしれません。 無料版だと学べることが限られる 2024年9月現在では853個のRoomがあり、そのうち無料のRoomは約555個でした。(個数が間違っていたらすみません!) 学習初期におすすめされたごく基礎的なLearning path内にも有料のRoomは含まれていて、しっかり学習するには有料版の方が良いかもしれません。 私は2ヶ月ほど有料版に登録していましたが、基礎的な部分を深掘りできたので個人的にはお金を払う価値はあったかなと思います。ただ、無料会員に戻ると自分が過去に学んだ有料限定のRoomは見れなくなる点には注意が必要です。 まとめ TryHackMeで学習した感想などを書かせていただきました。サイバーセキュリティに関連することを基礎から学ぶことができ、非常に学習の助けとなりました。もし興味がありましたら、無料版からぜひ始めてみてください! 最後までお読みいただきありがとうございました。
Macの環境構築用のシェルスクリプトを作成しました
こんにちは! 冨田です。 久しぶりに英語学習を再開しようと思い、現状のレベルを測るために、初めてTOEICを受けてきました! TOEICには合格・不合格といった基準はないので、内心ホッとしていますが、点数を高くできるように勉強していこうと思います! 概要 今回は、初期状態のMacで開発環境を迅速に構築するためのスクリプト「auto_setup_dev」を作成してみました。 新しいMacが手元に届いたら、必ず必要になる開発環境のセットアップ。しかし、日頃行う作業でもないため、何をしていたか忘れてしまいがちです。 忘れてしまうのであれば、GitHubに環境構築用のシェルスクリプトを用意しておけばいいのではと思い、作成してみました! 作成 こちらが、作成したファイルとなります。 必要な項目ごとに、シェルスクリプトを分けて作成しました。 prezto_set_up.sh homebrew_install.sh install_brew_packages.sh envs_set_up.sh peco_set_up.sh install_latest_versions.sh prezto_set_up まずは、ターミナルの環境を見やすくするためのpreztoをインストールするシェルスクリプト。 preztoの公式は、こちらです。 zshのフレームワークで、CLI環境をリッチにしてくれます。(プロンプトテーマ、自動補完、etc) homebrew_install, install_brew_packages HomebrewのインストールとHomebrewから必要なアプリケーションをインストールするためのシェルスクリプト。 今回、Homebrew経由で色々なアプリケーションをインストールできることを知ったので、まとめてインストールできるように作成してみました! Homebrew経由でパッケージとアプリをインストールできます。 皆さんも、一つひとつをインストールするのが面倒だなと思ったら、一括でHomebrewでインストールするのはいかがでしょうか? envs_set_up anyenvをインストールし、rbenvとnodenvをインストールしています。 開発環境でDockerを使うようになったので、必要なくなってきたような気もしますが、、、 念の為、作成しました! 最近は、asdf というバージョン管理ツールも出てきているそうなので、そちらに変更するか悩み中です。 peco_set_up ターミナル操作を速くするためのツールです pecoの公式は、こちらです。 コマンド操作が苦手で、コマンドを覚えられない私には、とても便利なツールなので、シェルスクリプトを作成してみました。 コマンド操作が苦手な人にはとてもおすすめなツールです! install_latest_versions RubyとNodeの最新版と、nodenv-yarn-install(yarnを自動的にインストールするプラグイン)を インストールしています。 開発環境によってバージョン指定は異なると思いますが、最新版を入れておくためのシェルスクリプトを作成しました。 今回、nodenv-yarn-installというプラグインを見つけたので、そちらもいれてみました! まとめ 初回環境構築を簡単にするためのシェルスクリプトを作成することで、Linuxの基礎知識の1つを学べることができました。 作成するのに時間がかかった割には、実行機会は少ないので残念ですが、、、 また、困ったことや閃いたことがあれば、作成していきたいと思います! 最後まで、お付き合いいただきありがとうございました。
GitHubで経歴書を作成してみた
こんにちは! 最近、仕事着に何を着るか迷うので、すべて作務衣にしようかと考えている冨田です。 エクセルやスプレッドシートを使用して作成する経歴書ですが、管理が面倒で毎回どこにいったか忘れがちです。(私だけかもですが…) GitHubで管理できたら、わかりやすいのでは?と思い、作成してみました。 概要 GitHubの機能を使用して、自身の経歴書を公開・PDF保存できるサービスを作成してみました。 作成したものは、以下になります。 リポジトリ Webサイト リポジトリは、GitHub PagesとGitHub Workflowsを用いており、 GitHubリポジトリに格納されているファイルをそのままWebサイトとして公開(GitHub Pages) 経歴書をPDF形式で保存(GitHub Workflows) ができるようになっています。 作成 基本的な流れは、GitHub Pagesサイトを作成するを見て作成できます。 ざっくりした流れは下記になります リポジトリの用意:github.ioドメイン、もしくは独自ドメインを使用。 サイト用ファイルの作成:HTMLもしくはMarkdownファイルを作成。 リポジトリの設定:GitHubリポジトリの設定から、デプロイするファイルを指定します。 サイト用ファイルの作成 以下のようにHTMLファイルを作成していきました <!doctype html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>職務経歴書 冨田久樹</title> <link rel="stylesheet" href="css/index.css" /> </head> <body> <div class="container"> <div class="title"> <h1>職務経歴書</h1> </div> <div class="profile-content"> <h2>プロフィール</h2> <div class="profile"> <div class="info"> <h3>基本情報</h3> GitHub Actionsを使用したPDF保存 PDF保存には以下のツールを使用しました: puppeteer:HTMLからPDFに変換するためのライブラリ。 fonts-ipafont-gothic:日本語フォントに対応するため。 cheerio:PDFのファイル名をサイトタイトルに対応させるため。 以下は、GitHub Actionsのworkflowの一部です: name: Convert HTML to PDF on: push: branches: - main workflow_dispatch: jobs: convert: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 # PDFファイル保存するための処理を記載 - name: Upload PDF uses: actions/upload-artifact@v4 with: name: ${{ env.PDF_TITLE }} path: ${{ env.PDF_TITLE }}.pdf GitHub Actionsを使用して、好きなときにPDFを保存できるようにしました。 苦労したポイント 特に苦労したのは、サイトタイトルをPDFファイル名に指定する部分と、HTMLをPDFに変換する部分です。使い方がわからず、悪戦苦闘しましたが、以下のように解決しました: サイトタイトルの抽出:cheerioを使用してHTMLからタイトルを抽出し、環境変数として保存。 HTMLからPDFへの変換:puppeteerを使用して、指定したHTMLファイルをPDFに変換。 - name: Extract Title id: extract run: | TITLE=$( node -e " const fs = require('fs'); const cheerio = require('cheerio'); const html = fs.readFileSync('${{ github.workspace }}/index.html', 'utf-8'); const $ = cheerio.load(html); const titleHtml = $.html('head title'); const titleText = cheerio.load(titleHtml)('title').text(); console.log(titleText.replace(/ /g, '_')); ") echo "PDF_TITLE=${TITLE}" >> $GITHUB_ENV - name: Convert HTML to PDF run: | node -e " const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('file://${{ github.workspace }}/index.html', {waitUntil: 'networkidle2'}); await page.pdf({path: '${{ env.PDF_TITLE }}.pdf', format: 'A4'}); await browser.close(); })(); " まとめ 今回、GitHubの機能をつかうことで、簡単にサイト公開やPDF保存機能を作ることができるようになりました! また新しいことを思いついたら、作ってみたいと思います。 最後まで、お付き合いいただきありがとうございました。
[オブジェクト指向]メッセージ基点の設計で柔軟性を高める
こんにちは。お久しぶりです。浅野です。 最近、「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本を読みました。いくつか新しい発見がある中で、「設計において重要なのは、ドメインオブジェクトよりもメッセージだ」という主張が印象に残ったので、アウトプットしていきたいと思います。 本の紹介 今回読んだ「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本は、オブジェクト指向設計についての重要なエッセンスが分かりやすくまとまった一冊です。タイトル通りサンプルコードがRubyで書かれており、非常に理解しやすかったです。 前提 本書によると、オブジェクト指向設計の目的は、「あとにでも」設計できるようにすることです。そしてオブジェクト指向設計とは、「依存関係を管理すること」だと主張します。変更を許容できるようなかたちでオブジェクトの依存関係を構成することで、未来の新たな要求にも柔軟に対応できるようになります。 本題 本書の主張では、「設計においては、ドメインオブジェクトよりもメッセージが重要」とされています。ドメインオブジェクトに集中しすぎると、オブジェクトに不自然な振る舞いを強いることがあります。しかし、メッセージを起点に設計を考えることで、必要なオブジェクトを特定し、適切な振る舞いを実現することができます。 事例 本書では、ある自転車旅行会社の事例をもとに説明されています。旅行には、参加者の上限と体力に応じた難易度が設定されています。自転車は持ち込みも可能ですが、旅行会社から借りることもできます。今回は、「参加者が、適切な難易度の、特定の日付の、自転車が借りられる旅行の一覧を見たい」というユースケースについて考えていきます。 必要なドメインオブジェクト 上記の例では、以下のドメインオブジェクトの必要性が見えてきます。 参加者(Customer)旅行(Trip)行程(Route)自転車(Bicycle) それでは、これらのオブジェクト同士がどのようなやりとりをしていくのか考えていきたいと思います。 シーケンス図の利用 著者は、オブジェクト同士のメッセージを考えるのに最適な方法が「シーケンス図」を書くことだと主張します。これにより、どのオブジェクトがどんなメッセージを送受信するかが明確になります。例えば今回のユースケースでは、CustomerであるAliceとTripは、以下のメッセージのやりとりを行うことが想定できそうです。 ここでは、CustomerであるAliceがTripに向けてsuitable_tripsというメッセージを送っています。このメッセージはon_date, of_difficulty, need_bikeという3つのパラメータを要求します。これはAliceがTripクラスに適切な難易度の、特定の日付の、自転車が借りられる旅行を見つけてくれることを期待しています。 誰がメッセージに応答すべきか しかし、ひとつ違和感を覚えます。Tripは日程と難易度によって旅行を見つける責任を負うのは妥当に見えますが、自転車が利用可能かどうかを調べる責任を負うべきでしょうか?Tripの責任領域ではないように思われます。では、誰が適任でしょうか?先程考えたドメインオブジェクトからはBicycleクラスの方がより適切に見えます。その場合のシーケンス図は以下でしょうか。 今回の改善によって、Tripから余計な責任を取り除くことができました。しかし単にそれをCustomerに移しただけとも言えます。Aliceは適切な旅行を見つけたいだけにも関わらず、その旅行が具体的にどのようなものか(例えば、自転車が借りられる旅行か)まで知ってしまっています。つまり、Aliceが必要以上にシステムの内部構造を理解しているということです。これではあまりいい設計とは言えません。 必要なオブジェクトを見つける これまでCustomerが送るsuitable_tripsメッセージに対する適切な受け手を考えてきましたが、既存のドメインオブジェクトからは見つけることができませんでした。新しいオブジェクトの導入が必要そうです。その新しいクラスには、適切な旅行を見つけるという責任を担ってほしいため、TripFinderと名付けましょう。シーケンス図は以下となります。 TripFinderは、適切な旅行の条件に関する知識を全て持ち合わせ、このメッセージに応答するために必要なことを何でも行います。さらに、安定したパブリックインターフェースを提供する一方、変化しやすい内部実装の詳細は隠蔽しています。いまや、TripFinderはCustomerから独立し、他のオブジェクトからも利用可能となりました。将来他の旅行会社からWebサービスを介してTripFinderを利用したいという要求が出てきた場合にも、柔軟に対応できます。このアプローチは、システムの拡張性と柔軟性を大幅に向上させています。 まとめ 今回はメッセージ中心のアプローチを取り、アプリケーションの設計に取り組んできました。「どのオブジェクトがこのメッセージに応答すべきか」という視点で考えた結果、本当に必要なオブジェクトを特定することができました。オブジェクトは存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトが存在するのです。シーケンス図は、このプロセスでメッセージに集中することを助けてくれました。もしドメインオブジェクトにのみ焦点を当てていた場合、今回のような解決策は見逃されていた可能性が高く、オブジェクトに無理な振る舞いを強いた複雑な設計になっていたかもしれません。メッセージを中心とした設計により、システムの構成要素間の関連を適切に把握し、より効率的で理解しやすいものにすることができます。 参考 Sandi Metz, "オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方"
Railsマイグレーションの管理:schema.rbのコンフリクトを避けよう
こんにちは、冨田です。 2024年のF1シーズンが開幕し、今年もレッドブルが一強になるのかどうか、楽しみにしているところです。 先日、MigrationファイルのコンフリクトをGitHub上で修正しようとしたら、CIに怒られてしまいました。 原因を調べていく中で、GitHubとRailsのMigration及びschema.rbについて勉強することができたので、ブログにしました。 概要 dev1ブランチ(commit-4)でafterオプションを使って新しいカラムを特定の位置に追加するMigrationファイルを作成 Migrationファイル add_column :posts, :content, :text, after: :title, comment: '内容' schema.rb t.string "title", null: false t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false dev1ブランチ(commit-4)から派生した後続ブランチ(dev2, etc)で開発を進めていました。 後にafterオプションが特定のデータベース環境でサポートされていないことがわかり、dev1ブランチに新たなコミット(commit-7)を追加し、Migrationファイルとschema.rbを以下のように修正しました。なお、この変更は後続ブランチには反映していませんでした。 Migrationファイル add_column :posts, :content, :text, comment: '内容' schema.rb t.string "title", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 featureブランチへのマージ過程で、Migrationファイルのコンフリクトが発生。 見逃しポイント: schema.rb の修正はコンフリクトが発生しておらず、見逃していた。 GitHub上でこのMigrationファイルの修正。 その後、CIの実行中にyou can't define an already defined columnのエラーが発生しました。 結果 schema.rbに重複したカラムが生成されてしまっていました。 t.string "title", null: false t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 原因 GitHubでMigrationファイルを修正した際、ローカル環境でschema.rbファイルを更新しなかったため、dev1ブランチのschema.rbは修正後の状態になっていましたが、dev2ブランチ以降は修正前の状態を維持していました。GitHubの履歴は同じ場所の変更でないため、schema.rbはコンフリクトの対象にならず、結果としてdev1とdev2ブランチのschema.rbの変更が両方とも反映されてしまいました。 [dev1ブランチ]schema.rb t.string "title", null: false, "タイトル" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 [dev2ブランチ]schema.rb t.string "title", null: false, "タイトル" t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false まとめ Migrationファイルに関わる変更は、ローカル環境で必ず確認することをおすすめします。それは、schema.rbの修正・変更にも関わってくるからです。 今回の失敗から、GitHubとRailsのMigration及びschema.rbについて理解を深めることができました。 最後までお付き合いいただき、ありがとうございました。また、新たな学びがあれば、ぜひ共有したいと思います!
動的型付け言語と静的型付け言語を理解してみる
年末までに10kgダイエットと言いながら、8kgで目標未達になってしまった冨田です。 今回のテーマについて深く学習をしてこなかったので、何が違うのかを改めて調べてみました。(TypeScriptに何故か苦手意識があり、避けてきたので...) 静的型付け言語: MDN Static typing 静的型付け言語(例えば、Java, C, C++)とは、コンパイル時に変数の型がわかる言語です。静的型付け言語の多くは、プログラマが明示的に型を指定 動的型付け言語: MDN Dynamic typing 動的型付け言語とは、(JavaScript のように) インタプリターが実行時に、変数の値 に基づいて、変数 にデータ型 を割り当てる ※インタプリター型言語とコンパイラー型言語で動的・静的型付け言語を判断しているわけではないので注意! メリットとデメリット 詳細な部分までは、触れない。 エラーのタイミング 動的型付け言語の場合、実行時。 静的型付け言語の場合は、コンパイル時(意味解析の型検証時)。 コード量 動的型付け言語の場合は、少ない。 静的型付け言語の場合は、多い。 コンパイルなどの機械語の部分は、省略する。(機械語部分の詳細は、これから勉強していきます!) RubyとTypeScriptをつかって、同じコードを書いてみる。 TypeScript 明示的にどの型が必要なのかを定義する。(number型しか入りませんよ〜) class Calculator { add(x: number, y: number): number { return x + y; } } ※実行する際は、下記の流れで実行する。(node.jsとTypeScriptコンパイラが必要です) //コンパイル中にエラーがない場合、jsファイルが生成される // --noEmitOnError がないと、エラーがあっても生成されます $ tsc --noEmitOnError ファイル名.ts // 対象ファイルを実行する $ node ファイル名.js 正しい型の引数を渡した場合 下記のように、計算結果を出力する const calculator = new Calculator(); const result: number = calculator.add(3, 5); console.log(result); // 8 定義した型と異なる型を引数に渡した場合 const calculator = new Calculator(); const result: number = calculator.add("aaa", 5); console.log(result); JSへコンパイルするときにエラーが発生し、JSファイルを生成しない。 $ tsc typescript.ts typescript.ts:8:39 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. const result: number = calculator.add('aaa', 5); number型に定義したパラメータにstring型の引数を割り当てられないというエラーを、プログラム実行前のコンパイル時に出力する。 Ruby 暗黙的に型を引数に定義することになる。(もちろんInt型ですよね?) class Calculator def add(x, y) x + y end end 正しい型の引数を渡した場合 calculator = Calculator.new result = calculator.add(3, 5) puts result # 8 異なる型を定義した場合 calculator = Calculator.new result = calculator.add('aaa', 5) puts result 実行後にエラーが出力される。(実行するまで、エラーが発生しない) ruby.rb:3:in `+': no implicit conversion of Integer into String (TypeError) x + y ^ from ruby.rb:3:in `add' from ruby.rb:8:in `<main>' まとめ どちらが良いという判断ではなく、「どちらも使い道によっては...」と要素が大きく感じました。 型注釈をコストと考えるか、投資と考えるか? サバイバルTypeScriptより 静的型付け言語 チーム開発や大規模開発向け 型情報によって、可読性が良くなる 動的型付け言語 個人開発やスピード感が求められる開発 すぐプログラムを実行できる エンジニア経験の浅い私としては、静的型付け言語のほうが、IDEでエラーやタイポに気づきやすいといった点でこっちのほうが見やすいのかな?と思った気づきとなりました。 (動的型付け言語でも検出できる機能やIDEはあるかと思いますが) 以上、最後までお付き合いいただきありがとうございました!
AWSにおけるアカウントとは?
こんにちは、小林です。 先日、自社でも現場でも管理しているAWSアカウントを整理する必要がありました。 その際に「AWSアカウントのルートユーザー」という単語を出したときに、伝わらなかった(伝え方が下手だったというのもあります)ので、改めて、整理・周知しようかと思った次第です。 というわけで、今回のテーマはこちらです。 AWSにおけるアカウントとはなにか? アカウントとは? そもそも、アカウントとは何でしょうか? Wikipediaによると、以下のように定義されています。 コンピュータ用語でのアカウント (英: account) は、ユーザーがネットワークやコンピュータやサイトなどにログインするための権利のことである。ユーザーに割り当てられたアカウントをユーザーアカウントとも呼ぶ。 引用:アカウント AWSにおけるアカウントとは? AWSという文脈では、アカウントというと以下の3つが登場します。 AWSアカウント ルートユーザー IAMユーザー それでは、ひとつずつ説明をしていきます。 AWSアカウント AWSとの契約の単位。要は1契約につき、1アカウント付与されます。 AWS的にはリソースのコンテナであり、リソースのセキュリティ境界である、と定義されています。 ルートユーザー AWSアカウントの所有者。AWSアカウントを作成する際に使用したメールアドレス。 このメールアドレスとパスワードを利用して、AWSアカウントにログインすることができますが、普段使いするアカウントではありません。 最初のIAMユーザーを作成したり、契約に関する設定を行ったりすることに使用します。 AdministratorAccessの権限を持つIAMユーザーとは異なります。 IAMユーザー 普段、S3のバケットを作成したり、CloudWatchのログを確認したりするために、作業者一人ひとりに払い出されるアカウントです。 おまけ ちなみに、クラスメソッド様経由でAWSを契約すると、initial-admin-user-123456789012のようなユーザーを最初に払い出していただきますが、これもIAMユーザーです。 ルートユーザー自体はクラスメソッド様にて管理していただいています。 さいごに 私自身はたまたま以下の機会があったため、これらを明確に区分することができましたが、普段の業務でIAMユーザーしか使用する機会がないのであれば、意識することもないでしょう。 自身でAWSと契約した クラスメソッド様経由でAWSと契約した これを機に理解してもらえればと思います。 参考資料 AWS アカウント管理リファレンスガイドへようこそ AWS アカウントのルートユーザーの使用 IAM ユーザー
WordPressで構築したブログをAstroに移行してみた
こんにちは。 18年ぶりに阪神タイガースがアレしたので、嬉しくてたまらない小林です。 最近、同僚にAstroというWebフレームワークを教えてもらってからというもの、面白くて仕方なく、毎週何かしら触って遊んでいます。 弊社のブログでは、Astroに関して初めて触れるのですが、それが何かというは割愛します。 Astroって何?という方は、以下を参照してください。 はじめに チュートリアル ある程度、Astroを理解している人向けの内容になります。 はじめに 今回のテーマは、 WordPressで運用されているブログをAstroに移行する です。 以下のようなことを検討している場合に有益なのではないかと考えています。 WordPressでの運用をやめたい 記事のレビューをGitHubのPullRequestで行いたい そもそも、WordPressとAstroを組み合わせるという話になると主に2つの方法があります。 WordPressに組み込まれたREST APIを使用する 一つ目は、ビルド時にWordPressにアクセスして、取得したデータを元にブログ用のページを生成する方法です。 参考:Headless WordPress & Astro WordPressのデータをMarkdown変換して使用する 二つ目は、WordPressのデータをMarkdownに変換し、チュートリアル内にもあったようにプロジェクトの一部として管理する方法です。 参考:Migrating from WordPress 今回は、目的が「WordPressをやめたい」なので、後者を選択します。 応答に時間が掛かる、サーバーの負荷を減らしたいみたいな場合は前者を選択した方がよいと思います。 ブログをWordPressからAstroに移行する WordPressから記事のデータをXMLとして出力する まず、WordPressの投稿したデータをXMLファイルに出力します。 WordPressのダッシュボード > サイドバー > ツール > エクスポート を選択します。 「エクスポートする内容を選択」からエクスポートする内容を選択します。 「エクスポートファイルをダウンロード」ボタンを押下するとXMLファイルがダウンロードされます。 % ls -l WordPress.2023-09-19.xml -rw-r--r-- 1 koba-masa staff 2152770 9 19 22:05 WordPress.2023-09-19.xml XMLのデータをMarkdownに変換する 次に、ダウンロードしたXMLファイルを解析して、Markdownとして出力をします。 If you want to move all of your existing post content to Astro, you may find this tool for exporting Markdown from WordPress helpful. You may need to make some adjustments to the result if you have to convert a large or complicated WordPress site to Markdown. 引用:https://docs.astro.build/en/guides/migrate-to-astro/from-wordpress/#switch-from-wordpress-to-astro 公式ドキュメントにlonekorean/wordpress-export-to-markdownという便利なツールが紹介されていましたので、こちらを利用して変換していきます。 上記のプロジェクトをgit cloneします。 % ls -l total 4600 -rw-r--r-- 1 koba-masa staff 198559 9 18 22:11 WordPress.2023-09-18.xml drwxr-xr-x 12 koba-masa staff 384 9 18 22:10 wordpress-export-to-markdown あとはREADMEに使い方が書いてありますので、それに倣って実行します。 % cd wordpress-export-to-markdown % npm install && node index.js Starting wizard... ? Path to WordPress export file? ../WordPress.2023-09-18.xml ? Path to output folder? ../. ? Create year folders? Yes ? Create month folders? Yes ? Create a folder for each post? Yes ? Prefix post folders/files with date? Yes ? Save images attached to posts? Yes ? Save images scraped from post body content? Yes ? Include custom post types and pages? Yes Parsing... 13 posts found. 0 attached images found. 49 images scraped from post body content. Saving 13 posts (0 already exist)... [OK] post - aws-踏み台サーバネットワークの構築 (省略) [OK] IMG_4709-scaled.jpg [OK] wyrd_member.jpg Done, got them all! All done! Look for your output files in: <your_directory> 指定したディレクトリにMarkdownファイルが出力されています。 % cd .. % ls -l total 4600 -rw-r--r-- 1 koba-masa staff 198559 9 18 22:11 WordPress.2023-09-18.xml drwxr-xr-x 3 koba-masa staff 96 9 26 22:29 posts drwxr-xr-x 12 koba-masa staff 384 9 18 22:10 wordpress-export-to-markdown % % find posts -type f post/2021/06/iosのショートカット機能を利用して勤怠管理を行-2/index.md (省略) post/2020/10/aws-踏み台サーバネットワークの構築/images/b8d665222d747a4d68fe084cfa02b58e.png post/2020/10/aws-踏み台サーバネットワークの構築/images/1989aa933c22cc3d909a3d5c949767a4.png post/2020/10/aws-踏み台サーバネットワークの構築/images/ed61dde1b9f396cf9d968414430bde58.png post/2020/10/aws-踏み台サーバネットワークの構築/images/6df701b87120fed4e4338211fc4ea7f7.png post/2020/10/aws-踏み台サーバネットワークの構築/index.md post/Invalid DateTime/Invalid DateTime/index.md % 完成 出力先のディレクトリの中身を丸ごと、src/pages/posts配下に移動し、サーバーを起動します。 % find src/pages/posts/2020 -type f src/pages/posts/2020/10/aws-踏み台サーバネットワークの構築/images/b8d665222d747a4d68fe084cfa02b58e.png (省略) src/pages/posts/2020/10/aws-踏み台サーバネットワークの構築/images/6df701b87120fed4e4338211fc4ea7f7.png src/pages/posts/2020/10/aws-踏み台サーバネットワークの構築/index.md 起動させた結果がこちらになります。 注意事項 実際には上記を実行しただけでは、起動しませんでした。 しかし、技術的に難しいことだったり、面倒なことではありません。 例えば、画像ファイルのパスがMarkdownファイルからの相対パスになってしまっていたので、適切なディレクトリに移動したり、チュートリアルで設定したプロジェクト独自の設定を反映させたりとエラーを読んで対処できるものばかりでした。 感想 移行作業そのものに関しては、良くも悪くもAstroだからという部分はありませんでした。 しかし、こんな簡単に移行できてしまうのかと驚きもしました。 今回は、Astroプロジェクト側にレイアウト等を整えたわけではないので、見栄えという点では微妙ですが、それこそAstroをうまく使いこなせば、良いものができると思います。 参考 AstroでCMSを利用する Migrating from WordPress
MySQLにおけるNOT NULL制約とDEFAULT制約
こんにちは、小林です。 今回は、MySQLのNOT NULL制約とDEFAULT制約について話をしたいと思います。 私はNOT NULL制約に関して、勘違いをして覚えてしまっており、レビュー時にそれを元に指摘をしてしまうことがあります。 それがついに3回目になってしまったので、ブログとしてまとめ、自分の中に定着させようと思い、筆を取りました。 勘違いしていたこと 「MySQLにおいて、NULLが格納されている既存のカラムに新しくNOT NULL制約を付与してもNOT NULL制約違反にはならない」という勘違いをしていました。 思考としては、以下のような感じです。 更新用のDML(INSERT、UPDATE、DELETE)を実行したときに制約は適用される 制約の追加(ALTER)はDDLのため、制約は適用されない ※但し、制約の追加後の最初の登録、更新時に該当カラムにNULL以外の値を入れないといけない 勘違いをしてしまっていた理由は、今回二つあることがわかりました。 制約が適用されるタイミングを誤認していた まず、制約の適用されるタイミングを誤認していました。 過去のプロジェクトで、DMLの実行時にトリガーを発火させるということをしていたことがあり、「トリガーはDMLの実行時に発火する」と覚えてしまっていました。 ※トリガーはDDL実行時にも発火させることが可能です。 そこから、いつの間にかトリガーと制約を取り違えてしまい、「制約がチェックされるのはDMLの実行時のみ」と思い込んでいました。 これにより、DDLの実行時には影響がないと勘違いをしていました。 MySQLとPostgreSQLにてNOT NULL制約の扱いが異なっていた こちらの方が本題で詳細は後述しますが、以下によって勘違いをしていました。 PostgreSQLでは、DEFAULT制約なしのNOT NULL制約を付与するとエラーとなる MySQLでは、DEFAULT制約なしのNOT NULL制約を付与してもエラーとならない ※条件によります MySQLではエラーにならないのだから、制約は動いていない 実際のところ、どうなのか? 検証 検証に使用したSQLはこちらのリポジトリに用意してあります。 koba-masa/play_with_sql 検証手順は、以下の通りです。 テーブルを作成する テーブルにレコードを登録する 新しいカラムを追加する カラムの追加と同時にNOT NULL制約を付与する・・・パターン1 カラムを追加後にNOT NULL制約を付与する・・・パターン2 MySQL パターン1 使用したSQLは、こちら。 mysql> SELECT * FROM not_null_when_add_columns1; +----+-----------+ | id | name | +----+-----------+ | 1 | nobunaga | | 2 | hideyoshi | +----+-----------+ 2 rows in set (0.00 sec) mysql> mysql> ALTER TABLE not_null_when_add_columns1 ADD COLUMN added_column VARCHAR(255) NOT NULL; Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM not_null_when_add_columns1; +----+-----------+--------------+ | id | name | added_column | +----+-----------+--------------+ | 1 | nobunaga | | | 2 | hideyoshi | | +----+-----------+--------------+ 2 rows in set (0.00 sec) mysql> カラムの追加に成功しました。※NUllが格納されている場合は、NULLと表示されます。 パターン2 使用したSQLは、こちら。 mysql> SELECT * FROM not_null_when_add_columns2; +----+-----------+ | id | name | +----+-----------+ | 1 | nobunaga | | 2 | hideyoshi | +----+-----------+ 2 rows in set (0.00 sec) mysql> ALTER TABLE not_null_when_add_columns2 ADD COLUMN added_column VARCHAR(255); Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM not_null_when_add_columns2; +----+-----------+--------------+ | id | name | added_column | +----+-----------+--------------+ | 1 | nobunaga | NULL | | 2 | hideyoshi | NULL | +----+-----------+--------------+ 2 rows in set (0.00 sec) mysql> ALTER TABLE not_null_when_add_columns2 MODIFY added_column VARCHAR(255) NOT NULL; ERROR 1138 (22004): Invalid use of NULL value mysql> カラムを追加した際に、カラムにNULLが設定されています。とエラーが出力されました。 PostgreSQL パターン1 使用したSQLは、こちら。 (結果一部抜粋) play_with_sql=# SELECT * FROM not_null_when_add_columns1; id | name ----+----------- 1 | nobunaga 2 | hideyoshi (2 rows) play_with_sql=# play_with_sql=# ALTER TABLE not_null_when_add_columns1 ADD COLUMN added_column VARCHAR(32) NOT NULL; ERROR: column "added_column" of relation "not_null_when_add_columns1" contains null values play_with_sql=# カラムを追加した際に、カラムにNULLが設定されています。とエラーが出力されました。 パターン2 使用したSQLは、こちら。 (結果一部抜粋) play_with_sql=# SELECT * FROM not_null_when_add_columns2; id | name ----+----------- 1 | nobunaga 2 | hideyoshi (2 rows) play_with_sql=# play_with_sql=# ALTER TABLE not_null_when_add_columns2 ADD COLUMN added_column VARCHAR(32); ALTER TABLE play_with_sql=# SELECT \* FROM not_null_when_add_columns2; id | name | added_column ----+-----------+-------------- 1 | nobunaga | (null) 2 | hideyoshi | (null) (2 rows) play_with_sql=# play_with_sql=# ALTER TABLE not_null_when_add_columns2 ALTER COLUMN added_column SET NOT NULL; ERROR: column "added_column" of relation "not_null_when_add_columns2" contains null values play_with_sql=# カラム追加時にはエラーになりませんでしたが、NOT NULL制約を付与した際にエラーが出力されました。 結論 MySQL NOT NULL制約のあるカラムを追加する際に、DEFAULT制約が設定されていない場合、(SQLモードによるが)その型のDEFAULT値が設定されるため、NOT NULL制約を回避する しかし、カラムを追加後にNOT NULL制約を追加する場合には先にNULLが設定され、そのカラムに対してNOT NULL制約を設定されることとなり、エラーとなる 明示的な DEFAULT 句のない NOT NULL カラムに対するデータエントリでは、INSERT または REPLACE ステートメントにカラムの値を含まれていない場合、または UPDATE ステートメントがカラムを NULL に設定する場合、MySQL はその時点で有効な SQL モードに従ってカラムを処理します。 参考:https://dev.mysql.com/doc/refman/8.0/ja/data-type-defaults.html PostgreSQL DEFAULT制約が指定されていないとNULLが設定されるため、エラーとなる 新しい列にはデフォルト値が初期値として入ります(DEFAULT句を指定しない場合はNULL値が入ります)。 参考:https://www.postgresql.jp/document/14/html/ddl-alter.html#DDL-ALTER-ADDING-A-COLUMN さいごに 毎回、調査して間違っていることに気づくのですが、しばらく経つと忘れてしまい、また指摘しまうという悪循環に陥っていました。 しかし、それもこれで終わりです。おそらく。。。きっと。。。多分。。。
自作した赤外線リモコンの全て - プログラムはChatGPTに書かせた!
最近、電子工作の新たなツールとして手に入れたテスター FNIRSI DSO-TC3の機能を試していたら、赤外線通信のデコード機能に感動しました。そして、思いついたのが自作の赤外線リモコンを作ること。なんと、そのプログラムは人工知能ChatGPTに書かせてしまいました! まずは、この驚きのプロジェクトの一部始終を以下の動画でご覧ください。 http://www.youtube.com/watch?v=xQ1xpEb7Fzg ただサーキュレータの電源をオンオフするだけのシンプルな動作ですが、確実に反応しています。プログラミング部分は実は私ではなく、AIのChatGPTが書いてくれたものなのです。ここではその驚きの過程を紹介します。 赤外線信号のデコード 初めにDSO-TC3を用いて赤外線信号のデコードを行いました。対象としたリモコンのボタンを押すと、以下のようなデコード結果が得られました。 UserCode: 0x80DE DataCode: 0x00FF この結果を基に、同様の赤外線信号を出力するリモコンを作るのが目標です。 電子部品の調達と回路作成 仕事の昼休みを利用して、赤外線LEDとRaspberry Pi Picoを購入しました。選んだのは光波長が940nmの赤外線LEDと、秋月電子オリジナルのRaspberry Pi Picoです。ブレッドボードと抵抗(22Ω)はすでに持っていました。 回路は赤外線LEDを使用しているものの、基本的には普通のLチカの配線と変わりません。 ChatGPTによるコード作成 さて、コードの作成ですが、これはChatGPTにお任せしました。ChatGPTとの対話を記録したスクリプトはこちらのChatGPTのスクリプトからご覧いただけます。 最初に生成されたコードは、さすがはChatGPT、それなりに完成度が高いものでした。しかし、実際にPicoに移して実行してみると、想定した値とは異なるデコード結果が出てしまいました。 UserCode: 0x017B DataCode: 0x00FF それで、この誤った結果を条件に加えてChatGPTに再度コードを生成してもらい、そして期待通りのデコード結果が得られました。以下に、そのコードを一部修正したものを載せておきます。 from machine import Pin, PWM import time def reverse_byte(b): b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4) b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2) b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1) return b def send_ir_code(pwm, code, bits): on = int((2**16-1) / 3) off = 0 # NECプロトコルに基づくIR信号送信 # Header pwm.duty_u16(on) time.sleep_us(9000) # 9ms pulse pwm.duty_u16(off) time.sleep_us(4500) # 4.5ms space # send for i in range(bits): pwm.duty_u16(on) time.sleep_us(560) # 560us pulse # 各ビットはoffの長さで決まる pwm.duty_u16(off) if code & (1 << (bits - 1 - i)): # 1 time.sleep_us(1690) else: # 0 time.sleep_us(560) # Stop bit pwm.duty_u16(on) time.sleep_us(560) # 560us pulse pwm.duty_u16(off) pwm = PWM(Pin(14)) pwm.freq(38000) # IR carrier frequency # カスタムコード 0x80DE, データコード 0x00FF custom_code = (reverse_byte(0x80) << 8) | reverse_byte(0xDE) data_code = reverse_byte(0x00FF) send_ir_code(pwm, (custom_code << 16) | data_code, 32) # 32ビットNECコード 主に使った機材と部品 RP2040マイコンボードキット 5mm赤外線LED 940nm OSI5LA5113A グレー (10個入) FNIRSI DSO-TC3 まとめ: AIによる電子工作 こうして見ると、計測機器とAIの力を組み合わせれば、赤外線通信の専門知識がなくても赤外線リモコンを自作することが可能です。初めての赤外線通信プロジェクトだったので、すんなりと作れるとは思っていませんでした。しかし、AIが生成したコードを読み解くことで、電子工作の知識も深まったと思います。これぞ、最新のDIY体験と言えるでしょう。
サービスやアプリケーション開発における中間層の設計と効果
イントロダクション ライブラリは、アプリケーションやサービスの開発において便利な道具です。しかし、ライブラリを直接使用することにはいくつかの課題や問題点が存在します。そこで、本記事では、ライブラリの使用におけるベストプラクティスとして、ラッパーや中間層の利点について探っていきます。 ライブラリの利用における課題 ライブラリの使用には、以下のような課題があります。 複雑さと柔軟性の問題 ライブラリは、多くの使用ケースを想定して開発されています。そのため、直接使用すると複雑なコードが必要になり、可読性や一貫性の維持が難しくなります。サービスやアプリケーションに特化したより具象的なAPIがあれば、開発効率や保守性が向上します。 ライブラリのバージョンアップの影響 ライブラリはバージョンアップが行われることがあります。しかし、バージョンアップに伴ってAPIが変更される場合、既存のサービスやアプリケーションに影響を与える可能性があります。ライブラリを直接使用する場合、バージョンアップによる修正作業や互換性の確保に時間とリソースが必要になることがあります。 抽象度と具象度のバランス ライブラリは一般化された抽象化されたツールですが、サービスやアプリケーションは具体的な要件やビジネスロジックに基づいて開発されます。ライブラリを直接使用すると、抽象度の高いコードと具象度の高いコードが混在することになり、開発や保守の複雑さが増します。 ラッパー・中間層の導入のメリット ラッパーや中間層を導入することで、ライブラリの利用における課題を解決し、以下のようなメリットを得るこことができます。 サービスやアプリケーションに特化したAPIの提供 ラッパーや中間層を介してライブラリを利用することで、より具象的でサービスに特化したAPIを提供することができます。これにより、開発者はライブラリの複雑さを隠蔽し、よりシンプルなインターフェースを利用することができます。 ライブラリの変更への影響の軽減 ラッパーや中間層を介してライブラリを使用すると、ライブラリ自体の変更がサービスやアプリケーションに与える影響を軽減することができます。ライブラリのバージョンアップや別のライブラリへの移行が必要な場合でも、ラッパーや中間層のみを修正すれば済むため、開発や保守の負荷が軽減されます。 抽象度と具象度のバランスの取れた設計 ラッパーや中間層は、抽象度と具象度のバランスを取りながら設計されます。ライブラリの抽象化された機能を具象化し、サービスやアプリケーションの具体的な要件やビジネスロジックに沿ったインターフェースを提供します。これにより、開発者はより具体的なコードを書くことができ、開発や保守が容易になります。 ライブラリの切り替えやバージョンアップの容易さ ラッパーや中間層を使用すると、ライブラリの切り替えやバージョンアップが容易になります。新しいライブラリが利用可能になった場合や既存のライブラリがメンテナンスされなくなった場合でも、ラッパーや中間層のみを修正すれば済みます。これにより、技術スタックの変更やアップグレードが柔軟に行えます。 まとめ ライブラリの直接使用には課題や問題が存在しますが、ラッパーや中間層を導入することでこれらの課題を解決できます。サービスやアプリケーションに特化したAPIの提供、ライブラリの変更への影響の軽減、抽象度と具象度のバランスの取れた設計、そしてライブラリの切り替えやバージョンアップの容易さを実現することができます。 ラッパーや中間層の導入により、開発効率や保守性が向上し、柔軟な開発プロセスを実現することができます。さらに、ライブラリのバージョンアップや別のライブラリへの移行が必要な場合でも、それらの変更への対応が容易になります。 ライブラリを使ったアプリケーションやサービスの開発において、ラッパーや中間層を導入して利点を最大限に活用しましょう。これにより、持続的な開発と変更への適応性を高め、高品質なソフトウェアを提供できるでしょう。 ラッパー・中間層の設計と実装 ライブラリの利用における課題を解決し、サービスやアプリケーションに特化したAPIを提供するためには、適切なラッパーや中間層を設計し、実装する必要があります。以下では、その設計と実装について詳しく説明します。 ラッパー・中間層の役割と目的の明確化 ラッパーや中間層を設計する前に、その役割と目的を明確化することが重要です。具体的には、以下のような点を考慮します。 ライブラリの機能を具象化し、サービスやアプリケーションに特化したインターフェースを提供すること ライブラリの変更への影響を最小限に抑えること テストやデバッグの容易性を確保すること ライブラリの機能を包括するAPIの設計 ラッパーや中間層の主な目的は、ライブラリの機能を包括するAPIを提供することです。このAPIは、サービスやアプリケーションの要件に合わせて具体化されるべきです。 APIの設計にあたっては、以下の点に留意してください。 シンプルさと直感的な使いやすさを重視すること サービスやアプリケーションの特定の機能やユースケースに焦点を当てること ドキュメントやコメントを充実させ、利用者にとって分かりやすい情報を提供すること テスト駆動開発(TDD)とラッパー・中間層のテスト ラッパーや中間層を設計・実装する際、テスト駆動開発(TDD)を活用することで品質保証を強化し、バージョンアップ時の互換性を確保することができます。 テスト駆動開発(TDD)を通じた品質保証 テスト駆動開発(TDD)は、ソフトウェアの品質を確保するためのアプローチです。この手法では、最初にテストケースを作成し、そのテストケースをパスするためのコードを実装していきます。 TDDをラッパーや中間層の開発に適用することで、次のようなメリットを享受することができます。 ラッパーや中間層の品質を保証し、予期せぬバグや問題の発生を防ぐ 変更時に既存のテストケースを実行し、互換性の問題を検出する 開発者がテストを実行することで、自信を持って変更を加えることができる ラッパー・中間層のバージョンアップ時のテスト ラッパーや中間層を使用する場合、ライブラリのバージョンアップに伴うテストも重要です。ライブラリのバージョンアップは、新機能の追加やバグ修正などの変更が含まれる可能性があります。 ラッパーや中間層のバージョンアップ時のテストには、以下のポイントに留意します。 ライブラリの新機能や修正が、ラッパーや中間層のAPIと互換性を持つかどうかを確認する 既存のテストケースが正常に動作するかを確認する ラッパーや中間層のテストケースを追加して、ライブラリの変更点をカバーする バージョンアップ時のテストは、ラッパーや中間層が期待通りに動作し、ライブラリの変更に対応できることを確認するために重要です。継続的な統合テストや自動化されたテストスイートを使用して、テストを自動化し、効率的に実行することも推奨されます。 ラッパーや中間層のテストを継続的に実施することで、ライブラリのバージョンアップ時に互換性の問題を早期に特定し、迅速な修正が可能となります。これにより、サービスやアプリケーションの安定性と信頼性を確保することができます。 まとめ ラッパーや中間層を設計・実装する際には、テスト駆動開発(TDD)を通じた品質保証とバージョンアップ時のテストが重要です。TDDにより、ラッパーや中間層の品質を確保し、変更時にも互換性を保つことができます。また、バージョンアップ時のテストは、ラッパーや中間層の正常な動作とライブラリとの互換性を確認するために必要です。 適切なテストケースを作成し、テストを継続的に実施することで、高品質なソフトウェアの開発と保守を実現しましょう。 実際の適用事例と効果 AWS S3の中間層の抽象化 AWS S3は一般的にはファイルを保存するストレージとして利用されます。しかし、そのストレージという概念をより抽象化することで、より柔軟な開発とテストが可能となります。ここでは、AWS S3の中間層の抽象化について紹介します。 通常、AWS S3の操作をテストする際には、ローカル開発環境や互換サーバー(LocalStackやMinioなど)を利用する必要があります。しかし、中間層を導入し、ストレージをキーバリューストア(KVS)のようなインターフェースとして扱うことで、ローカルでの開発やテストが容易になります。以下にFactory Methodパターンを用いた具体例を示します。 class KVS { put(key: string, value: any) { throw new Error("not implemented"); } get(key: string) { throw new Error("not implemented"); } } class S3 extends KVS { put(key: string, value: any) { s3client.put(key, value); } get(key: string) { return s3client.get(key); } } class Mem extends KVS { values: Record<string, any>; constructor() { values = {}; } put(key: string, value: any) { values[key] = value; } get(key: string) { return values[key]; } } この例では、KVSを基底クラスとして、S3クラスとMemクラスを実装しています。S3クラスはAWS S3を利用した実装であり、Memクラスはインメモリのキーバリューストアとしての実装です。 このような設計にすることで、以下のような効果が得られます。 開発効率の向上:インメモリの実装を利用することで、テストの実行時間が短縮されます。開発者は高速かつリアルタイムなフィードバックを得ながら、迅速に開発を進めることができます。 テストの容易化:中間層の抽象化により、ローカル環境でのテストが可能となります。これにより、依存関係のあるリモートサービスへのアクセスを避け、テストの安定性と信頼性を高めることができます。 バージョンアップへの柔軟な対応:中間層を通じてストレージの操作を抽象化することで、AWS S3のバージョンアップや他のストレージサービスへの移行が容易になります。既存の中間層の実装を修正することなく、異なるバックエンドサービスに切り替えることができます。 このように、AWS S3の中間層の抽象化によって、開発効率の向上、テスト容易性の向上、バージョンアップへの柔軟な対応が実現されます。中間層の設計と実装には慎重なプランニングとテスト戦略の確立が必要ですが、その努力は柔軟で信頼性の高いアプリケーション開発において大きな価値をもたらします。 以上が、AWS S3の中間層の抽象化に関する実際の適用事例と効果の紹介です。 その他の適用事例と効果 AWS S3の中間層の抽象化以外にも、さまざまな適用事例と効果が存在します。以下にいくつかの例を紹介します。 データベース中間層 データベースはアプリケーションにおける重要な要素ですが、直接データベースにアクセスすると変更が難しくなることがあります。そこで、データベース中間層を導入することで、データベースとの間に抽象化されたAPIを持つ中間層を作成します。これにより、データベースの変更や移行が容易になり、アプリケーションの柔軟性が向上します。 外部APIラッパー 外部のAPIとの連携を必要とする場合、APIラッパーを作成することで柔軟なコントロールとテスト容易性を実現できます。外部APIに依存せず、独自の抽象化されたインターフェースを提供することで、外部APIの変更による影響を最小限に抑え、アプリケーションの信頼性を高めることができます。 キャッシュ中間層 キャッシュはアプリケーションのパフォーマンス向上に重要な役割を果たしますが、直接キャッシュにアクセスするとテストが困難になることがあります。キャッシュ中間層を導入することで、キャッシュとのやり取りを抽象化し、テスト容易性と柔軟性を高めることができます。また、異なるキャッシュストレージ(メモリ、Redis、Memcachedなど)への切り替えも容易になります。 これらの適用事例によって、中間層の利用による柔軟性、保守性、テスト容易性が実現されます。各アプリケーションの要件やニーズに応じて、適切な中間層の設計と実装を行いましょう。 以上が、中間層の抽象化の実際の適用事例とそれによって得られる効果についての紹介です。 まとめ 中間層の設計と実装は、アプリケーションの柔軟性と保守性を向上させるために重要です。実際の適用事例から見ると、中間層は様々な場面で効果を発揮します。既存のライブラリを利用する際には、中間層を通じて抽象化することで、アプリケーションに特化したインターフェースを提供できます。これにより、開発効率の向上、テスト容易性の確保、バージョンアップへの柔軟な対応が可能となります。 中間層の設計と実装においては、適切なテスト戦略を構築し、テスト駆動開発(TDD)を通じた品質保証を行うことも重要です。テストを継続的に実施し、バージョンアップ時の互換性を確認することで、アプリケーションの信頼性を高めることができます。 中間層の導入は、柔軟性、保守性、テスト容易性を向上させ、アプリケーションの品質を確保するための有力な手法です。是非、これらの考え方と実践に基づいて中間層を活用し、高品質なアプリケーションの開発に取り組んでください。 持続的な改善と成長 中間層の設計と実装は、単なる一時的な解決策ではなく、サービスやアプリケーションの持続的な改善と成長にも寄与します。以下に、その重要性と効果について考えてみましょう。 アジャイルな開発と変更への柔軟性 中間層を持つアーキテクチャは、アジャイルな開発プロセスに適しています。中間層を介してライブラリや外部APIとの連携を行うことで、柔軟な変更への対応が可能となります。新しい要件やビジネス上の変更が発生した場合でも、中間層のインターフェースを修正するだけで済みます。これにより、開発者は迅速かつ効率的に変更を反映することができます。 システムの安定性と信頼性 中間層は、システムの安定性と信頼性の向上にも寄与します。中間層を介して行われる抽象化とエラーハンドリングにより、予期しないエラーや障害への対処が容易になります。また、中間層におけるテスト駆動開発(TDD)や品質保証によって、システムの品質を向上させることができます。 結論 中間層の設計と実装は、サービスやアプリケーションの品質や成長において重要な要素です。柔軟性とシステムの安定性を向上させるために、中間層を導入し、適切なテスト戦略を構築することをおすすめします。 ラッパーや中間層の使い分け 最後に、ラッパーや中間層の設計と実装において留意すべきポイントがあります。ラッパーや中間層は便利なツールではありますが、必ずしもすべての場面に適しているわけではありません。適材適所で使い分けることが重要です。 ライブラリを直接利用するべき場合もあります。既存のライブラリが提供する機能や性能が必要であり、ラッパーや中間層を介在させるメリットが少ない場合は、直接利用することが適切です。また、ラッパーや中間層を導入することで複雑性が増す場合や、開発効率に大きな影響がない場合も検討すべきです。 適切な判断をするためには、開発のニーズや要件をよく理解し、設計上のメリットとデメリットを検討することが重要です。また、実装後も適切なテスト戦略を持ち、品質保証を行うことが大切です。 結論 ラッパーや中間層は、サービスやアプリケーションの開発において便利な手法です。適切な設計と実装により、柔軟性、保守性、テスト容易性を向上させることができます。しかし、適材適所で使い分けることが重要であり、開発ニーズや要件に合わせて適切な判断を行う必要があります。 この記事を通じて、ラッパーや中間層の設計と実装の重要性、具体的な手法、適用事例、そして適切な使い分けについて理解していただけたことを願っています。
Composeファイルを拡張する
みなさん、こんにちは。小林です。 今年のGWはどのように過ごされましたか? 私はコーディング三昧の毎日でした。(仕事じゃないですよ。趣味ですよ。) 新しい現場で困っていることがあったので、その回避方法を紹介したいと思います。 今回は、Dockerの話をします。 困っていること 実際に困っていることは、現場固有のものなので少しわかりやすくしていますが、こんな感じです。 本番環境を再現するために、ローカル環境に大量のデータをインポートしないといけなくて、データの入れ替えに時間がかかってしまう。 回避策 Dockerを使用しているので、volumeの設定を切り替えることができればよいのではないか、と考えました。 Git管理されているComposeファイルは以下とします。 # compose.yml version: "3" services: db: image: postgres:14.6 environment: (省略) volumes: - db_data:/var/lib/postgresql/data volumes: db_data: 案1:Composeファイルを編集する 既存のComposeファイルを使用するデータに応じて書き換えるようにしました。 # compose.yml version: "3" services: db: image: postgres:14.6 environment: (省略) volumes: - db_data_prod:/var/lib/postgresql/data volumes: db_data_prod: Git管理されている場合に差分として扱われてしまうので、気軽にgit add .できなくなってしまうので、微妙でした。 案2:Composeファイルをコピーする 既存のComposeファイルをコピーして、データに応じて使い分けるようにしました。 docker-compose -f compose.mine.yml up -d さらに、グローバルの.gitignoreに該当ファイルを設定することで、案1の問題点も解消できました。 # compose.mine.yml version: "3" services: db: image: postgres:14.6 environment: (省略) volumes: - db_data_prod:/var/lib/postgresql/data volumes: db_data_prod: もう少し上手くできないかな、と思って調べたところ、Composeファイルを上書きできるということがわかりました。 Composeファイルの内容を上書きする(compose.override.yml) # compose.override.yml version: "3" services: db: volumes: - db_data_prod:/var/lib/postgresql/data volumes: db_data_prod: Composeファイルを複数指定した際に、各ファイルの内容が上書き・マージされるそうです。 docker-compose -f compose.yml -f compose.override.yml up -d なので、最小限の設定を記載するだけ、案2と同じことができるようになります。 ちなみに、ファイル名をcompose.override.ymlにすると、-fオプションで指定しなくても、compose.ymlと一緒に読み込まれます。 参考 Compose 設定をファイルとプロジェクト間で共有
timestampsマクロとは何者なのか?
みなさん、こんにちは。 今年は久しぶりに花粉の影響が強く苦しんでいる小林です。 また一つ、Ruby on Rails(以下、Rails)の謎が解けたので、紹介したいと思います。 今回のテーマは、こちらです。 timestampsマクロは何をしているのか? timestampsマクロは何をしているのか? timestampsマクロとは何か? timestampsを呼び出すと作成日時(created_at)、更新日時(updated_at)を自動的に作成してくれます。 # db/migrate/2023032562631_create_products.rb class CreateProducts < ActiveRecord::Migration[7.0] def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end sample=# \d products id | bigint | | not null | nextval('products_id_seq'::regclass) name | character varying | | | description | text | | | created_at | timestamp(6) without time zone | | not null | updated_at | timestamp(6) without time zone | | not null | (↑うまく整形できなかったので、諦めました。) Railsガイドにも記載されています。 timestampsマクロは、created_atとupdated_atという2つのカラムを追加します。これらの特殊なカラムが存在する場合、Active Recordによって自動的に管理されます。 引用:Railsガイド#1 マイグレーションの概要 使用する分にはこれで十分なのですが、中で何をやっているのだろうと気になってしまったのが事の始まりでした。 何をしているのか? 結論から言うと、さほど複雑なことはしていませんでした。 active_record/connection_adapters/abstract/schema_definitions.rb#L460-L469 def timestamps(**options) options[:null] = false if options[:null].nil? if !options.key?(:precision) && @conn.supports_datetime_with_precision? options[:precision] = 6 end column(:created_at, :datetime, **options) column(:updated_at, :datetime, **options) end 要は、自身で以下のように書けるけど、timestampsマクロを使用すれば、記述量が減らせて楽ですよ。ということです。 # db/migrate/2023032562631_create_products.rb class CreateProducts < ActiveRecord::Migration[7.0] def change create_table :products do |t| t.string :name t.text :description t.datetime :created_at, null: false, precision: 6 t.datetime :updated_at, null: false, precision: 6 end end end 最後に 今回は、仕組みが複雑怪奇というよりも、どこで定義されているかが見つけられずにもやもやしていたので、読み込みが足りないなと痛感しました。
RFC2119を斜め読みしてみる
こんにちは。 株式会社ウィルドの豊田です。 いよいよ新年度がスタートしましたね。 昨年度は採用活動に注力しましたが、なかなか結果に繋げられず辛い思いをしました。 今年度は良い一年になることを願って、頑張っていきます! さて、今回のブログはRFC2119(ちょっとだけRFC8174)を、英語力ゼロの私が読んで自分なりに解釈してみたよ。という内容でお届けしたいと思います。 RFC2119って? RFC2119は、1997年に「Key words for use in RFCs to Indicate Requirement Levels」(RFCで使用される要求レベルを示すキーワード)というタイトルで発行されたRFCで、IETF(当時のNetwork Working Group)が発行しています。 この文書では、技術仕様やプロトコルの標準化に関する文書(主にRFC)で使用される、要件レベルを示すキーワードの定義と使用方法が説明されています。 元々はRFC(IETF)での利用を目的としたものでしたが、現在では様々な団体の仕様策定文書の中で利用されています。エンジニアの方々なら比較的目にする機会が多いのではないでしょうか。 ※ちなみに、機能の「仕様策定者」と「利用者」の観点で捉え方が変わるため、今回は「仕様策定者」の観点で解釈しています。 1. MUST まず、"MUST" 。英単語の意味そのままって感じです。 絶対に守るべき条件であることを意味します。 特にこの項目については書くこともないですね。 同じ意味で、"REQUIRED", "SHALL"も利用される場合があります。 2. MUST NOT 次に "MUST NOT"。これは "MUST" の逆ですね。 絶対に行ってはならない禁止事項であることを意味します。 こちらも "MUST" 同様、シンプルにそのままの意味です。 同じ意味で、"SHALL NOT"も利用することができます。 3. SHOULD 次は "SHOULD" ですね。 "MUST" ほどの強制力は無いものの、遵守することを強く推奨する仕様であることを意味します。 守らなくても一応動くように作ってある機能につけるキーワードですかね。 開発側は、この仕様を守らなかった場合に発生するリスクをきちんと説明し、利用者側に正しく判断してもらうための十分な情報を提供しなければいけません。 同じ意味を持つキーワードとして "RECOMMENDED" があります。 4. SHOULD NOT こちらは "SHOULD" の逆の意味ですね。 禁止...とまではいかないものの、明確な理由がない限り利用を避けるべき仕様であることを意味します。 SHOULD同様、仕様策定者は、避けるべき仕様である理由をきちんと説明しなければなりません。 同じ意味を持つキーワードとして "NOT RECOMMENDED" があります。 5. MAY 最後は "MAY" です。 これは、利用してもしなくても、どちらでも良い仕様につけられるキーワードです。 仕様策定者に対する注意事項として「利用者がどちらの選択をしたとしても、問題なく動作するように作り込まなければならない。」みたいなことが書かれていました。 同じ意味を持つキーワードとして "OPTIONAL" があります。 6. Guidance in the use of these Imperatives 一応、キーワードの定義としては "MAY" までなのですが、RFCには続きがあるので、一応サクッと読んでみます。 Imperatives of the type defined in this memo must be used with care and sparingly. In particular, they MUST only be used where it is actually required for interoperation or to limit behavior which has potential for causing harm (e.g., limiting retransmissions) For example, they must not be used to try to impose a particular method on implementors where the method is not required for interoperability. このメモに定義されているタイプのキーワードは、注意深く、控えめに使用されなければならない。特に、相互運用のために実際に必要な場合、または害を及ぼす可能性のある動作を制限する場合(例えば、再送信の制限)にのみ使用しなければならない(MUST)。例えば、相互運用のために必要でない特定の方法を実装者に押し付けるために使用してはならない。 (Translated by DeepL翻訳ツール) (ここでいう「実装者」は利用者のことです) ここまでのキーワードの利用に関する注意事項のようです。 本来SHOULDなのに、MUSTであるかのように書いちゃダメ(嘘をついて実装方法を押し付けちゃダメ)ですよ。って感じですかね。 この文章の中で既に「MUST」が使われているあたり、個人的には洒落がきいてるなーと思います。 7. Security Considerations 最後です。 These terms are frequently used to specify behavior with security implications. The effects on security of not implementing a MUST or SHOULD, or doing something the specification says MUST NOT or SHOULD NOT be done may be very subtle. Document authors should take the time to elaborate the security implications of not following recommendations or requirements as most implementors will not have had the benefit of the experience and discussion that produced the specification. これらの用語は、セキュリティに影響を与える動作を指定するために頻繁に使用されます。 MUSTやSHOULDを実装しない、あるいは仕様でMUST NOTやSHOULD NOTとされていることを行うことによるセキュリティへの影響は、非常に微妙なものである場合がある。文書作成者は、推奨事項や要求事項に従わない場合のセキュリティへの影響について、時間をかけて詳しく説明する必要がある。なぜなら、ほとんどの実装者は、その仕様を作成した経験や議論の恩恵を受けていないからである。 (Translated by DeepL翻訳ツール) (ここでいう「実装者」は利用者のことです) 日本語がちょっと??な部分もありますが、基本的に「"SHOULD" や "SHOULD NOT" などの定義を守らずに実装した場合、セキュリティ面でのリスクは、それほど無いケースが多い」(本当に?)ので、そのあたりの内容は、仕様策定者(ドキュメント著者)側が丁寧に説明すべき。ということみたいです。 ちなみに RFC2119に準拠したドキュメントは、冒頭で以下のフレーズを組み込む必要があるそうです。 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. 確かにSemVerやInfra Standardのページにも、このコメントが組み込まれていました。 キーワードのバリエーション "MUST"のエイリアスとして"REQUIRED"や"SHALL"が定義されているように、キーワードにはバリエーションが定義されていることがわかります。 ただ、実際のところは "MUST" , "MUST NOT" , "SHOULD", "MAY" の4つくらいで十分で、実際にそれだけに限定しているドキュメントもあるようです。(WHATWGの"Infra Standard") ドキュメント毎の説明をちゃんと読まないといけませんね。 大文字とか小文字とか RFC2119では、キーワードを "MUST" や "SHOULD"、"MAY"など大文字で書いていますが、RFC内で「大文字で記述すべし」とは定義していなかったりします。 実はその後2017年に発行されたRFC8174で「大文字の時だけRFC2119のキーワードとしての意味を持つ」と明確に定義してたりするんですが、RFC2119ほど広く受け入れられなかったようです。 (「RFC8174が定義以前の多くの文章(大文字も小文字も混在して使われていた頃)の習慣を維持したい」という動機らしい?諸説アリ) 実際、多くの団体から参照されているWHATWGの"Infra Standard"においては、RFC8174の定義に故意に違反している旨が明記されています。 そのあたり、ChatGPTさんはどのような見解なのか聞いてみました。(有料ユーザーになったので、4.0をただ使いたかっただけ) なるほど。英語の文章として読みづらくなることを嫌ったということなんですね。 まとめ 今回はRFC2119の内容を斜め読みしつつ、他のドキュメントへの影響についても見てみました。 各団体の考えは様々ですが、その人たちが標準化してくれた仕様のおかげで、今のITの世界があると考えると、とても興味深い世界ですね。 ※冒頭のRFCの画像は e-words.jp のサイトから拝借しました m(_ _)m
コードレビューについて考える
こんにちは。浅野です。 私ごとですが、最近入籍しました。苗字が変わりましたが、旧姓はエイリアスとして残り続けているので、このまま「浅野」と名乗り続けていきます。 さて、今回のテーマは「コードレビュー」です。コードレビューにまつわる問題について考え、解決手段の1つとしてGitHubの機能を紹介したいと思います。 コードレビューにまつわる問題 みなさん、コードレビューしていますか? レビューって結構大変ですよね。忙しい時や自分の作業に集中したい時は、ついついなおざりになってしまうこともあるのではないでしょうか? 私の肌感覚ですが、以下はどの開発チームでもよくある問題なのではないかと思います。 レビュー依頼溜まりすぎ問題 レビュアー固定化問題 それぞれ補足します。 レビュー依頼溜まりすぎ問題 あるあるとして、結構共感してもらえると思います。いや〜、本当にレビュー依頼っていつの間にか溜まっているんですよね。 やりがちではあるものの、単純にチームの開発スピードを低下させるので良くないです。1つのプルリクエスト(以下、PR)がマージされないことで、他の作業に影響が出て、前に進めなくなってしまう、なんてことにもなりかねません。 レビュアー固定化問題 これもあるあるですよね、きっと。レビューを後回しにする → 他の人がレビューしてくれる、というサイクルを繰り返していると、毎回同じ人がレビューしているという事態になりかねません。 これの何がまずいかって、ナレッジが共有されないことだと思います。コードレビューをしていると、さまざまな発見があります。そういった知見が共有されないままだと、チーム全体としてのコードの質が向上しません。 原因 こうした問題は、どうして発生してしまうのでしょうか。私の所感ですが、こんな個人の気持ちが影響しているように思います。 「自分がやらなくても、誰かやってくれるでしょ!!」 ああ、よくない!よくない!でも、つい思ってしまう!! 特にレビュアーにアサインされている人が多いと、甘えがちになってしまうかもしれません。 改善策 GitHubには、レビュアー自動割り当て機能というのがあります。現在所属しているチームで最近導入されたのですが、上記の問題へのアプローチとしてとても良いと思いました。 レビュアー自動割り当て機能の概要 毎回レビュアーを自動で設定してくれる機能です。レビュアーの人数も指定できます。 自動割り当てのアルゴリズムは、ラウンドロビンとロードバランスの2つから選べます。ラウンドロビンは順番に割り当てていくのに対し、ロードバランスは30日間で同数となるようです。 期待する効果 レビュアーの人数が減ることで、「自分がレビューしなければいけない」という状況が強制的に生まれます。その結果、レビュアーの当事者意識が高まりそうです。また、レビュイーの立場としても「誰かレビューしてください」ではなく、「◯◯さんとXXさん、レビューしてください」と言えるので、より早くレビューしてもらえそうな気がします。 加えて自動でレビュアーが決められるので、誰に依頼するか悩まずに済む点も良いです。 導入方法 ざっくりですが、大きく2つの手順が必要です。 Teamの作成 自動割り当ての設定 詳細は、ドキュメントで確認してください。 使用方法 PRのレビュアーに、作成したTeamを指定してください。するとTeam内のランダムで選ばれた人が、レビュアーにアサインされます。 副次的効果 今まではチーム全員をレビュアーとしてアサインしていたので、一人ひとり探すのが手間でした。この機能を使うと、ワンクリックで終わるので楽チンです。 最後に この機能を使うようになって間もないので、正直なところどれほど効果があるのかは分かりません。それでも、メンバーの意識を変えやすくする機能ではあると思っています。
EC2内のログファイルをCloudWatchログに出力する
こんにちは。小林です。 今年の夏は気温が高く、過ごしづらかったですが(毎年思う)、それも終わり、ブログの執筆が捗る涼しい季節となってきました。 それでは今回の内容ですが、 タイトルの通り「EC2(AmazonLinux2)内のログファイルをCloudWatchログに出力する」方法を紹介します。 ※EC2やIAM、CloudWatchに関する基本的なことは理解している前提とします。 はじめに もともと、EC2インスタンス内に出力されているログファイルを日に一度S3にアップロードをしていたのですが、ログ内に特定の文字列が出力された場合に通知をしたくなったことが事の発端です。 問題点 現状では以下の問題があります。 日に一度のアップロードのため、リアルタイムでの検知ができない (仮に1秒ごとにファイルをアップロードしたとしても)S3に格納されたファイルを解析する必要があり、手間がかかる 解決策 色々調べたところ、CloudWatchエージェントというものを使用して、CloudWatchログに出力することができる、ということがわかりました。 CloudWatchログに出力することができるのであれば、CloudWatchアラームにて文字列を検知すればよいので、かなり楽に実現することができます。 ※CloudWatchログに出力するところまでとし、アラーム通知の設定に関しては割愛します。 CloudWatchエージェント CloudWatchエージェントとは 統合 CloudWatch エージェントを使用すると、以下のことを実行できます。 オペレーティングシステム全体で Amazon EC2 インスタンスから内部システムレベルのメトリクスを収集します。このメトリクスには、EC2 インスタンスのメトリクスに加えて、ゲスト内メトリクスを含めることができます。収集することができる追加のメトリクスについては、「CloudWatch エージェントにより収集されるメトリクス」を参照してください。 オンプレミスサーバーからシステムレベルのメトリクスを収集します。これには、ハイブリッド環境のサーバーや によって管理されていないサーバーも含まれる可能性がありますAWS カスタムメトリクスは、StatsD および collectd プロトコルを使用して、アプリケーションまたはサービスから取得します。StatsD は、Linux サーバーと、Windows Server を実行するサーバーの両方でサポートされています。collectd は、Linux サーバーでのみサポートされています。 Linux または Windows Server を実行している Amazon EC2 インスタンスおよびオンプレミスサーバーから、ログを収集します。 引用:CloudWatch エージェントを使用した Amazon EC2 Instances インスタンスとオンプレミスサーバーからのメトリクスとログの収集 要するに、 EC2インスタンスやオンプレミスのサーバからログやメトリクスを取得し、CloudWatchに出力することができる ということになります。 インストール方法 既にパッケージが用意されているため、以下のコマンドで簡単にインストールできます。 sudo yum install amazon-cloudwatch-agent /opt/aws/amazon-cloudwatch-agent/にインストールされます。 IAMポリシー AWSが以下のポリシーを用意しているため、対象のインスタンスに設定しているIAMロールに紐づけてください。 CloudWatchAgentServerPolicy 設定ファイル NGINXのアクセスログとエラーログをCloudWatchログに出力するために、必要最低限の設定を作成しましたので、それをベースに解説します。 ファイルの形式はJSON形式です。 セクション 項目 概要 agent region 送信先のリージョンを指定。(EC2の場合)デフォルトはそのEC2インスタンスが存在するリージョン run_as_user 実行ユーザ。デフォルトはrootユーザ logs log_stream_name collect_list内にてログストリーム名が指定されなかった場合のデフォルト値を指定。必須項目 force_flush_interval ログをCloudWatchに出力するまでの最大秒数。バッファ内に溜めているログのサイズが1MBに達すると強制的に送信。デフォルト値は60(秒) logs_collected files 収集するログの情報を指定。collect_listの配列 collect_list log_group_name 出力するロググループ名 log_stream_name 出力するログストリーム名 file_path 対象のログファイル名 timezone CloudWatchログに表示するタイムスタンプのタイムゾーンを指定。デフォルトはUTC timestamp_format CloudWatchログに表示するタイムスタンプのフォーマット { "agent": { "region": "us-west-2", "run_as_user": "root" }, "logs": { "log_stream_name": "{instance_id}", "force_flush_interval": 5, "logs_collected": { "files": { "collect_list": [ { "log_group_name": "/ec2/output_log_in_ec2_to_cloudwatch", "log_stream_name": "{instance_id}_access", "file_path": "/var/log/nginx/access.log", "timezone": "UTC", "timestamp_format": "%Y/%m/%d %H:%M:%S" }, { "log_group_name": "/ec2/output_log_in_ec2_to_cloudwatch", "log_stream_name": "{instance_id}_error", "file_path": "/var/log/nginx/error.log", "timezone": "UTC", "timestamp_format": "%Y/%m/%d %H:%M:%S" } ] } } } } 起動方法 以下のコマンドで起動します。 sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ -a fetch-config \ -m ec2 \ -s \ -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/amazon-cloudwatch-agent.json オプションはそれぞれ以下のようになっています。 a・・・最新の設定ファイルをロード -a fetch-config・・・ -cオプションか-oオプション、もしくはその両方を指定 m・・・起動するサーバの種別(EC2/オンプレミス)を指定 -m ec2・・・EC2を指定 s・・・起動 c・・・設定ファイルの指定 -c file:configuration_file_path・・・ローカルに配置されたファイルを指定 詳細や上記以外のオプションは--helpオプションにて確認ができます。 上記のコマンドを1度実行したあとは、systemctlコマンドにて起動停止を行うことができます。 補足 amazon-cloudwatch-agent-ctlコマンドを使用して起動した後に、設定ファイルに誤りを見つけ、修正しました。 しかし、systemctlコマンドを使用して、再起動をしたところ、修正内容が反映されていませんでした。 色々と調べていくと、-cオプションにて指定したJSONファイルを以下のTOMLファイルに一度変換してから、読み込まれているということがわかりました。 /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml 設定ファイル(JSON)を編集しても、エージェントが読み込む設定ファイル(TOML)には反映されないため、いくら再起動しても設定が反映されないということです。 設定を変更した後には、必ずamazon-cloudwatch-agent-ctlコマンドを実行する必要があります。 起動結果 以下のように、NGINXにて出力されているログファイルの内容をCloudWatchログに出力ができるようになりました。 あとは、CloudWatchアラームにてアラーム設定をするだけで、通知をすることも可能になります。 おまけ そういえば、みなさんはサーバの性能に関する状態を確認するのにCloudWatchを使用したことがあると思います。(ありますよね?) しかし、(例えば)メモリ使用率のメトリクスは存在しないですよね?結構大事な項目だと思うのですが、、、 CloudWatchエージェントを使用するとこれらの値も取れるらしいです。 次回はこれをネタにブログを書こうと思います。 参考 CloudWatch エージェントを使用した Amazon EC2 Instances インスタンスとオンプレミスサーバーからのメトリクスとログの収集 CloudWatch エージェントのインストール CloudWatch エージェント設定ファイルを手動で作成または編集する
俺の MacBook セットアップ 2022
最近、故障や働く現場が変わってMacをセットアップすることが多くなったので折角なので備忘録として設定内容を残そうかと思います。 環境 キーボード配列: US 言語設定: en スクリーンショットの言語設定は英語ですが説明時には日本語で説明します。 方針 macOS を利用してる場合は通常 Time Machine を使ってバックアップや MacBook 買い替え時の移行をするが Time Machine は使用しない。 代わりに必要なものはクラウドで管理するようにする。 Xcode や Homebrew 等が必要なツールはキリがないので今回は紹介しません。 スクリーンショットの保存先の変更 スクリーンショットはデフォルトではデスクトップに保存されます。個人的には画像はPictures以下に配置してほしいので ~/Pictures/Screenshots にスクリーンショットの画像や動画を保存するようにしています。 方法は以下のコマンドを実行して、反映するために option + command + escから Finder を再起動します。 mkdir ~/Pictures/Screenshots defaults write com.apple.screencapture location ~/Pictures/Screenshots 調べて初めて知ったのですが、ショートカットからでも設定が可能なようです。 Macでスクリーンショットの保存先を変更する方法 | ライフハッカー[日本版] キーボード入力 キーリピート デフォルトの設定ではaを長押しすると発音記号等が入力できるようになっています。 これをオフにするためにはターミナルで以下のコマンドで設定を変更します。変更後は再起動すると長押しをしても発音記号等の入力ではなく、キーをリピートしてくれます。 defaults write -g ApplePressAndHoldEnabled -bool false さらにリピートの速度の変更は システム環境設定 (System Preferences) の キーボード から設定できます。最速最短だと早すぎるのでその一段階下の値に設定しています。 ダブルクォート、シングルクォートの入力 デフォルトではダブルクォートやシングルクォートで囲むと終端のクォートが別の文字コードのクォートに変換されて入力されるのでコーディングしてる際はとても不便なのでオフにします。 システム環境設定 から キーボード を開き、ユーザ辞書 タブを選択します。 スマート引用符とスマートダッシュを使用 (Use smart quotes and dashes) のチェックを外します。 続いてクォートを始端と同じ終端のものを選択します。見た目は然程変わりませんがセレクトメニューの一番下です。 トラックパッド クリック デフォルトだと、クリックの動作は少し押し込む必要があるので、これを軽くタップするだけでクリックできるように変更します。 システム環境設定 から トラックパッド を開き、 ポイントとクリック タブを選択し タップでクリック にチェックを入れます。 ドラッグ ドラッグする時デフォルトだと押し続けてドラッグするので、これも設定を変更して、軽いタップとカーソルの移動だけでドラッグをできるようにします。 システム環境設定 から アクセスビリティ の ポインタコントロール を選択して、 トラックパッドオプション を開き ドラッグを有効にする をチェックし ドラッグロックなし を選択します。 USキーボードでキーを押しっぱなしでa… - Apple コミュニティ スクリーンセーバーとスクリーンロック 自社でもなるべく離席する際はホットコーナーを使ってスクリーンロックをしています。設定方法は システム環境設定 から デスクトップとスクリーンセーバ を開いて スクリーンセーバ タブの左下から ホットコーナ の設定で 画面のロック ズーム 画面共有等で一部を拡大したい時は control とトラックパッドのジェスチャーで拡大しています。この設定は システム環境設定 から アクセスビリティ の ズーム機能 から スクロールジェスチャと修飾キーを使ってズーム (Use scroll gesture with modifier keys to zoom) にチェックを入れると使えます。 SSH -C はコメントで自分の場合は習慣的に $MAIL/$HOSTNAME のようなフォーマットにしています. ssh-keygen -t ed25519 -C "your_email@example.com:mbpa2251" ssh-add -K vi ~/.ssh/config chmod 600 ~/.ssh/config ~/.ssh/config の内容は以下のようにします。 Host * UseKeychain yes AddKeysToAgent yes # Host foo # HostName 111.111.111.1111 # Port 1111 # User user_name # IdentityFile ~/.ssh/id_rsa # ForwardAgent yes 新しい SSH キーを生成して ssh-agent に追加する - GitHub Docs ファイル等の管理 普段作業している時はなるべくクラウドに同期するようにしています。普段から同期しておけば外部デバイスを使って Time Machine を使う必要がなくなります。 ここでは自分が使ってるものだけ紹介しておきます。 iCloud macOS を使ってる人ならほぼ利用していると思いますが、iOS等と同期ができるのは非常に便利です。 1Password を使ってパスワードやMFAを管理 iCloudでもパスワード管理等はできるのですが、1Passwordの方が細かいところに手が届く感じなので使っています。 会社で1Passwordを利用していて、ビジネスプランの場合はファミリープランが無料で利用できるので、個人的なものはファミリープランの自分のアカウントで管理しています。 MFAに関して以前はパスワードとMFAは別に管理したくてAuthyを利用していましたが 最近は 1Password で積極的に管理するようにしています。理由は将来的にはパスワードレスな世の中になるので 1Password 自体はパスワード管理というより本人認証のツールへと移行していくらしいので、それならMFAも1Passwordに寄せても良いと判断しました。 パスワードマネージャのLastPassと1Passwordが相次いでFIDO/WebAuthnへの対応を発表。パスワードレスの時代にパスワードマネージャの存在意義はどうなるのか? - Publickey Github プライベートでもGithubを使っていているので、なるべくコード等はコミットするようにしています。 AWS S3 多少大きめのファイルで長期間保存しておきたいものはS3を使っています。Glacier とか使えば結構安く使えるのと、AWS はよく使ってて慣れてるのでS3を選択しています。 アプリのクラウドストレージを使う 最近、使い出したCADのFusion360はアカウントを登録しておけば、Fusion360のクラウドストレージにデータが保存されます。 同様に VSCode で Microsoft か Github アカウントに紐付けして、設定を同期しておきます。 Chromeの設定同期もGoogleのアカウントに紐付けしておけば拡張機能等が同期されます。 最近のアプリは結構同期されることがあるので別途設定ファイルを管理する必要がなくて簡単です。 その他SNS等 写真や動画等は、秘密にしたいようなものもほぼ無いので、 Instagram や YouTube にアップロードしています。 今回のセットアップでは使わなかった設定 今回のセットアップでは、今のところ必要性が感じなかったが、以前は設定していたものがあるので、こちらも紹介だけですが残しておきます。 CLI の sudo から TouchID をできるようにする 最近は全然使ってないですが、以前は sudo を頻繁に使うことがあったので TouchID から使えるようにしていました。 DevelopersIO の記事に設定方法がまとめられているので、必要があれば設定しようと思います。 MacのTerminalでsudo実行時にタッチIDを使用する方法 | DevelopersIO
Raspberry Pi の ヘッドレス インストール
久しぶりに家に眠ってた Raspberry PI で遊んでみたくなったので、 2022年度版のインストールを備忘録として残そうかと思います 自分の用途的にはGUIは不要なのでキーボード、モニターレスな環境でヘッドレス インストールしています。今回もこのやり方で進めます。 用意したもの Micro SD Card 16GB Raspberry Pi 3 Model B MacBook Pro SDカードリーダー USBキーボードとHDMI端子のあるモニタは、トラブルがあったときには便利ですが今回は無くても何とかなりました。 Raspberry Pi Imagerのインストール まずは Raspberry Pi に OS を入れる必要があるので、www.raspberrypi.com から Raspberry Pi Imager をダウンロードし Mac にインストールします。 SDカードに Raspberry Pi OS をインストール 今回は Raspberry Pi 3 Model B で 64bit に対応してるので以下の構成で Raspberry Pi OS を用意していきます。 Wikipedia の Raspberry Pi に主な仕様がまとめられているので確認する際に便利です。 今回のインストールで要点となるのは以下の箇所です。 Raspberry Pi OS Lite (64bit) ホスト名指定 SSH有効化 Wi-Fi有効化 次に Mac Raspberry Pi Imager を起動します。 起動するとシンプルなUIが表示されます。 最初に OS と保存するストレージを選択します。 Operating System Raspberry Pi OS Lite (64bit) を選択 Storage SDカードを選択 次に細かいカスタマイズをするため、歯車アイコンのボタンを押して Advanced options のウィンドウを開きます。 Set hostname には 任意の名前を入れます。今回は satoruk-rp3b.local にしました。 Enable SSH は Allow public-key authentication only を選択して、 Set authorized_keys for pi に公開鍵を指定します。 Set username and password には Username 任意のユーザ名かpi, Password 任意のパスワードを入れます。 Configure wireless LAN には 接続したい WiFi の SSIDとパスワードを入れます。この時 Wireless LAN countryも指定します。日本国内での利用であれば JP となります。 Set locale settings には、使用するTZとキーボードレイアウトを指定します。 設定を確認した後、問題がなければ SAVE して WRITE ボタンでSDカードに書き込みます。 Raspberry Pi OS の起動確認 OS をセットアップした SDカード を Raspberry Pi へ挿して、電源供給をするUSBを繋ぎます。 しばらくしてから、設定したネットワークで ssh 接続を試してみます。 今回の例だと以下のようなコマンドで接続できます。 ssh pi@satoruk-rp3b.local この時どうしても繋がらない場合は、複数Wi-Fiがある場合は、別のSSIDも試してみると繋がることがあります。実際に 5GHz(a)に繋ごうとした場合、繋がらず 2.4GHz(g)に繋いだらアッサリと繋がりました。 今回は以上です。
AWS CloudFrontのポリシーとは?
こんにちは、小林です。 みなさん、GWはどのように過ごしましたか? 私は信州に旅行に行きましたが、電車やバスの時間を調べておらず、2日で30km歩くという苦行を課すことになってしまいました。。。 さて、今回もAWS関連のブログを書きましたので、よろしくお願いします。 はじめに 2020年12月ごろに初めてCloudFrontを使用しました。 その際にキャッシュをLegacy cache settingsを使用して、設定していました。 キャッシュの設定は、ビヘイビアごとに設定する必要があるにもかかわらず、同じ設定を複数の箇所に指定するのはとても手間がかかりました。 さらにその1年後にキャッシュの設定を確認したところ、当時設定した内容と異なっていることに気づきました。 これらの解決策を調べていたところ、以下のブログを見つけました。 Amazon CloudFront キャッシュポリシーとオリジンリクエストポリシーを発表 なぜ、設定内容が変更されてしまったのかはわかりませんでしたが、「ポリシー」を利用することで冗長な設定が不要になり、設定も楽になりました。 このブログは2020年7月に公開になったもので、よく調べていれば、苦労することなく構築できたのかもしれません。(反省) 今回はそのポリシー、特にキャッシュポリシーとオリジンリクエストポリシーについて紹介します。 ※CloudFrontを使用したことがある人を対象としているため、ポリシー以外の説明は省略しています。 CloudFrontポリシー CloudFrontイベント ポリシーの話をする前に少しCloudFrontのイベントの話をします。 CloudFront全体での正式な名称なのかが曖昧ですが、クライアント-ディストリビューション-オリジン間でのリクエスト/レスポンスの名称として、以下が定義されています。 クライアント-ディストリビューション間・・・ビューワーリクエスト/レスポンス ディストリビューション-オリジン間・・・オリジンリクエスト/レスポンス 参考:Lambda@Edge 関数をトリガーできる CloudFront イベント ※Lambda@Edge関数としては、マニュアルが用意されていますが、それ以外では見つかりませんでした。しかし、CloudFront全体で使用されている言葉だと感じています。 CloudFrontポリシー CloudFrontでは、以下の3種類のポリシーが用意されています。 キャッシュポリシー ビューワーリクエスト時に使用する キャッシュの条件に含める値を指定する オリジンリクエストポリシー オリジンリクエスト時に使用する オリジンリクエストに含める値を指定する ビューワーレスポンスポリシー ビューワーレスポンス時に使用する ビューワーレスポンスに追加するHTTPヘッダーを指定する 参考:ポリシーの使用 キャッシュポリシー キャッシュポリシーでは、2つの設定を行うことが可能です。 キャッシュの条件 キャッシュの有効期限 キャッシュの条件 HTTPヘッダー、Cookie、クエリ文字列によって、キャッシュの条件を指定します。 上記の設定では以下の値が全て一致する場合にキャッシュされている内容をレスポンスとして返却することになります。 HTTPヘッダーに含まれるUser-Agentの値 cookieに含まれるsampleの値 リクエストクエリ文字列に含まれるnameの値 参考:キャッシュキーについて キャッシュの有効期限 キャッシュの有効期限は複雑な条件分岐によって決定します。 (ここでは省略しますので、公式ドキュメントを参考にしてください) 参考:コンテンツがキャッシュに保持される期間 (有効期限) の管理 オリジンリクエストポリシー キャッシュミス(キャッシュされていない)した場合に、オリジンリクエストに送信する値を設定します。 デフォルトで以下の値が含まれます。 URLパス(ドメイン名、クエリ文字列を含まない) リクエストボディ CloudFrontがオリジンリクエストに自動的に含めるHTTPヘッダー Host、User-Agent、X-Amz-Cf-Id、etc また、上記に加えてキャッシュポリシーに指定した内容は、オリジンリクエストに含まれます。 上記のオリジンリクエストポリシーがあり、以下のリクエストをした場合 curl -H 'User-Agent: sample_agent' -b 'sample=true' -d 'sample=false' -d 'name=kobayashi' -d 'age=30' https://example.com キャッシュポリシーの設定により、オリジンに送信されるもの HTTPヘッダーのUser-Agent cookieのsample クエリ文字列のname オリジンリクエストポリシーの設定により、オリジンに送信されるもの クエリ文字列のage 参考:オリジンリクエストの制御 感想 冒頭で書いた通り、設定が冗長ではなくなったので、管理が楽にはなったものの、まだ仕様を把握しきれていない部分が多いため、使いこなせているとは言えない状態です。 これからも精進を怠らず、使いこなせるようになりたいと思います。 参考 Amazon CloudFront キャッシュポリシーとオリジンリクエストポリシーを発表 ポリシーの使用 キャッシュキーについて コンテンツがキャッシュに保持される期間 (有効期限) の管理 オリジンリクエストの制御
メッセージキューのデザインパターン 優先度付きキュー の紹介
大量のタスクを処理するようなシステムを組む場合、最近はサーバーレスが当たり前になってきたので、コンピューティング リソースが足りない場合は一時的に増やして処理しまうことができるようになってきました。 小さい簡単な処理をする場合は 最初から AWS Lambda などを使ったワークフローにシステムを構築しても良いのですが、外部システムとの連携などで再処理が必要だったりすると、サーバーレスだと緻密に組まれたピタゴラスイッチの様になってしまい融通が効かせにくいことがあります。 そこでキューイングを使うことで、システム間を一方方向のメッセージングを使った疎結合できます。再処理する際もキューに同様のメッセージを積めば簡単に実現できるし、キューイングのシステム自体に再処理する仕組みも備わってることが多いです。 今回はキューイングを使ったシステムのデザインパターンの一種である、優先度付きキューイングを紹介します。キューイングには AWS SQS や Rabbit MQ など いろいろなものがあり、キューイングのプロダクトによっては優先度付きのキューの機能を持ってるものもあります。 機能的なユースケースは以下の様なものが考えられます。 結果の同時書き込み数を制御したい場合 通常の処理とは別に優先させて処理する場合 余力のコンピューティングリソースで処理するようにしたい場合 例えば優先度付きキューの特徴を活かせるユーザーストーリーで例えると以下の様になります。 例えばGoogle Sheets の様な書き込み制限のある API に処理結果を書き込む 各地のIoT機器から不定期に時系列データが送られてきて、最新の情報をより優先して処理する 課金ユーザは優先して処理し、無課金ユーザーは余力で処理する メッセージキューのプロダクト自体に優先度付き機能がない場合でも工夫すれば優先度付きキューを実現できます。優先度付きキューの機能を持ってないプロダクトは以下のように、優先したいものとそれ以外が混ざってしまいます。 これを優先度付きにする仕組み自体はとてもシンプルで、2つのキューを用意し、それぞれのキューの役割は先に処理するキューそして、その後に処理するキューという感じの役割になります。図で表現すると以下の様な感じになり、優先してさらに早く処理したい方のキューにコンシューマを多く割り当てます。この方法だとコンシューマは1つのキューを知っておくだけなのでとてもシンプルな実装になります。 実装をさらに工夫して、1つのコンシューマが複数のキューを使い分けて処理する方法もあり、実装が多少複雑になりますがコンピューティングリソースを効率的に使いまわせます。 この記事自体は優先度付きキューの説明だけですが、 優先度付きキュー 自体は広く知られてるデザインパターンなのでググってみると具体的な実装コード等もあるので気になる方は更に深ぼってください。
[React]mapメソッドについてお勉強になった話
こんにちは。浅野です。2022年最初の投稿です。早いもので、今年も4分の1が終わってしまいました、、、恐ろしいスピードです、、、 さて最近、業務でReactを扱うようになりました。そこで今回は、最近出くわしたエラーとその解決方法を紹介します。 まずは、こんなコードを書きました。 const items = [ { name: "name1", description: "description1" }, { name: "name2", description: "description2" }, ]; export function Hoge() { return ( <dl> {items.map((item, index) => ( <> <dt>{item.name}</dt> <dd>{item.description}</dd> </> ))} </dl> ); } すると、以下のエラーメッセージが出ました。 Warning: Each child in a list should have a unique "key" prop. Reactでmapメソッドを使う際、一意となる値をkeyとして渡さないといけません。今回dtタグにkeyを渡しているのでOKと思ったのですが、ダメでした。keyは、mapメソッドでreturnする親要素につけてあげる必要があったのです。かといって、dtタグとddタグをラップするdivタグを増やすのは、HTMLの構造として適切ではありませんし、場合によってはレイアウト崩れが起きてしまいます。 というところで、解決策は以下でした。 export function Hoge() { return ( <dl> {items.map((item, index) => ( <React.Fragment key={index}> <dt>{item.name}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> ); } <React.Fragment>にkeyを持たせてあげればいいのでした。普段は省略記法である<></>を使用しても問題はないのですが、こういうケースでは、省略できないようです。 Reactの公式サイトでも、まさに今回のケースが紹介されていました。 <></>は他の要素と同じように使うことが可能ですが、key や属性のサポートはありません。(中略)明示的に<React.Fragment>と宣言したフラグメントでは key を持つことができます。これはコレクションをフラグメントの配列に変換するときに有用です。たとえば定義リストを作成する時に利用します: またひとつ賢くなりました。この記事が誰かのエラー回避に繋がれば幸いです!
なぜRuby on Railsではrequireを実施なくてよいのか
こんにちは。 今年は花粉影響が大きく、外出するのが辛い小林です。 今回はRuby on Rails(以下、Rails)を使用するようになってから、ずっと気になっていたことが(たくさん)あるのですが、それが1つ解決しました!! その喜びを伝えたく、キーボードを執りました。 今回のテーマは、これです。 なぜ、bundlerでインストールしたgemをrequireせずに使用することができるのか? はじめに Rubyの勉強を始めたときにこういうことを覚えますよね? 外部ファイルを使用する場合はrequireにて対象のファイルをロードする 参考:module function Kernel.#require しかし、bundlerとRailsを組み合わせて使用している際に、requireを使用しませんよね? なぜなのでしょうか? なぜ、reuqireを省略することができるのか? 皆さんは、なぜだと思いますか? 実は、requireしなくても呼び出すことができる? いいえ。 それだとrequireの存在意義がなくなってしまいます笑 「requireは同じファイルを複数回ロードしない」ということは、一度ロードされてしまえば、再度requireを実行する必要ない。 つまり、すでにどこかでrequireが実行されている? よいところに目をつけましたね。 それでは、答え合わせをしていきましょう。 答え合わせ 答えは以下のファイルにあります。 config/application.rb config/apprlication.rb このファイル内で以下の記述があります。 Bundler.require(*Rails.groups) 参考:yasslab/sample_apps/blob/main/6_0/ch14/config/application.rb#L7 この処理によってGemfileに記載されているgemが依存関係を含めてロードすることができるようになります。 If you have a lot of dependencies, you might want to say "require all of the gems in my Gemfile". To do this, put the following code immediately following require 'bundler/setup': Bundler.require(:default) 参考:How to use Bundler with Ruby#Setting Up Your Application to Use Bundler ソースコード的な解説をしだすと切りがないので省略しますが、興味がある方は以下を参考にしてください。 参考:bundler/lib/bunder.rb Bundler.requireでGemfile内のgemをロードするのはわかったのですが、そもそもBundler自体もgemなので、どこかでrequireしないといけないのでは? 上記に記載がある通り、require 'bundler/setup'の後に実施するように記載されています。 該当コードを確認すると内部でBundlerをロードしています。 参考:bundler/lib/bundler/setup.rb 最後の疑問です。 config/application.rb内では、require 'bundler/setup'を実施していないではないですか。 config/boot.rb config/application.rb内でrequire_relative 'boot'を実施しています。 config/boot.rbを確認すると、require 'bundler/setup'を実施していることが確認できます。 参考:yasslab / sample_apps sample_apps/6_0_0/ch14/config/boot.rb 参考:Railsの初期化プロセス 注意事項 以下のように:require => falseと指定すると、Bundler.requireを実行しても、ロードされないので、注意してください。 Specify :require => false to prevent bundler from requiring the gem, but still install it and maintain dependencies. gem 'rspec', :require => false gem 'sqlite3' 参考:Gemfiles 最後に 最初のRails案件でプルリクエスト出した際に、「個別のrequireは不要」と指摘されて依頼、ずっと気にかけていたことなので、本当によかったです。 完全に理解しました。(Railsガイドちゃんと読まないと。。。) 他にも気になっているものは多々あるので、引き続き調査していこうと思います。 参考 Railsの初期化プロセス How to use Bundler with Ruby Gemfiles rubygems / bundler bundler/lib/bundler/bundler.rb bundler/lib/bundler/setup.rb
Let's Encryptの証明書自動更新をAWS CLIで実現した話
連日寒い日が続いていますね。 二十四節気では一年で一番寒いとされる時期のようです。 こんにちは。WYRDの豊田です。 2022年が始まってもう一ヵ月が経とうとしています。遅ればせながら明けましておめでとうございます。 2021年もあっという間に過ぎ去った感じですが、2022年はどんな一年になるでしょうか。 今回のブログでは、この年末年始の余暇を使ってメールサーバーのSSL証明書更新処理を自動化した話を書こうと思います。 なお、今回の記事ではLet's EncryptやAWSに関する基礎的な解説はしていません。 背景 私が運用しているメールサーバーの証明書はLet's Encryptを利用しています。そのため、3ヶ月おきに証明書の更新作業を行う必要があります。 Let's Encryptの証明書更新にある程度詳しい方からの「cronで自動化すればいいのでは?」というツッコミが聞こえてきそうですね。 しかし、このサーバーは通常時80番ポートを開いておらず、HTTP-01チャレンジを行うには、事前に80番ポートを解放してあげる必要があるのです。 ※認証の仕組みについては公式サイトの解説を参照ください つまりSSL証明書を更新するときの手順は以下になります。 AWSコンソールにログイン EC2セキュリティグループで80番ポートを開く EC2インスタンスにログイン 証明書更新コマンド(certbot renew)実行 EC2セキュリティグループで80番ポートを閉じる 手順としてはそれほど複雑ではないですが、3ヶ月おきに手動で実施するとなると結構面倒なので、前々から自動化したいと思っていました。 (DNS-01チャレンジなどの方法も検討しましたが、当時DNSの設定を動的に書き換える方法を思いつかず、断念した覚えがあります) AWS CLIを使えるようにする SSL証明書更新を自動化するには、「AWSコンソールから80番ポートを開かなければならない」点がネックになります。 これを自動化することができれば、自動化できたも同然です。 そこでAWS CLIを使って、コンソールにログインせずに80番ポートを開け閉めできるようにします。 AWSコンソールでの作業 AWS CLIからセキュリティグループの設定を変更するためには、まず専用のユーザー(IAM)を用意します。(名前は適当なものをつけます) 今回は自動化バッチから利用するだけなので、AWS認証情報タイプは「アクセスキー」だけでOKです。 次に作成したユーザーに適用するポリシーを用意します。 あらかじめAWS側が用意しているプリセットポリシーがありますが、余計な権限まで付与してしまう可能性があるので、必要最低限の権限に限定したポリシーを独自に用意した方が良いでしょう。 ということで、今回のユーザーには以下の権限を付与します。 ec2:AuthorizeSecurityGroupIngress # inboundの設定を追加できる権限 ec2:RevokeSecurityGroupIngress # inboundの設定を削除できる権限 ec2:DescribeSecurityGroups # セキュリティグループの設定一覧を取得できる権限 ※この辺りの権限設定は、親子関係(ある権限を付与する時に、自動的に一緒に付与される権限)があったりするので、コンソールからGUIで設定すると楽です これでAWS側での事前準備は終わりです。 クライアント側での作業 次はサーバー側の準備です。 AWS CLIを使ってリクエストするために、名前付きプロファイルを作成します。 $ aws configure --profile ec2-sg-update 4項目ほど入力を求められますが、AWSコンソールでユーザー作成時に発行されたアクセスキーIDやシークレットアクセスキーなどを入力します。 動作確認 ここまで作業が終われば、あとは実際にリクエストを送るだけです。 以下のコマンドで80番ポートを開いたり閉じたりできるはずです。 80番ポートを開く $ aws ec2 authorize-security-group-ingress --group-id <セキュリティグループID> --protocol tcp --port 80 --cidr 0.0.0.0/0 --profile ec2-sg-update 80番ポートを閉じる $ aws ec2 revoke-security-group-ingress --group-id <セキュリティグループID> --protocol tcp --port 80 --cidr 0.0.0.0/0 --profile ec2-sg-update 更新スクリプトを用意する これでCLIベースでポートの解放が行えるようになりました。 あとはスクリプトを用意してcronで定期実行すればOKです。 スクリプトによる更新結果をSlackに通知するなど、もう少しカスタマイズすることで一層便利になりますが、その辺りについても今回は割愛します。 まとめ 長い間、手動で行っていた証明書更新作業を自動化できました。 AWSコンソールにログインしてポチポチせずに、コマンド一発で済むのはやはり楽ですね。 そういえば冒頭で断念したと書いたDNS-01チャレンジもAWS CLIと連携すればいけそうです。 DNS-01チャレンジなら80番ポートを解放することなく証明書の更新ができるので、状況によっては選択肢となるかも?(Let's EncryptはHTTP-01チャレンジを推奨しているようですが)
Chrome に追加された Copy link to highlight とは
最近, Chrome に追加された機能で Copy link to highlight があります。これが結構便利でWebページ内の選択箇所へのリンクを作成してくれます。 このリンクの仕様自体はChrome独自でなくW3CにもText Fragmentsとして書かれてる仕様でChrome 80 以降から利用でき他のブラウザも対応するはずです。Copy link to highlight はこの Text Fragments に沿ったリンクをコピーするものになります。 URL Fragments 拡張するもので URL中にある https://example.com/#id の #id の部分を #:~:test=smth とするとページ上のsmth を表示してくれます。仕様上では表示をどう表現するかは各ブラウザの実装に委ねていますが、何かしらの方法でページ上で指定した文字列を視覚的にわかる効果で表現されるはずです。 このText Fragmentsの構文としては以下のように定義されています。 #:~:text=[prefix-,]textStart[,textEnd][,-suffix] context |-------match-----| context スクエアブラケットで囲まれてるところはオプショナルのパラメータでtextStartのみが必須になります。prefix, textStart, textEnd, と suffix は実際には任意の%エンコーディングされた文字列になります。prefix と suffix は、前後の一致を表現できホワイトスペースを許容します。 各ブラウザでの確認 もう少し詳しく見ていきます. Chrome(96.0.4664.55), Microsoft Edge(96.0.1054.34) の挙動を元にしたものになります。Filefox(94.0.2), Safari(15.1)でも確認しましたが未対応の様です。 2021/11/28時点で https://example.com/ は以下の様に表示されています。 Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. More information… textStart #:~:text=for と指定すると This domain is for の for がハイライトになります Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. prefix-,textStart #:~:text=asking-,for と指定すると asking for の for がハイライトになります Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. textStart,textEnd #:~:text=for,in と指定すると This domain is for use in の for use in がハイライトになります Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. textStart,-suffix #:~:text=examples%20in,-documents と指定すると examples in がハイライトになります Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. textStart,textEnd,-suffix #:~:text=examples,in,-literature と指定すると examples in documents. You may use this domain in がハイライトになります Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. 通常のフラグメントとの併用 通常のフラグメントと併用する場合は #id:~:text=sample の様に :~: の手前に指定できます。 セキュリティ面の考慮 セキュリティ面からの説明も書かれていてトラッキングできない様に考慮されています。今までのフラグメント同様、サーバサイドではフラグメントの情報は取得できません。アクセス分析等の実装では JavaScript で Location のhash で取得していました。Text Fragmentsは更に厳しく、JavaScriptでも取得できないようになっています。 Text Fragment Browser HTTP https://example.com/#id:~:text=for https://example.com/#id https://example.com/ 参考 Text Fragments URLの #:~:text= とは何か?( Text Fragments ) - Qiita
こんな機能あったんだ 〜GitHub Suggested changes篇〜
こんにちは。浅野です。 寒くなってきて、お鍋が美味しい季節ですね。私はおでんが食べたいです。おでんの美味しいお店があれば教えてください。 さて今回ですが、GitHubの便利機能を教えてもらったので、それについて書いていきたいと思います。 タイトルにも書きましたが、GitHubのSuggested changesをご存知ですか?ざっくり説明すると、レビュアーが提示してきたコードの修正案を画面上の操作だけで取り込めてしまう、という機能です。要は「エディターで修正して、コミットして、プッシュする」という過程を取らずに、修正案を反映させることができます。 具体的な使い方を見ていきましょう。まずプルリクエストに対して、修正案を提示する方法です。コメントを記述する画面から、赤枠の箇所をクリックしてください。 すると、コメント欄に該当箇所のコードが反映されます。 これに手を加え、修正案を記述します。書き終わったら、通常のコメントと同様に送信します。 その結果、以下のように表示されます。ビフォーアフターが分かりやすいですね。 ここまでで修正案を提示することができました。続いて、修正案を採用する場合を見ていきましょう。なんと以下のボタンをクリックするだけです。これがそのままコミットになるので、コミットメッセージを変更したい場合はこの画面で編集してください。 無事、修正案が取り込まれました。 びっくりするくらい簡単ですね。こうした機能を活用して、開発スピードをアップさせていきたいです。 他にも使える便利機能があれば、ぜひ教えてください。 参考記事: https://docs.github.com/ja/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request
EC2インスタンスにAWS EC2コンソール経由でアクセスしてみる
みなさん、こんにちは。 小林です。 早いもので、ブログを書き出してから1年が経ちました。 技術系の記事は適当なことは書けない(書きたくない)ので、元々裏どりを丁寧にする方ではありましたが、より丁寧にするようになったと実感しています。 それでは、今回のブログの内容はこちらです!! つい先日、出先で突然、AmazonLinuxを触りたくなってしまったのです。 手持ちにはiPadがありましたがSSH接続できる環境が整っていなくて、困ってしまいました。 みなさんもそんな経験ありますよね? そんな時に便利なのが、EC2 Instance Connectという機能です。 AWSコンソールからサーバにアクセスし、CLIにて操作することが可能になります。 C2 Instance Connect を使用した Linux インスタンスへの接続 今回は、EC2 Instance Connectを使用して、EC2インスタンスにアクセスできるようにするまでの手順を紹介します。 EC2 インスタンスを用意する 今回はほぼデフォルトの設定のまま、EC2インスタンスを起動させます。 リージョンは「オレゴン(us-west-2)」を使用します。 AMI 今回は、AmazonLinuxを使用したいので、AmazonLinux2用のAMIを使用します。 ちなみに、EC2 Instance Connectを使用できるOSに制限があり、以下のOSである必要があります。 以下に挙げた、サポートされている Linux ディストリビューションに対し、EC2 Instance Connect をインストールできます。 Amazon Linux 2 (すべてのバージョン) Ubuntu 16.04 以降 参考:EC2 Instance Connect セットアップ#Limitations インスタンスタイプ 特に希望がなければ、無料枠のt2.microで問題ありません。 インスタンスの詳細 ブラウザからEC2 Instance Connectを使用するためには、パブリックIPv4アドレスが必要になります。 ユーザーがインターネット経由でインスタンスにアクセスする場合、インスタンスにパブリック IP アドレスがあり、パブリックサブネット内にある必要があります。詳細については、Amazon VPC ユーザーガイドの「インターネットアクセスを有効にする」を参照してください。 参考:EC2 Instance Connect セットアップ#タスク 1: インスタンスへのネットワークアクセスを設定する ネットワーク関連の項目に以下を設定してください。 サブネット:パブリックなサブネット デフォルトのものを使用するのであれば、どれでも可 パブリックなサブネットに関しては、以下にて言及していますので参考までに AWS 踏み台サーバ(ネットワーク)の構築 自動割り当てパブリックIP:有効 もちろん、後から設定することも可能です。 ストレージの追加、タグの追加 この項目は特に指定がないので、デフォルトのまま使用します。 セキュリティグループの設定 EC2 Instance Connectを使用するにあたり、必要な設定は後述しますので、ご自身の必要な設定にしてください。 今回はブログ用なので、新しく作成し、デフォルトで設定されているSSH用のルール(0.0.0.0/0)も削除してしまいます。 これで、使用するインスタンスの準備は一旦完了です。 EC2 Instance Connectをインストールする 使用するインスタンスにEC2 Instance Connectをインストールする必要があります。 が、以下の条件を満たす場合、すでにインストールされているので、今回はスキップします。 それ以前のOSを使用する場合、下記のリンク先に手順が記載されていますので、そちらを参照してください。 以下に挙げる AMI は、EC2 Instance Connect に事前インストールされており、いずれかを使用してインスタンスを起動する場合には、このタスクはスキップできます。 Amazon Linux 2 2.0.20190618 以降 Ubuntu 20.04 以降 参考:EC2 Instance Connect セットアップ#タスク 2: (条件付き) EC2 Instance Connect をインスタンスにインストールする EC2 Instance Connect CLIをインストールする ブラウザを経由せず、自身のコンピュータを利用する場合には、EC2 Instance Connect CLIが必要になりますが、今回はブラウザからアクセスしたいので、スキップします。 詳細手順は、以下のリンク先を参考にしてください。 参考:EC2 Instance Connect セットアップ#タスク 3: (オプション) EC2 Instance Connect CLI をお使いのコンピューターにインストールする セキュリティグループの設定を行う ブラウザからアクセスする場合、EC2 Instance ConnectというサービスからのSSHでのアクセスを許可する必要があります。 (Amazon EC2 コンソールのブラウザベースのクライアント) インスタンスに関連付けられているセキュリティグループで、このサービスの IP アドレス範囲からのインバウンド SSH トラフィックが許可されていることを確認します。 参考:EC2 Instance Connect セットアップ#タスク 1: インスタンスへのネットワークアクセスを設定する SSH接続の許可 先ほどEC2に設定したセキュリティグループを開き、インバウンドに以下の設定を追加します。 ソースに設定する値は、以下から取得することが可能です。 https://ip-ranges.amazonaws.com/ip-ranges.json 参考:AWS IPアドレスの範囲 上記にアクセスするとJSON形式で記述されていますが、全リージョン、全サービス分が記述されているため、以下の項目で絞り込みます。 region: us-west-2 service: EC2_INSTANCE_CONNECT 絞り込んだら、ip_prefixに設定されている値をセキュリティグループのソースに設定します。 EC2 Instance Connectを利用するための許可を与える EC2 Instance Connectを利用するには、利用したいIAMユーザに対して、許可を与える必要があります。 IAMポリシーの設定 以下のポリシーを利用させたいIAMユーザにアタッチしてください。 サービス アクション リソース リクエスト条件 備考 EC2 Instance Connect SendSSHPublicKey 対象のインスタンス 条件キー: ec2:osuser限定条件: リクエストの任意の値演算子: StringEquals値: ec2-user リクエスト条件には、接続したいユーザ名やそのほかの条件を追加します { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "ec2-instance-connect:SendSSHPublicKey", "Resource": "arn:aws:ec2:<リージョン>:<アカウントID>:instance/<インスタンスID>", "Condition": { "ForAnyValue:StringEquals": { "ec2:osuser": ["ec2-user"] } } } ] } 参考:EC2 Instance Connect セットアップ#タスク 4: EC2 Instance Connect の IAM アクセス許可を設定する これで準備は万端です。早速、接続してみましょう。 EC2 Instance Connectを使用してみる 対象のEC2インスタンスを選択し、接続ボタンを押下します。 EC2 Instance Connectを選択し、ユーザ名に対象のユーザ名を指定します。 そして、接続ボタンを押下します。 あら、不思議。EC2インスタンスにブラウザ経由でアクセスできるようにになりました。 インターネットに接続さえできれば、EC2インスタンスにアクセスし操作することができるようになりました。 これで、急にAmazonLinuxを触りたくなっても問題ありません。 他にも「セッションマネージャー」や「EC2シリアルコンソール」といった機能があるので、どういった機能なのかは今後調査していきたいと思います。
QUICご存知ですか?
連日暑い日々が続いていますが、みなさんいかがお過ごしでしょうか。 WYRDの豊田です。 最近は日傘とハンディ扇風機を常に携帯するようになりました。 マスクをしながら、この酷暑を乗り切るのは今年で最後にしたいところですね。 さて今回のブログでは、HTTP/3を実現する上で合わせて策定され、5月にIETFにより承認された"QUIC"(クイック)というプロトコルについて書いていこうと思います。 HTTPの歴史 QUICを説明するには、まずHTTPの変遷について理解しておく必要があります。 HTTP/1.1 HTTP/1.1は、1997年に最初のRFCが発行されて以来、これまで何度も拡張を繰り返し、現在も利用され続けています。 近年のインターネットの目覚ましい発展により、HTTPには通信データ量の増加、通信速度の高速化が求められるようになりました。 ここでHead of Line Blockingという問題が表面化します。 HTTP/1.1には「リクエストとレスポンスは送受信の順番を一致させる」というルールがあり、処理に時間のかかるリクエストをすると、その処理が終わるまで次のリクエストは処理を待たされます。 これを(HTTPレベルの)Head of Line Blockingと言います。 クライアント サーバー | | |--------- req_1 -------->|---- | | ^ | | | | | この間req_2を送っても処理されない | | | | | v |<-------- res_1 ---------|---- | | いくつかの回避策(HTTPパイプラインやコネクション多重化)は考えられましたが、どれも根本的な解決にはなりませんでした。 通信の効率が落ちてしまうこの問題は、発展を続けるインターネットの世界で悩ましい問題だったと言えるでしょう。 HTTP/2 2015年5月、HTTP/1.1の抱える問題の解決を図るため、新たなRFCが採用されます。これがHTTP/2です。 HTTP/2では、ひとつのTCPコネクション内にストリームという通信の単位が与えられ、リクエスト-レスポンスをセットで管理するようになりました。 個々のストリームを独立した通信として管理することで、あるストリームで発生した遅延が他ストリームに影響を及ぼすことはなくなりました。 これで(HTTPレベルの)Head of Line Blockingは解決されました。 ※HTTP/2ではヘッダの圧縮など、他にもさまざまな点で効率化が図られましたが、今回は割愛しています HTTP/2にも問題があった HTTP/2の登場により、全て解決...とはなりませんでした。 次に問題になったのは(TCPレベルの)Head of Line Blockingでした。 TCPとは、HTTPのプロトコルスタックにおいて、下層で利用しているトランスポート層のプロトコルです。 TCPは通信の信頼性(データの欠落や誤りが無い)を保証します。 その性質上、通信(パケット)に問題が発生した場合、その問題が解決されるまでTCPレベルで通信が止まります。 HTTPはTCPの上で動作するプロトコルなので、TCPレベルで通信が止まると、当然HTTPも通信が止まります。これが(TCPレベルの)Head of Line Blockingです。 多くの場合TCPの信頼性の担保はOSレベルで行われるため、アプリケーション(HTTP)は問題が解決するまで待つしか無いのです。 QUIC登場 2013年、Googleが独自に開発を進めていた QUICというプロトコルが発表されます。 当時はトランスポート層とアプリケーション層のプロトコルが混在し、プロトコルスタック上では明確に分けられていなかったようです。 TCPレベルのHead of Line Blockingに対する解決が試みられていたこのプロトコルは、2016年にIETFワーキンググループが発足し標準化が進められることになります。 QUICの特徴 QUICはトランスポート層のプロトコルですが、下位プロトコルに同じトランスポート層のUDPを利用しているという特徴があります。 UDPは通信の信頼性を保証しませんが、上位のQUICが通信の信頼性を保証するための拡張を行います。 TCPでは手の届かなかった痒いところをUDPの拡張により解決した感じですね。 2020年時点で、QUICにはGoogle QUICとIETF QUICという**"似て非なるプロトコル"**が混在している状況ですが、Googleは順次IETF QUICに対応していくことを明言しています。 ChromeではすでにHTTP/3が? QUICと並行して策定が進められているHTTP/3は、現在草案34版がでていますが、正式なRFCにはなっていません。 ただし、最新のGoogle Chromeを使ってGoogleのトップページへアクセスすると、すでに(部分的に)HTTP/3が利用されていたりします。(Protocolが "h3" となっている) 最後に QUICはTLS(暗号化)なども一手に引き受けることで、コネクション確立を効率化したり、トランスポート層の通信内容まで暗号化できるなど、他にもさまざまな改善が盛り込まれています。 ただ、世界にはすでにたくさんのTCPで動作する機器が存在していることもあり、すぐにQUICが主流となることはなさそうです。 環境と仕組みが相互に発展を続けていく、というサイクルの象徴的な出来事と言えるかもしれません。
快適なGitライフを送りたいと思っている話
こんにちは。浅野です。 最近応援しているアイドルグループのメジャーデビューが決まりました〜〜いや〜めでたい!なかなかコンサートやイベントに行きづらいご時世ではありますが、細々と応援していきたいと思います! さて、今日のテーマは「Git」です。開発に欠かせないツールで、毎日使っていますが、使いこなせているかと訊かれれば、みぞおちにワンパンを食らったかのごとく、「ゔ...」となってしまいます。 そんな感じなので、もっといろんなGitの使い方を教えてもらいたい〜と思い、とりあえず話のきっかけを作るために、私が便利だな〜と思っているコマンドたちについて書いていこうと思います。内容としては、以前プロジェクト内で共有してもらったものや、自分で調べて使えるな〜と思ったもの、よく使うのに忘れてしまうコマンドなどを備忘録という目的も兼ねて、載せていきます。 git add -p これは教えてもらってから愛用しています。差分を一つ一つ確認しながら、ステージングエリアにファイルを追加できます。余計な空白や意図していない変更に気付けるので、大変便利です。 git stash -p stashも一つ一つ確認しながらできるらしい。特定のものだけ退避できるので、複数の作業をしていて、ブランチを間違えた場合などに使えそうです。ただ実行するとめちゃめちゃ遅い(私だけ?) git commit --amend 直前のコミットを変更できるコマンド。私はgit commit -mで、コミットメッセージを書いている途中にも関わらず、勢い余ってEnterキーを押してしまうことがあるのですが、このコマンドのおかげで、そしらぬ顔で作業が継続できます。 git rebase -i コミットID こちらもコミットを変更できるコマンドですが、2つ以上前のコミットにも手を加えることができます。私は、多くなりすぎたコミットログをまとめるために使ったりしています。あとはあまりないかもしれませんが、CommitterやAuthorを変更することもできます。注意点としては、指定したコミットは含まれず、次のコミットからが対象になります。 git reset HEAD git reset HEAD ファイルパス # 特定のファイルを指定する場合 これは、「私〜〜いい加減覚えてくれ〜〜」という思いをこめて載せました。git addしたものを取り消せます。resetと聞くと、どうしてもコミット済みのものに対して行うというイメージがついており、git addを取り消すことと結びつかず、毎度忘れてしまいます。HEADは「現在のブランチの最新のコミットの状態」で、そこまで戻したいんだからresetで良い。よく考えれば分かりそう... git checkout リモートブランチ名 git checkout -b ローカルブランチ名 リモートブランチ名としなくても、リモートブランチをローカルに持ってくることができます。記述が少なくて楽チンですね♪なお事前にgit fetchが必要です。 git checkout コミットid ファイルパス 特定のファイルを特定のコミットの状態まで戻してくれるコマンド。checkoutってブランチを切り替えるだけではないんですね。以前、文字コードの影響で意図せず発生してしまった差分の取り消し方が分からず、困り果てていたのですが、その状況を救ってくれた恩人コマンドです。 最後に、コマンドではないですが、Gitの使い方として、私は、SSHキーのパスフレーズを打たないとgit pushできないように設定しています。(これはGitの機能ではないですが...)一瞬ですが、git push前に立ち止まって冷静に考えることができるので、安心感があります。 さて、いくつか書き連ねましたが、まだまだ知らない便利なコマンドや、より良い使い方が山ほどあるんだろうな〜と思います。そういうのをたくさん吸収して、快適なGitライフを送りたいです。 そんなわけで、「このコマンド便利だよ」、「こんな使い方おすすめだよ」というのがあれば、ぜひ教えてください。では今回はこの辺で!
Rubyの正規表現
こんにちは。 WYRDの豊田です。 持ち回りブログ3週目です。 早速ですが、みなさんはお休みの日はどのように過ごしていますか? 私たちの業界は基本的に土日お休みなことが多いのですが、私は特に予定がなければ技術的な勉強(本を読んだり、調べ物をしたり)に時間を割くことが多いです。 技術は物凄いスピードで進化・進歩していて、常にアンテナを張っていないとすぐに取り残されてしまいますからね。 「これだけ学べば十分」と思える日は来ないと思っています。 少し話が逸れてしまいました。 最近、弊社内ではRuby技術者認定試験に合格しようぜ!という機運が高まっています。 この間の浅野さんのブログ記事もSilverを受験するというお話しでしたね。(ここだけの話、合格したらしいですよ) ということで、私も今更ながらGoldの合格を目指して久しぶりに試験勉強を始めました。 今回は試験勉強の中で出てきたRubyでの正規表現について書いていこうと思います。 基本的なところ Rubyでは他の多くの言語で採用されているPerl拡張正規表現が採用されています。(完全互換ではないので注意) Regexpクラス 正規表現を使ったパターンマッチングを行うにはRegexpクラスを利用します。 Regexpはregular expressionの略ですね。 Regexp.new('hoge') === 'hoge' #=> true または、正規表現リテラルを使うこともできます。(生成されるオブジェクトはRegexpクラス) /hoge/ === 'hoge' #=> true %r{hoge} === 'hoge' #=> true パターンマッチ パターンマッチには以下の方法があります。 Regexp#=== Regexp#=~ String#=~ Regexp#match? ( Symbol#match?, String#match? ) ※ 2.4以降 /hoge/ === 'hoge' #=> true /hoge/.match?('hoge') #=> true :hoge.match?(/hoge/) #=> true # `=~` はマッチした位置を返す /hoge/ =~ 'hoge' #=> 4 'hoge' =~ /hoge/ #=> 4 # String#===は挙動が異なる 'hoge' === /hoge/ #=> false String#===は同値であるかの判定しかしません。 Regexp#match Regexp#matchメソッドを使えば、マッチするかの判定に加えマッチした情報の詳細を取得することもできます。 match_data = /\d/.match('hoge9') match_data.class #=> MatchData MatchDataクラスについては、話すと長いのでここでは割愛します。 MatchData - リファレンスマニュアル 先頭と末尾 これはRubyを使っていると一度は通る道だと思います。 /\A ... \z/とすることで、文字列の先頭と末尾を明示的に指定できます。 他の言語でいうところの ^ , $ です。 Rubyでも ^ と $ も使えますが、挙動が違います。 "hogehoge\n1234567890" =~ /\A\d+\z/ #=> nil # 行単位でマッチするか確認するので、改行コードが入っていると挙動が変わる "hogehoge\n1234567890" =~ /^\d+$/ #=> 9 あと /\A ... \Z/というのもありますが、あまり使わないので割愛します。 (知りたい方はググるとたくさん良い記事が見つかります) キャプチャと後方参照 キャプチャは正規表現では結構普通に使われるテクニックになりますが、Rubyでも利用可能です。 ()で囲んだ範囲がマッチすると、マッチした文字列を同一正規表現内で使ったり確認したりできます。 # キャプチャした文字列は \1 で後方参照できる /(\w+) of \1/ === 'hoge of hoge' #=> true # Rubyの実行環境内であれば $1 でキャプチャ文字列を参照できる $1 #=> 'hoge' 複数キャプチャした場合、\1, \2, \3... , $1, $2, $3... 数字を増やすことで後方参照できます。 名前付きキャプチャ \1 で後方参照するのは直感的ではなく使いづらい気もします。 そんな時はキャプチャには名前をつけて後方参照することができます。 名前付きキャプチャは (?<name>pattern) (or (?'name'pattern) )のように書きます。 後方参照時は \k<name> (or \k'name' ) と書くことができます。 /(?<number>\d+) and \k<number> is same number/ === '1 and 1 is same number' #=> true 名前付きキャプチャと前述の数字の単純なキャプチャは併用できません。"use name" と怒られます。 numbered backref/call is not allowed. (use name) また、 Regexp#=~ を利用した場合、キャプチャした文字列はローカル変数に代入してくれたりします。 (正規表現側に #{} による文字列展開が含まれていない場合に限定) /(?<name>\w+)/ =~ 'toyoda' #=> 0 name #=> 'toyoda' 部分式呼び出し マッチした文字列ではなくて、マッチした正規表現自体を呼び出すことも可能です。 \g<1> でキャプチャした箇所の "正規表現" を呼び出せます。 # /\d+-\d+-\d+/ と同じ意味 /(\d+)-\g<1>-\g<1>/ === '123-456-789' #=> true # 名前付きキャプチャも利用可能 /(?<num>\d+)-\g<num>-\g<num>/ === '123-456-789' #=> true なんかもういろいろ出来すぎて、そろそろ覚えていられなくなってきました。 あと少しで終わりにしようと思います。 先読み、後読み ある正規表現にマッチした位置より前(先読み)または後(後読み)にある特定の正規表現にマッチさせることができます。 肯定先読み(positive lookahead) マッチさせたい文字列の後ろに、さらに特定の文字列が続く場合にだけマッチさせたい場合に使います。 わかりにくいと思うので例を示します。 肯定先読みは (?=pattern) と記述します。 # "ですかね" が続く直前の数字を取りたい /\d+(?=ですかね)/.match('たぶん20ですかね') #=> #<MatchData "20"> 否定先読み(negative lookahead) 肯定先読みの逆です。 マッチさせたい文字列の後ろに、特定の文字列が続かない場合にだけマッチさせられます。 否定先読みは (?!pattern) と記述します。 # "ですかね" が続かない直前の数字を取りたい /\d+(?!ですかね)/.match('たぶん20ですかね') #=> #<MatchData "2"> 直感的でない動作だと思った方もいるかもしれませんので、簡単に説明します。 /\d+(?!ですかね)/ が意味するところは "「ですかね」が続かない数字" です。 20という文字列のうち 0は後ろに "ですかね" が続くので否定条件に合致します。(マッチしません) 2は後ろに "0" という文字が続くので否定条件に合致しません。(マッチします) 肯定後読み(positive lookbehind) 肯定先読みは後に続く文字を指定しましたが、後読みは先に来る文字列を指定します。 肯定後読みは (?<=pattern) と記述します。 /(?<=答えは)\d+/.match('答えは3600円でした') #=> #<MatchData "3600"> 否定後読み(negative lookbehind) 否定先読みと同じ要領で、先行する文字列を指定します。 指定した文字列に続かない文字列を条件にマッチします。 否定後読みは (?<!pattern) と記述します。 /(?<!答えは)\d+/.match('答えは3600円でした') #=> #<MatchData "600"> これも、否定先読みと同じ原理ですね。 "3" は否定されますが、それ以降の "600" は該当しないのでマッチします。 最後に Rubyの勉強していたはずなのに、なぜか正規表現の勉強をしていたなんてことは良くある話で... 他にも色々ありますが今日はこの辺にしておこうと思います。 いやー正規表現は奥が深い。 興味のある方はRubyの公式リファレンスを見てみてもいいかもしれません。
Thunderboltブリッジ接続を試してみた
こんにちは 昨年ようやく岐阜関ヶ原古戦場記念館がオープンしましたが、まだ訪れていない小林です。 コロナウイルスの猛威が収まったら、行きたい場所ナンバー1です。 はじめに 最近は週の半分を会社でもう半分を自宅で作業しています。 それでは本題に入る前に私の仕事道具について紹介させてください。 メインのコンピュータはMacbook Pro(2019)です。こいつを会社に持っていって作業しています。 自宅では、さらにモニタ4枚、外付けのUSBキーボード、Bluetoothマウスを使っています。 モニタは数と質だよ、あにき〜。 もちろん、自宅で作業している時は、これらをMacbook Proに接続します。 快適ですね。 ただ、ただですよ。自宅には遊び用兼検証用兼予備としてMac miniがあるのです。 こっちを使う時も当然それらを使います。 毎回、モニタやキーボードのケーブルを差し替えるのが、面倒なんですよ。 皆さんもそう思うことがありますよね? え?思わない?嘘だと言ってよ、ば〜にぃ。 今回はそんな悩みを解決するための方法を紹介したいと思います。 まず思いついたのが、 作業した内容をUSBメモリ等に保存して、それぞれのMacにコピーすれば、「ケーブルを差し替えなくて良い」という点においては問題ありません。 ですが、今度はコピーし忘れたり、USBメモリを紛失してしまったら、目も当てられません。 Cloudに保存することも考えたのですが、どのサービスを利用するか、コスト的に等々検討するのが大変だったので、今回はなしです。 はっ!! Mac miniからMacbook Proにリモートアクセスすることができれば。 Macbook Pro上のファイルを直接操作することができれば、これらの問題を解決できるのではないでしょうか? 幸いにもMacには簡単にリモートアクセスする方法が複数提供されています。 では、どの方法が良いのかを確認していきましょう。 アクセス方法 SSH接続 確かにMacbook Pro上のファイルを操作できますが、せっかくのモニタやアプリケーションをいかせないので、試すまでもなく却下です。 参考:リモートコンピュータにMacへのアクセスを許可する 画面共有 画面共有であればGUIでぽちぽち触れるので、やってみました。 ターミナルでSSH接続するより、いいですね。VSCodeも使用できるし。 いやいや、せっかくマルチディスプレイなのに全然いかせていないです。 赤い枠で囲ったアプリケーション分のサイズでしか使用できませんでした。 しかも、ラグがありますし。 これだとMacbook Proを直接開いて作業した方が良さそうですね。 というわけで却下です。 参考:別のMacの画面を共有する ファイル共有(WiFi経由) MacOSではSMBを使用してファイル共有ができるらしいので、共有したいディレクトリをmountしてやれば、Mac mini上にあるディレクトリとしてGUIからも操作ができるのでは? mount -t smbs //ユーザ@ホスト名/マウント先のディレクトリパス マウント元のディレクトリパス しかし、まだ問題があります。WiFi経由で操作しているのでとにかく遅い。 1GBもあるディレクトリをコピーをすれば、数分かかる。 う〜む、発想自体はよかったと思うんだけどな。。。 参考:Macでファイル共有を設定する ファイル共有(Thunderbolt経由) さて、ここからが今回の本題です。 Thunderboltポートを搭載しているMac同士をThunderbolt対応のケーブルで接続し、インターネットプロトコルを利用して通信を行うことができるのです。(IPoverThunderbolt) Thunderbolt3であれば、転送速度が40Gbpsもあり、早そうですね。これは期待大です。 Thunderbolt 3 2015年6月にUSB Type-Cコネクタを利用し、転送速度40 Gbpsを実現するThunderbolt 3を発表した[10][11]。ケーブルはUSB 2.0と3.1(アクティブケーブルを除く)と後方互換性がある。アップルはThunderbolt 3を搭載したMacBook Proを2016年10月28日に発売した[12]。今では、ほとんどのMacに搭載されている。 https://ja.wikipedia.org/wiki/Thunderbolt#Thunderbolt_3 ベンチマークの結果は以下のようになりました。 WiFi経由の場合と比べると、圧倒的じゃないか、Thunderboltは。 参考:ThunderboltでIPを使ってMacコンピュータを接続する さいごに Thunderboltを使用することで、当初の目的を達成可能なことがわかりました。 同じ様に悩んでいる方、ぜひ試してみてください!
ワンライナーをリーダブルにリファクタリング
今回はコードレビュー中に リーダブル コード を思い出すことがあったのでその事例を紹介します。 前提として以下のコードになります。 Webブラウザ で動作する Babel でトランスパイルして minify する とあるプロジェクトで以下の様な JavaScript のコードがありました。(コンテキストが分かりやすいようにプロパティ名等は変更してます) reviewerAttributeNames() { return this.currentProject ? [...Array(this.currentProject.max_reviewers).keys()].map(n => ++n).map(n => `reviewer${n}`) : [] }, よく読めば何をしているのかは分かる気がしますが、読むのに少し時間がかかります。 例えば this.currentProject.max_reviewers の値が 3 だとしましょう。このメソッドの戻り値はどうなるでしょうか? 結果はこんな感じです. [ "reviewer1", "reviewer2", "reviewer3" ] 何となくは結果は想像できますが、途中の過程のコードが読みづらいと思います。 リファクタリング さて本題です。これを リファクタリング していきます。細かく見ると色々ありますが、主要な注目すべき以下の点があります。 三項演算子 を使っている Array.prototype.map() メソッドが 2度呼ばれている [ガード節] Guard Clauses を使う まずは 三項演算子 をなんとかしていきましょう。ここでは リーダブル コード の 7.5 関数から早く返す で書かれている、 ガード節 (Guard Clauses) を使います。ついでに メソッドチェーン をしてるところに改行も入れていきます。 reviewerAttributeNames() { if (!this.currentProject) { return []; } return [...Array(this.currentProject.max_reviewers).keys()] .map(n => ++n) .map(n => `reviewer${n}`); }, ガード節のおかげで条件分岐自体はシンプルになりました。元のコードよりは断然マシです。 ワンライナーを分解 ここでも リーダブル コード の 8.4 短絡評価の悪用 で書かれている. 1行で書かれているコードを分解するテクニックを使います。 鍵となる考え 「頭がいい」コードに気を付ける。あとで他の人がコードを読むときにわかりにくくなる。 スプレッド構文やらArray.prototype.keys()やArray.prototype.map()は忘れて普通に書いていきます。 reviewerAttributeNames() { if (!this.currentProject) { return []; } const { max_reviewers } = this.currentProject; const values = []; for (let i = 1; i <= max_reviewers; i++) { values.push(`reviewer${i}`); } return values; }, 愚直ですがfor文を使うのがベターです。 関数化する レビュー当時はこの1箇所しかありませんでしたが複数箇所あるのであれば関数を用意するのもいいと思います. lodashのtimesを使うのもいいですし、下記のような関数を用意しても良いです. function times(n, iter) { const values = []; for (let i = 0; i < n; i++) { values.push(iter(i)); } return values; } 上記のような関数を用意していればかなりシンプルになります. reviewerAttributeNames() { if (!this.currentProject) { return []; } const { max_reviewers } = this.currentProject; return times(max_reviewers, (i) => `reviewer${i + 1}`); }, それでも ワンライナー が使いたいと思う人へ ワンライナーでも少しは リーダブル にはなります。例えば以下のように書き直すこともできます。 reviewerAttributeNames() { if (!this.currentProject) { return []; } const { max_reviewers } = this.currentProject; return Array(max_reviewers).fill('reviewer').map((v, i) => `${v}${i + 1}`); }, ここで前提を思い出してみましょう。 Webブラウザ で動作する Babel でトランスパイルして minify する ショートコーディングのために工夫したはずですが、 minify までするとこの差はほとんどないと思います。 ちょっと試し書きする感じだと重宝するワンライナーですが、基本的には使わないほうが読みやすくなります。
AWS 踏み台サーバ(ネットワーク)の構築
はじめまして。 本年度入社しました小林と申します。 城に魅了されてしまったエンジニアです。 どうぞ、よろしくお願いします。 今回はちょうど業務でAWSに触れる機会があったため、ネットワークの設定に関して紹介していきます。 要件 以下のような環境を構築します。 踏み台サーバ(Amazon EC2) インターネットから直接アクセス可能 Webサーバ(Amazon EC2)(※今回Webサーバとして公開することは考慮しない) インターネットから直接アクセス不可 踏み台サーバからのみアクセス可能 踏み台サーバとWebサーバは異なるサブネットに所属 サブネットはそれぞれ異なるAZに存在 ネットワークの構成図はこのようになります。 ネットワークの構築 はじめに 用件に見合う構成にするために、以下のサービスを利用します。 Amazon VPC-VPC Amazon VPC-サブネット Amazon VPC-インターネットゲートウェイ Amazon VPC-ルートテーブル Amazon VPC-セキュリティグループ ※VPCは以下の設定で作成し、これら以外はデフォルトの設定を使用することを前提とします。 VPC VPC ID vpc-XXXXXXX IPv4 CIDRブロック 10.125.0.0/16 IPv6 CIDRブロック IPv6 CIDRブロックなし テナンシー デフォルト タグ Name TestVPC サブネット パブリックサブネット(インターネットからアクセス可能なサブネット) 以下の設定でサブネットを作成します。 名前タグ TestPublicSubnet VPC vpc-XXXXXXX アベイラリティゾーン※ us-east-2a IPv4 CIDR ブロック 10.125.1.0/24 ※リージョンはオハイオ(us-east−2)を利用しています。 このままだと、EC2インスタンスを起動した際にパブリックIPが自動で割り当てられないため、自動割り当ての設定を行います。(EC2インスタンスの作成時に割り当てることも可能です。) 該当のサブネットを選択→アクション→「自動割り当てIP設定の変更」を押下 「IPv4 の自動割り当て」をチェック→保存 プライベートサブネット(インターネットからアクセス不可なサブネット) 以下の設定でサブネットを作成します。 名前タグ TestPrivateSubnet VPC vpc-XXXXXXX アベイラリティゾーン※ us-east-2b IPv4 CIDR ブロック 10.125.2.0/24 ※リージョンはオハイオ(us-east−2)を利用しています。 インターネットゲートウェイ VPCの内部とインターネット間の通信を許可するために、インターネットゲートウェイを作成します。 インターネットゲートウェイ インターネットゲートウェイ自体には細かい設定は存在しません。 名前タブ TestIG 作成後、VPCと関連付けます。 アクション→「VPCにアタッチ」を押下 「使用可能なVPC」に対象のVPC(TestVPC)を選択→「インターネットゲートウェイのアタッチ」を押下 これでVPCは作成したゲートウェイ経由でインターネットと通信ができるようになる入り口が用意されました。 ルートテーブル ゲートウェイは入り口に過ぎず、ゲートウェイへのルートを用意する必要があり、そのためにルートテーブルを用意する必要があります。 ルートテーブル 以下の設定でルートテーブルを作成します。 名前タグ TestPublicRouteTable VPC vpc-XXXXXXX 作成したルートテーブルにインターネットゲートウェイを経由する設定を追加します。 対象のルートテーブルを選択→「ルートタブ」を選択→「ルートの編集」を押下 ルートに以下の設定を追加 送信先 0.0.0.0/0 ターゲット 作成したインターネットゲートウェイ 作成したルートテーブルにサブネットを関連付けします。 対象のルートテーブルを選択→「サブネットの関連付け」を選択→「サブネットの編集」を押下 パブリックサブネットのみを選択→保存 セキュリティグループ 最後にEC2への接続を許可するためにセキュリティグループを作成します。 セキュリティグループ 踏み台サーバ用のセキュリテイグループ 以下の設定でセキュリティグループを作成します。 基本的な詳細 セキュリティグループ名 TestSGForBastion 説明 ssh from internet to bastion VPC vpc-XXXXXXX インバウンドルール/アウトバウンドルール タイプ プロトコル ポート範囲 ソース インバウンド SSH TCP 22 0.0.0.0/0 アウトバウンド すべて すべて すべて 0.0.0.0/0 これでインターネット側からのSSH接続を許可しました。 ※必要に応じて接続元を絞ることをお勧めします。 この設定ではどこからでもアクセスできてしまいます。 Webサーバ用のセキュリティグループ 以下の設定でセキュリティグループを作成します。 基本的な詳細 セキュリティグループ名 TestSGForWebServer 説明 ssh from bastion to web server VPC vpc-XXXXXXX インバウンドルール/アウトバウンドルール タイプ プロトコル ポート範囲 ソース インバウンド SSH TCP 22 踏み台サーバ用のSG アウトバウンド すべて すべて すべて 0.0.0.0/0 ソースに対象のセキュリティグループを指定することで、そのセキュリティグループが設定されているEC2インスタンスからのアクセスを許可することができます。 もちろん、IPアドレスで指定することも可能です。 ネットワーク側の設定は以上になります。 これらの設定をEC2インスタンスに関連付けることで、踏み台サーバからのSSHのみを受け付ける環境が完成します。 EC2 インスタンス サブネット セキュリティグループ 踏み台サーバ パブリックサブネット 踏み台サーバ用のセキュリティグループ Webサーバ プライベートサブネット Webサーバ用のセキュリティグループ まとめ 肝となるのは、以下の2点かと思います。 私自身はこの設定が漏れていたことにより、うまく接続することができませんでした。 ルートテーブルとインターネットゲートウェイの関連付け セキュリティグループのソースにセキュリティグループを指定 補足 セキュリティグループに関してなのですが、実はWebサーバ用のセキュリティグループで設定した内容は、VPCを作成した際に作成されるデフォルトのセキュリティグループを指定することで実現が可能です。 しかし、デフォルトのセキュリティグループを使用する場合は、踏み台サーバ、Webサーバ双方に設定する必要があり、双方向の接続が可能になってしまうことに注意が必要です。 セキュリティグループの基本
shellを「bash」 → 「zsh」 にしてみたら、補完がとても良かった
macを使っていて、terminalを起動したら、、、 以下のようなメッセージを目にするようになりました The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. For more details, please visit https://support.apple.com/kb/HT208050. zshを使えと言っています。今まではbashを使っていました。 $ echo $SHELL /bin/bash これを機にzshにしてみます! zshって何? Z shell(ズィーシェル、zsh)は、Unixのコマンドシェルの1つである。 対話的なログインコマンドシェルとしても、強力なシェルスクリプトコマンドのインタープリターとしても使うことができる。 zsh は数多くの改良を含んだBourne Shellの拡張版という見方もできる。 のみならず、bashやksh、tcshの非常に有用な機能も一部取り込まれている。 macOSではmacOS 10.15 Catalinaからデフォルトのシェルとして設定されている。 また、Windows上でネイティブUnix環境を提供する Interix サブシステム上ではUnix版のソースコードをビルドしてWindows上で使用することができる。 wikipediaより うーん。。。これだけだと、何が出来るのかよくわからない イメージ的にはzshはbashより優れているって感じがします。色々機能があるよう(ちゃんと見てない...)ですが、補完機能(Completion System)が優れているようです ドキュメント bash → zshに移行してみる(for mac) shellを切り替える $ echo $SHELL /bin/bash $ chsh -s /bin/zsh # terminalを再起動します % echo $SHELL /bin/zsh % zsh --version zsh 5.3 (x86_64-apple-darwin18.0) bash_profile等を移行 元のbash設定をzshに写します(ここはzsh用にあとで修正する必要があります) % cat ~/.bash_profile >> ~/.zprofile % cat ~/.bashrc >> ~/.zshrc # プロンプトの設定 % ~/.zshrc >> "export PS1='%F{blue}%n@%m%f: %F{yellow}%~%f %1 %(!.#.$) '" $ source ~/.zshrc 補完強化するプラグインのinstall $ brew install zsh-completions zsh-autosuggestions zsh-syntax-highlighting $ echo "source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh" >> ~/.zshrc $ echo "source /usr/local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ~/.zshrc $ echo "export ZSH_HIGHLIGHT_HIGHLIGHTERS_DIR=/usr/local/share/zsh-syntax-highlighting/highlighters" >> ~/.zshrc これで基本的な設定は完了です おお!補完が効くようになりましたね!!!
MySQLを使ったデータの洗い替え
案件によって稀に大量のデータを入れ替える必要がある場合どうしていますか? 例えば、ECサイトで何かしらのセールで日付が変わると同時に大量の商品の在庫数を更新したり、連携する外部システムから膨大なデータが送られてきて自社のDBに取り込む場合等です。 一般的にこの様な処理を洗い替えといいます。 件数が少ない場合は、UPDATEなり、DELETE & BULK INSERTを使って直接対象のテーブルに反映させたりするかもしれません。同時トランザクション数や件数が少ないうちは気がつかないかもしれませんが、大量に処理させるとデッドロックが起きたりします。そうするとエンドユーザにも影響がでてしまいます。 洗い替え方法には幾通りか方法がありRDB毎に使える方法が違います。今回はMySQL(5.7)でのケースを考えてみます。 ケース: テーブルを入れ替える 一度にテーブル全体を入れ替える方法を考えてみます. 案1: View による参照の切り替え Viewを使えばテーブルの切り替えが簡単にできます。ただしMySQLのViewには制約があるので使用には注意が必要です。特にViewへの更新系クエリはサポートされてないケースもあります。 OR REPLACEを使えば既存のViewを置き換えることができます. CREATE OR REPLACE VIEW v1 AS SELECT * FROM t2; SWAPPING VIEW TABLES | DB Fiddle にプレイグラウンドを用意してみたので試してみてください。 案2: ALTER TABLE ~ RENAME を利用する(非推奨) 次は ALTER TABLE ~ RENAME を使った方法です. /* Bad */ ALTER TABLE t1 RENAME t_; ALTER TABLE t2 RENAME t1; ALTER TABLE t_ RENAME t2; この方法は何の問題もなくできる様に思えますが、RENAME間に別のコネクションからのテーブルの参照が行われる可能性があります。その場合にテーブルが見つからずエラーになってしまいます。 それならばとトランザクションで囲っても実は無駄です。MySQLはALTER TABLE等のDDLには 暗黙的なコミット(Implicit Commit) が発生する仕組みがあるので何も解決しません。 /* Bad */ START TRANSACTION; ALTER TABLE t1 RENAME t_; /* ここでCommitされてしまう */ ALTER TABLE t2 RENAME t1; ALTER TABLE t_ RENAME t2; COMMIT; 案3: RENAME TABLE を利用する MySQLにはこの様な時に使えるRENAME TABLE があります。RENAME TABLE は1度に複数のテーブルの名称変更ができます。 RENAME TABLE t1 TO t_, t2 TO t1, t_ TO t2; 暗黙的なコミットの確認はできませんが SWAPPING TABLES | DB Fiddle にプレイグラウンドを用意してみたので試してみてください。 ケース: テーブルの一部分だけを入れ替える こちらのケースは、外部システムからデータを受け取り部分的に置き換える様な時に使います。 案1: View による参照の切り替え テーブルを入れ替えると同様にViewが使えます。 この場合は、Viewにはインデックスを仕掛けられないので単純に複数のテーブルをJOINまたはUNIONした物になるのでソート等の実行計画が良くできません. 案2: Partitioning を利用する MySQLには Partitioning がサポートされており、このパーテーション毎にデータの置き換えができます。 ALTER TABLE t1 EXCHANGE PARTITION p00 WITH TABLE t1_p; 仕組みが複雑で更に Partitioning の制約もあります。端的にいうとPKを含むINDEXやUNIQUE INDEX にPartitionのキーを含めないといけません これさえ上手く使いこなせればViewより良い実行計画になります。 こちらも、 SWAPPING PARTIAL TABLES | DB Fiddle にプレイグラウンドを用意してみたので試してみてください。 まとめ いくつか案を出しましたが一長一短があるので銀の弾丸的なのはなく、一番マシな方法を選択する必要があります。 Also see MySQLでWebサービスを止めずに、その裏のバッチ処理で大量にデータを洗い替える。 - Qiita






