OmniAICreator/Qiita-1.07M huggingface dataset:
IPアドレスが重複したVPC間をトリッキーに疎通させてみる（ただしHTTP/HTTPS限定）

# はじめに
　複数のVPCでIPアドレスが重複しているが，それでもなんとかして通信したいという悲しい要件を実現してみます。（そもそもアドレス設計段階で…というお小言は言わないお約束）。トリッキーな方法でなんとかしましょう。
 
# 前提知識
この記事は，以下のスキルと知識をお持ちの方むけの解説です。
- AWS
    - ルートテーブル
    - NAT Gateway（プライベートNAT Gateway）
    - VPC
    - TransitGateway
    - Application Loadbalancer
    - SecurityGroup
    - サブネット
- IPv4の知識
    - サブネット
    - NAPT
    - NAT

# 要件整理
- クライアントとWebサーバ間でHTTP/HTTPS通信させる。
- クライアントとWebサーバが別VPCにある。
- クライアントのネットワークアドレスとWebサーバのネットワークアドレスが重複している:frowning2:
- Webサーバ側のVPC構成はできるだけ変更しない（サブネットを追加したり追加リソースはできるだけ使わない）
- できるだけAWSマネージドなサービスで

# 制約事項
- この記事は簡易的な構成で作成しており，実際のネットワーク設計に適合しない可能性があります。
- 疎通させるプロトコルは，HTTP/HTTPS限定とします。
- 可用性（AZ故障）は考慮しません。
- WebサーバのIPアドレスは固定とします。
- WebサーバのアクセスログにはクライアントのIPアドレスが記録できません（ALBもしくはNAT GatewayのIPアドレスが記録される）。
- Webサーバからクライアント宛の通信は発生しないとします。

# 構成図
![IP重複.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457890/aedb6f80-8727-6269-cb58-a331e048ba75.png)

ALBのターゲットグループにWebサーバのIPアドレスを登録しました。

# 結果

クライアントからALBにHTTPでアクセスすると，Webサーバにアクセスできました。
```
$ curl http://vpc1-alb-8c9e1*****5d52.elb.us-east-1.amazonaws.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
以下略
```
ということで，無事に動作しました。なお，ALBのホスト名は10.0.2.*のプライベートIPに解決されていました。

# トリッキーなポイント

- サブネット毎に，ルートテーブルで到達できる範囲を制限した
　特にPrivateSubnet3です。10.64.0.0/24は，Webサーバ側VPCにルーティングしました。「クライアントヘの経路はどうするの？」という疑問に対しては，この後のプライベートNATゲートウェイの説明を見てください。

- プライベートNATゲートウェイを使って，VPC内でNAPTした
　プライベートNATゲートウェイを使って，ALBには10.64.0.0/24ではなく，10.1.0.0/24のアドレスを見せています。クライアントから送信されたパケットの送信元IPアドレスは10.64.0.Xです。このパケットがプライベートNATゲートウェイを通過した後（ALBに届くまで）の送信元IPアドレスは10.0.1.Y（NAT Gatewayのアドレス）になります。
　NATゲートウェイよりもNAPTゲートウェイと呼んだ方がしっくりくるかもしれません。
- 1:1NAT（もどき）をALBで実現した
　AWSには1:1NAT（宛先IPアドレスを書換える）を実現するサービスがありません（多分）。なので，1:1NATの代替としてALBで無理矢理アドレス変換をしています。

# 微妙にストレス発生な設定
- プライベートサブネットのルートテーブル
　TransitGatewayをターゲットにする場合TransitGatewayを直接指定できず，TransitGatewayアタッチメントのENI（ElasticNetworkInterface）を指定する必要があります。ENIを特定するのが手間。

- ALBのターゲットグループ
　異なるVPCのインスタンス指定やDNS名（FQDN）をターゲットに設定できません。なのでWebサーバのIPアドレス直撃です。

- TransitGatewayの伝播を削除
　10.64.0.0/24宛ての経路をシビアに制御したかったので，デフォルトで作成される伝播を全削除しました。

# まとめ

IPアドレスが重複しているVPC間で，HTTP/HTTPSを疎通させてみました。

# トラブルシューティング
- つながらない
　ルートテーブルが誤っていないか，ルートテーブルがサブネットにアタッチされているか通信したいポートがセキュリティグループで設定されているか，など基本的な箇所を確認しましょう。
- 切り分け方法
　リーチャビリティアナライザーを使って切り分けましょう。以下のような区間で試験するとよいでしょう。
・Client→ALB
・PrivateSubnet3のTransitGateway ENI→Webサーバ

# 参考情報
参考にしたのは公式リンクです。構成図のうちWebサーバ側のVPCにあったALBをクライアント側VPCに持ってきました。

https://aws.amazon.com/jp/blogs/networking-and-content-delivery/how-to-solve-private-ip-exhaustion-with-private-nat-solution/

その他参考にした情報です。
https://aws.amazon.com/jp/blogs/news/connecting-networks-with-overlapping-ip-ranges/


https://www.lac.co.jp/lacwatch/people/20240124_003654.html

https://www.idaten.ne.jp/portal/page/out/secolumn/multicloud/column013.html

# おまけ
1:1NATをできるインスタンス（EC2＋Linux+iptablesとかFWなアプライアンスとか）を作って，同じようにルートテーブルを工夫すれば，HTTP/HTTPSに限定せずIPリーチャブルに近い環境を作れると思います。ただしAWSマネージドではないので構築保守運用が大変です。
自称VRハッカーの独り言

どうも、どうしてこのアドベントカレンダーに参加しているんでしょうか

のどかはです

今回は@asusnさん主催、「脆弱エンジニアの Advent Calendar 2024」の22日目の記事を書かせて頂く機会をもらいました

# さてVRCって知ってる？
僕が今回参加するにあたって他の記事のようなテクの知識も面白い内容に書けないので最初から、差別化を図ります

VRCってのは愛憎渦巻く謎のメタバースなんですが、今回では単純にプログラミングプラットフォームとして取り上げます

主にC#ってやつの亜種みてえなUdon#ってやつでスクリプトやゲーム内コンテンツを制作できるという、VRゲームフレームワークのようなものなんですが(誤解)

何を思ったのか僕はCTFをやりながらVRCにログインしていく中で最強のアイディア💡とか思いながら、この二つを悪魔合体するする方法を考えました

“メタバースの中でフラグを隠してCTFしたら面白えな”と

毎回思いつくアイディアを僕は最強だと思っているので、これは売れるぞ！！！とか思いながら懐で温めました

正直ちゃんと考えるとフラグをスクリプトに仕込んでしまってるならワールド(ぶっこぬきや、キャッシュで生データを見ることができる)を逆解析されて仕舞えば終わりです

けれど、別にそんなことわざわざするような~~頭おかしい人~~する人たちのことは考えなくても、初心者とか対象にすればいいやと思いました

正直に言ってCTFは難しいです

CTFが難しいのではなく、CTFを続けることや学び続けることが難しいでしょう

これは一重に直感的でなく、問題の傾向は一定でなく質もまちまちであり、手法は様々なものがありすぎるからです

だったらCTFの前段階のような、CTFクイズをVRというデバイスで、様々なアニメ的演出やインターフェースによってハッカー感を味わうゲームとして拡張しようと考えました

これは普通にsteamなどで売られてるゲームと同様の方向性です

ただ将来設計として、VRCではオンライン要素が強い側面をうまく活かして、アタック&ディフェンスなどをワールドギミックによって作ろうと考えていました

# 作った
最初は大きいことするつもりないので、クイズ回答システムを3日くらいでワールドギミックの作り方を勉強しながら完成させました

ほとんど、boothにて買った他の方のワールドギミックで用いられてるデモシステムを改変しただけでしたが…取り敢えず今回の要件は満たせそうでした

さてこのシステムでは、回答した答案をチェックしてチェックマークと正解音を出すだけです
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">勉強の成果<br>VRChatのコンポーネント何も分からんに辿り着いた <a href="https://t.co/70MJQuTY3U">pic.twitter.com/70MJQuTY3U</a></p>&mdash; のどかは (@bm9kb2thaGE) <a href="https://twitter.com/bm9kb2thaGE/status/1762521027563893238?ref_src=twsrc%5Etfw">February 27, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
でもこれするだけでも3日かかってるんで誰か褒めてください

できてきたので、CTFの集会を開くことにします

と言っても僕にはそんなことできるわけがないので、VRCフレンドに投げつけます

開催の目処を立てながら運営チームでテストしていきます

問題の設定などがあったので要望に従っては改良、ワールドの見た目やユーザビリティなども~~苦情~~ありがたいご意見が来るので対応していきます

完成してみると結構な出来栄えで、全能感を覚えはじめます

一躍有名になったらどうしようかなあとしょうもないことを考えながら、運営メンバーにひたすら自慢していました

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">- AFK最中に遊ばれる<br>- これ何の集まりなんだろ…<br>- これ本当に何の集まりなんだろ…あれ！？なんか見たことある文字が！<br>- 取れ高 <a href="https://t.co/32eCIDef8w">pic.twitter.com/32eCIDef8w</a></p>&mdash; のどかは (@bm9kb2thaGE) <a href="https://twitter.com/bm9kb2thaGE/status/1797992782826754219?ref_src=twsrc%5Etfw">June 4, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ここまで来といてなんですが、このワールド実は公開されてますが使われてないです

え？と思ったことでしょう

はい、使われてないです

何でぜひこの記事を見た皆さんが遊んであげてください(全然未完成ですが)

[TalkerCTF-club試験場 by のどかは](https://vrchat.com/home/launch?worldId=wrld_d190657f-9d6e-4aca-8457-70f0e1ed51ed)

## 終わりに
ここまでお読みいただきありがとうございました。

自己紹介も技術解説もせずに自慢だけしましたが、いかがだったでしょうか

いや、本当によく読んでくれましたね

それにしても、まさかアスースンさんにお声がけを頂き記事を書く機会がもらえるとは思いもしませんでした。

アスースンさんに見つかったきっかけ（と思われること）はこの記事で暴れてることを自白してるのでよければ見ていってください

https://qiita.com/nodokaha/items/ede49ea6faad3fe3a799

また、SatokiCTFで配信が可能なのを良いことにVRCでCTFしてみた、とかももしかしたらCTFerに目をつけられたきっかけかもしれません(一問も解けてないですが

そんなのどかはですが、CTFもVRChatもハマってないので、おそらくみなさんの目に触れるのは今回が最後でしょう

本当に何で参加できたんだここに

あ、ちなみにCTFアイディアを投げつけた先にいた~~被害者~~なフレンドは只今CTF集会を開催しているので良かったらご参加ください

https://vrc-ta-hub.com/community/54/

あと本人の感想を晒しておきます

https://qiita.com/kumama_nui/private/3aad635bbab76abb9004

というわけで、のどかはでした

明日は限界ちゃんさんによる恐らくセキュリティの取り組みだそうです

この濃い記事を読んで胸焼けした心を限界ちゃんさんにより癒されてください
２０２４参加イベントを振り返る　その２

お疲れさまです、みやもとです。

昨日に引き続き、今年参加したイベントをいくつか振り返っていこうと思います。
果たして読む方の役に立つのか、と問われるとちょっと不安が残る内容なだけに、せめていろんなコミュニティを紹介できるようなるべく主催がかぶらないイベントをピックアップしていくつもりです。
それでもいくつかはかぶってしまうかと思いますがご了承ください。

## 関ジャバ'24 5月度
関ジャバ（関西Javaエンジニアの会）主催の勉強会。
だいたい2ヶ月に1回ぐらいの頻度で定期的に開催されるイベントで、毎回異なるテーマを決めて参加者がLT発表をします。

https://kanjava.connpass.com/event/317503/

この時はLTではなくみんなでコードを書こう、という試みで、最低限の要件からそれぞれが適切と思う設計をしてコードを書こうというイベントでした。
普段コード書いてる言語なのに、いざ１から書こうとするとあれ？となることが多くて恥ずかしながらほぼ何もできずに終わりましたね…もっと勉強しよ、と思ったイベントでした。
「ドメイン駆動設計をはじめよう」の訳者のおひとりである増田 亨さんもおられました。

## Kobe Engineer SakeBash #3 with HACK.BAR
Acall株式会社さん主催のイベントです。
シリコンバレーのエンジニア界隈が発祥といわれるBeerBashにちなんで、酒造の多い神戸で開催するからSakeBash。

https://acall.connpass.com/event/320740/

参加者のみなさまも軽食を持ち寄ったりして、かなりフランクな雰囲気のイベントでした。
LTも比較的自由なテーマで、個人的には他のイベントから知り合った方のLTを聞けたのも楽しかった。
どちらかというと自分から話しかけるのが苦手で初対面の人が多い場では壁際でひっそり過ごすことが多いのですが、この時はそれなりにいろいろお話しに行けたような記憶があります。酔っ払いの幻覚でなければ。

## 死ン・プロジェクトの怪談弐
関西でITでなにかやったろうやない会！という、意気込みを感じさせる名前のコミュニティ主催のイベントでした。

https://osaka.connpass.com/event/318786/

名前の通り怪談です。
幽霊とかそういうオカルトではなくプロジェクトの怖い話。
要はバグとか案件なくなりとかやべーお客さまとか、そういう感じの怖い話。
もちろんあれこれぼかした上でのお話です。
この仕事やってれば何かしらあるよね、ということでひとりひとりお鈴を回して喋っていったのですが、この場限りの内緒話ということもあって静かながらかなりの盛り上がりでした。

## 自分にフィットするキャリアの見つけ方
エバンジェリストとしてお世話になっているWAKE Career主催のオンラインイベントです。
WAKE Careerさんはキャリアに関していろいろなテーマでイベントを開催されていて、この時はタイトル通り「自分にフィットするキャリア」をどう見つけていくかというお話でした。

https://wake-career.connpass.com/event/325398/

以前に[イベントレポート](https://qiita.com/Mymt_aggw2208/items/5314c0d924e02d395a6a)も書いたイベントだったりします。
漠然とキャリアアップという概念に囚われていた身にはすごく新鮮に感じられるお話でした。
キャリアに迷っている方が居たら是非アーカイブを見ていただきたいです。

## 参加イベント主催コミュニティ

### 関ジャバ
関西のJavaエンジニアのためのコミュニティです。
「Javaコミュニティ」ではなく「Javaエンジニアのためのコミュニティ」なので、純粋にJava技術についてよりも交流の場としてという面を意識されているようです。
普段使っているJavaがわりと古いバージョンなこともあり、新しい情報やほかの現場でどんなふうに開発されているのかを聞くことができて楽しくも身が引き締まります。

https://kanjava.connpass.com/

### Acall株式会社
神戸発のスタートアップ会社です。
最後に参加したイベントではオフィスを会場にしたイベントは難しいかも…というお話でしたが、交流や勉強会に対して積極的な印象を受けました。
イベントで会社の成り立ちについてもうかがい、地元企業ということもありすごく親しみを感じる企業さんです。

https://acall.connpass.com/

### 関西でITでなにかやったろうやない会！
関西のIT業界を盛り上げよう！というコミュニティ。
怪談話やすべらない話等、エンジニアの経験談をメインに交流イベントを開催されています。
過去イベントを遡るとハッカソン等もありました。
またイベント参加したいと思います。

https://osaka.connpass.com/

### WAKE Career
同名の女性エンジニア向けハイクラス転職サービスを運営されているbgrass株式会社のコミュニティです。
技術勉強会・ハンズオン・キャリアトーク・転職Tipsセミナー等、かなりの頻度でイベントを開催されています。
女性エンジニアのインタビュー記事やそれに関連する配信イベントは共感するところも多く、私も頑張ろうという気持ちになります。

## イベントの選び方
参加イベントを選ぶ基準はほぼ私の興味です。
仕事で使う使わないとか、自分が理解できるかどうかとかはあまり考えずに面白そうならできる限り参加しています。
最初はあまりにも知らない技術とか分野とかのイベントに参加するのは失礼かなと思っていましたが、主催の方でそういうのを気にされている方は思ったより少ないようでした。
仕事云々とか理解の範囲とかで考えると結局自分が知っている範囲しか参加できなくなるので、知らない技術でも面白かったらここから知っていこ！ぐらいの感覚で参加していいのかなと思っています。
telegram-bot-rbを使ってTelegram Botを動かす

# 概要
Telegramでは簡単にBotを作成でき、それを外部からあれこれ動かすためのAPIが提供されています。
いくつかのGemがこのAPIをラップしてくれていますが、今回はtelegram-bot-rbというGemでメッセージを返すBotを動かしてみます。


#### リンク
https://core.telegram.org/bots/api

https://rubygems.org/gems/telegram-bot

https://github.com/telegram-bot-rb/telegram-bot?tab=readme-ov-file

# 環境
MacBook Pro(M3 Pro)
docker desktop: 4.34.3
Ruby 3.3.5
rails ~> 7.2.2

# チュートリアル
### 1. Railsプロジェクトを用意する
以下の記事っぽく構築

https://qiita.com/Yusuke_Hoirta/items/3a50d066af3bafbb8641

自分がいつも参考にさせていただいている記事
細かい部分まで解説されている上に、内容がわかりやすく頭が上がらない

### 2. Gemを追加
Gemfileの末尾に以下を追加
```rb
gem 'telegram-bot'
gem 'telegram-bot-types'
```

追加したら反映するために再度 build & up
```sh
docker-compose build
docker-compose up -d
```

`telegram-bot-types`を有効にするために、以下を適当な場所に追加する

```:application.rb
# ひとまずapplication.rbに追記
Telegram::Bot::Client.typed_response!
```

### 3. TelegramBotの作成
BotFatherとのチャットを開く

https://t.me/BotFather

以下BotFatherへ送信する
```
# ボット作成開始コマンド
/newbot

# 任意のbotの名前(末尾がbotでないといけない 例: TetrisBot or tetris_bot)
test_231512_bot
```

そうするとアクセストークンをもらえるのでメモしておく
この記事では `123456:ABCDEFGHIJKLMN` だったということにする

### 4. Credentialsを設定
コンテナ内に入る
```
docker-compose exec -it web bash
```

Credentialsファイルを開く
```
EDITOR="vi" bin/rails credentials:edit
```

Botのアクセストークンを追記する
```
telegram:
  bot: 123456:ABCDEFGHIJKLMN
```

正しく設定できたかはrailsコンソールで確認できる
```
root@5112704f29eb:/app# rails c
app(dev)> Telegram.bot.get_me
=> #<Telegram::Bot::Types::User id=7652787638 is_bot=true first_name="bot_test_43824" last_name=nil username="test_231512_bot" language_code=nil can_join_groups=true can_read_all_group_messages=false supports_inline_queries=false>
```
Bot情報を取得できている

### 5. Webhookの用意
Webhook用のコントローラーを用意する
```ruby:app/controllers/webhook_controller.rb
class WebhookController < Telegram::Bot::UpdatesController
  include Telegram::Bot::UpdatesController::TypedUpdate

  def start!(word = nil, *words)
    respond_with :message, text: (word || "") + "hello!"
  end
end
```
ルーティングの設定
```ruby:routes.rb
Rails.application.routes.draw do
  telegram_webhook WebhookController
end
```

development.rbとproduction.rbに、パス生成用の設定を追加
```ruby:development.rbとproduction.rb
# 必ずhttpsじゃないといけない
# yourdomain.comは自分のホスト名に書き換える
routes.default_url_options = {host: 'yourdomain.com', protocol: 'https'}
```

以下を実行することで、Botにwebhookのリンクを設定できる (便利で嬉しい)
```
rake telegram:bot:set_webhook RAILS_ENV=production
```

### 6. Poller起動
ローカル環境で実際にWebhook用のAPIを叩かせるのは少しめんどくさいので、サーバーにデプロイされた時にwebhookが叩かれることを確認すればいい
ローカル環境ではpollerモードで動作確認する

```sh
# コンテナの中に入って...
docker-compose exec -it web bash

# Poller起動
rake telegram:bot:poller
```

### 7. 動作確認
Telegram Botに `/start` と送ってメッセージが返ってくることを確認する

<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100503/840d9a90-c761-17ae-cd72-26e8408dc34b.png">

完了！


### 作ったもの
https://github.com/OR-Sasaki/telegram-bot-qiita

# 補足
 - `telegram-bot-types` は導入必須ではない
開発支援用なので、何か不都合があれば入れなくて大丈夫
 - 基本的にWebhook推奨
 - テスト用に代替のクライアントを用意できる
https://github.com/telegram-bot-rb/telegram-bot?tab=readme-ov-file#testing
命名規則まとめ(〜ケース)

## はじめに
こんにちは、エンジニアを目指す大学院生の@bassaaa28です。
 
 今月から長期インターンに参加し、実務経験を積んでいます。実務では、様々なコーディングのルールがチームで設定されています。その中の一つにキャメルケースなどの`〜ケース`があります。PRでよく「〜ケースにしてください」というコメントをいただくため、今回はそれらの違いについてまとめたいと思います。

## まとめ
この表だけ見てもらえばバッチリです！
| 記法名              | スタイル  |
|-------------------|----------------|
| キャメル(ローワーキャメル)       | `userName`           
| パスカル(アッパーキャメル)       | `UserName`          
| ケバブ(チェーン)         | `user-name`       
| スネーク(ローワースネーク)       | `user_name`         
| アッパースネーク(スクリーミングスネーク、コンスタント) | `USER_NAME`

## キャメルケース (Camel Case)
　キャメルケースは、単語の区切りを大文字で表現する記法です。先頭の単語が小文字、以降の単語の先頭が大文字で始まります。**ローワーキャメルケース**と言われることもあります。ラクダ（キャメル）のコブのような形状に見えることが由来らしいです。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/628a6e04-c28a-ac24-d341-ec37d923f4fd.png" width="200">
>
> ウィキペディアより引用

**例:**
- `userName`
- `fetchDataFromAPI`

## パスカルケース
　パスカルケースはキャメルケースに似ていて、先頭の単語を大文字にする部分だけが異なります。**アッパーキャメルケース**と言われることもあります。プログラミング言語のPascalがこの記法を標準としているためパスカルケースと呼ばれるようになったみたいです。

**例:**
- `UserName`
- `FetchDataFromAPI`

## ケバブケース
　ケバブケースは、単語をハイフン(`-`)で区切るスタイルです。**チェーンケース**と言われることもあります。全ての文字は小文字で書かれます。ケバブのように見える？からこの名前だそうです。

**例:**
- `user-name`
- `fetch-data-from-api`

## スネークケース
　スネークケースは、単語をアンダーバー(`_`)で区切るスタイルです。**ローワースネークケース**と言われることもあります。全ての文字は小文字で書かれます。ヘビのように見える？からこの名前だそうです。

**例:**
- `user_name`
- `fetch_data_from_api`

## アッパースネークケース
　全ての文字を大文字にし、単語をアンダーバー(`_`)で区切るスタイルです。**スクリーミングスネークケース**、**コンスタントケース**と言われることもあります。
**例:**
- `USER_NAME`
- `FETCH_DATA_FROM_API`

## さいごに
　`〜ケース`は、コーディング規約や使うプログラミング言語によって推奨されるものが異なります。そのため、参加プロジェクトで採用されている命名規則を確認することが重要です。
MIT App Inventorの使い方（怠惰が楽するためのコツ！）

## はじめに
MIT App Inventorはビジュアルプログラミングで簡単なアプリならプログラミングがわからなくても簡単にアプリ作成できる素晴らしいコンテンツです．が，ブロックを組むときはネットからコピペができないといった不便さがあります．
このページでは，怠惰な人が楽にあぷりを作る為のちょっとしたコツを紹介します！
億劫になる様な作業はどんどん楽しましょう！

## コピー＆ペースト
ButtonやImage，VerticalScrollArrangementなどはコピー＆ペーストが使用できます．沢山のButtonやImageを配置するときに使えます！
ブロックもコピー＆ペーストを使用でき，Buttonのクリック時の動作が似ているときはコピペしてしまったほうが早いかもしれないです．

## ブロックのpng保存
**これ使えば複数人での開発が行える？**
ブロックはpng画像にして保存することができます．また，画像をブロック画面にドラック＆ドロップすることで，画像のブロックを作成できます．

- ブロックをpngで保存する
保存したいブロックの上で右クリックをし，英語のテキストが表示が表示されるので，下から2番目の`Download Blocks as PNG`をクリックします．保存先を選択し保存できます．

![スクリーンショット 2024-11-28 000706.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3928359/380e067f-4eeb-c37c-80eb-d38eea89f004.png)

- ブロックを使用する
保存したブロックのPNGデータをブロック画面へドラック＆ドロップを行います．白色の背景が画像を重ねると緑色になります．
![スクリーンショット (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3928359/98fe9003-6bbf-807d-bfb3-32670e9dd503.png)


## Androidエミュレータ
開発中はAICompanionで自身のスマートフォンに接続をしているとおもいます．ここで，開発を行うPCにAndroidエミュレータをインストールし,PC上でMIT Companionを行うことができます．PCにAndroidエミュレータを入れるメリットとしては，スクリーンがオフにならない，目線の移動が少ない，常に開発画面の近くに置くことができる点などがあげられます．一方，デメリットとしては一部のセンサやカメラが使用できないです（使用するエミュレータによって違うかも）．
筆者の感覚だと，スマホよりもAICompanionの切断が発生しにくいです．
[【8選】Windows 10/11で使える最高のAndroidエミュレーター](https://www.airdroid.com/ja/screen-mirror/android-emulators-for-windows-10-11/)

## 終わりに
折角難しいプログラミングを行わないでアプリが作れるのだから，できるだけ楽をして作れるといいですよね！
なぜ積極的にアウトプットしている組織が魅力的に見えるのか

## はじめに
どうしても組織でアウトプットしたかったので上司におねだりしまくっていたら、QiitaのOrganization作ってもらえました。
やったー！
なんだか個人でアウトプットするってなったらブログの方が資産化もできて良いんじゃないかと思っていた派だったのですが、Organizationならなぜかテンション上がるということで、これを機にQiitaにアウトプットしまくろうと思います。

ということで、本記事では私がこれまで漠然と感じていた「アウトプットしまくっている会社がなぜ魅力的に見えるのか？」を言語化していきたいと思います。

## 結論：技術力が高く見えるから
いきなり結論ですが、アウトプット量が多い会社は技術力が高く見えるからだと思います。
魅力を感じるポイントは人それぞれだと思いますが、少なくとも私は技術力が高い、もしくは技術力が高い組織になるために行動している組織というのはすごく魅力的に写ります。
やるなら上を目指した方が楽しいじゃんという至ってシンプルな理由です。

そしてアウトプットしている会社は技術力が高いように見えるんですよね。
これには複数の要因があると思うのですが、個人的には以下のようなナイスなループが回っている気がするんですよね。
1. アウトプットをした結果、社員が成長する
1. 社員が成長した結果、より質の高いアウトプットができるようになる
1. 質の高いアウトプットができるようになった結果、優秀なエンジニアに認知される
1. 優秀なエンジニアに認知されることで、優秀なエンジニアを獲得できる可能性が上がる
1. 優秀なエンジニアが獲得できた結果、より難しいことに挑戦できる組織になる
1. より難しい挑戦ができる組織になった結果、独自性の高いアウトプットができるようになる

とはいえまだ組織としてのアウトプットが始まったわけでもないので、これは完全に仮説で書いています。
ただどう考えても上記のループが起きる気しかしない。

さらにアウトプットをしていると、会社の知名度があがります。
どうしても人間なので知らないブランドよりも知っているブランドの方が優れて見えると思うんですよね。

また、エンジニア個人のメリットという観点で見ても、
* 技術力の向上
* 言語化能力の向上
* 将来転職するとなった時の武器になる

みたいなメリットがあってやる価値高いですよね。

もちろん書くのは面倒ですし、時間も労力もかかりますが、その対価が魅力的すぎるのでやるっきゃないですよね！？
ということで、本当に上記のメリットがあるかはこれからやってみて検証したいので、アウトプットしていきたいと宣言しておきます。

ということで、アウトプットをしている組織はとても魅力的だし、アウトプットをしている個人もとても魅力的だと思うので、強い意志でアウトプットを継続していきたいと思います。
クラスの関数に対して、selfを自分でバインドしなきゃいけなかったからしてみた

## 前置き
クラス内のすべての関数名をKey、関数をValueにした辞書を持ったクラスを作成しなければならなくなった。
selfをバインドしながら上記辞書を作成する方法を備忘録として残しておく。

また、self.関数名でバインドすることもできるが、関数の数だけ KeyとValueのセットを書かなければならないので、今回はその実装方法を採用しない。

もしより良い方法あればご指摘いただけると幸いです

**2024/11/28**: self.関数名 で良くねというご指摘を受けたので、状況が伝わりやすいようにサンプルコードや記事を修正

## やりかた

### これだとエラー（当たり前だが）
```python
import inspect

class Sample:
    def __init__(self):

        func_dic = {}
        for func_name, func in inspect.getmembers(self.__class__, predicate=inspect.isfunction):
            func_dic[func_name] = func
        
        self.func_dic = func_dic

        
    def print_test(self):
        print("hoge")

sample = Sample()
sample.func_dic["print_test"]()  # TypeError: print_test() missing 1 required positional argument: 'self'
```

### これでOK
```python
import inspect
from types import MethodType  # これが神

class Sample:
    def __init__(self):

        func_dic = {}
        for func_name, func in inspect.getmembers(self.__class__, predicate=inspect.isfunction):
            func_dic[func_name] = MethodType(func, self)
        
        self.func_dic = func_dic

        
    def print_test(self):
        print("hoge")

sample = Sample()
sample.func_dic["print_test"]()  # hoge

sample.print_test()  # hoge
```
TypeScript入門③配列

# 配列リテラル
配列リテラルとは、要は複数のデータの集まりのこと。
```typescript
let list : string[] = ['BELOVED', 'SOUL LOVE', '誘惑'];
```

実はそんなに今までと何かが変わるわけじゃないことが分かる。

自由に出し入れできるか
どんな名前か
：
どんな型か
配列か

これを定義しているだけ。
変数を定義するときはこの情報が必要なんだから、順番は分からずとも、どこかにこのパーツのいずれかがはいることが分かる。

## 応用！ユニオン型
型を定義する箇所がある。
ということはそこは自在に型を定義できる。
そう、ユニオン型だ。

```typescript
let list : (string | boolean) = [true,'サウンドスケープ','DREAM SOLISTER' ];
```
こんな形で「この型かこの型ね」というのを配列に定義できる。

## 応用！列挙型
列挙型とはなにか。
そう、定数をまとめて定義できるというアレ。
そんなもん、配列使えばいいじゃないか！とずっと思っていたあれ。
あれって、ただの定数まとめじゃなくて、そういう「型」を作ってるんじゃないか？
と思った。で、実行してみたら行けた。

```typescript
enum Metalgear {
    sneak = 'スネーク',
    fox = 'サイボーグ忍者',
    kazu = 'カズ',
}

let list3: Metalgear[] = [Metalgear.fox,];

console.log(list3);
```


## 配列の別の定義の仕方
あるんかい！
```typescript
let list: Array<stirng> = [x, y, z];
// これと同じ(xyzには文字列が入ってるとして・・・)
let list: string[] = [x, y, z]
```

# readonly
読んで字のごとく、読み取り専用。
これを書いた配列は読み取りしかできなくなる。
変数に代入したり、何かを追加したりができなくなる

```typescript
 let list : readonly string[] 
```

# 配列の分割代入
配列の要素のうち、特定のいくつかをある変数に入れたり、配列に入れたりできる。
```typescript
let soldier:Array<string> = [
'ビッグボス',
'オセロット',
'和平',
]

let list[x,y] = soldier;

//結果は[ビッグボス、オセロット]

```
別の配列にある配列を入れようとすると、前から要素の数だけ順番に入れる。


```typescript
let [,,z] = soldier

console.log(z) // zに三つ目の値がはいる

//こうやると前のふたつをスキップして三つ目だけ入れるよ、みたいなことができる
//注意すべきはletのあとに変数名が来ないこと。
```

# スプレッド構文
配列の中身をおっぴろげる。＝スプレッド
中身を全部展開して代入する
```typescript
let list = [...x]
```

```typescript
let soldier:string[] = [
    'ビッグボス',
    'オセロット',
    '和平',
    ];

let x =[...soldier]
// xの中身は
[
    'ビッグボス',
    'オセロット',
    '和平',
];
```

いや、ちょっと待って
冷静に考えてこれなんの意味あるの？
と思った人はセンスある。

これは配列のコピーに使う。
いや、それは
```typescript
let soldier: string [] = [
    'ビッグボス',
    'オセロット',
    '和平',
];

let listcopy = soldier;
```
ってやればいいじゃんと思うはず。

でもこれはすごく危険。なぜならこれは参照渡しで、コピーじゃないから。
配列はオブジェクトである。
オブジェクトの代入は参照渡しである。
参照渡しとは、同じ場所にあるものを色んな所から参照すること。
だからある個所から、その本体にあくせすして値を書き換えると、それを参照している箇所すべてが変更される。
だから迂闊にはやれない。やっちゃいけないわけじゃない。

そんなときに参照じゃなくて、コピーがしたい、ってなるはず。
```typescript
let list:string[] = [...soldier]
```
ちなみに配列をふたつくっつけるとかもできる.これはいい！
```typescript
[...soldier...soldier]
```


# 繰り返して配列の中身全部出してみた
for of という最高の文がある
```typescript
let soldier: string [] = [
    'ビッグボス',
    'オセロット',
    '和平',
];

for (let soldier of soldiers) {
    console.log(soldier);
}

```

ofの前後がどっちだけ？ってなりそうだが
私はone of themで覚えた。
一部　of 全体
ってかんじ。
大事なのはlet　を　oneの方に付けること。
oneはここで初めて定義する変数なので、変数宣言をする必要がある。

他にも繰り返しの方法としてはArrayオブジェクトのメソッド、forEachとかがある。
が、for ofがあれば使わない気がするので割愛。
長いし。

ちなみに、breakで処理を終了できる
```typescript
for (let soldier of soldiers) {
    console.log(soldier);
    if (soldier === 'オセロット') {
        // オセロットなら処理を終了
        break;
    }
}
```
Nuxtプロジェクトで 「DEPRECATION WARNING: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0」 が大量にでてきたのでやったことのメモ

# 概要

- Nuxt起動しているときに気がついたが、`DEPRECATION WARNING: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0`が大量にログで流れるようになった
- sassのバージョンをあげたことによる影響らしい
- これを修正したのでメモする

# 各種バージョン

- nuxt@3.14.1592
- sass@1.81.0

# 原因

参考URLたちを読み漁る

https://sass-lang.com/documentation/breaking-changes/legacy-js-api/
https://github.com/nuxt/nuxt/issues/30007
https://qiita.com/bearl27/items/88754480880acf45a3b3
https://zenn.dev/levtech/articles/1174c95ef81cbb


* sass@1.69.5にダウングレードしたら、表示はされなくなるが、2.0.0で使えなくなるAPI利用しているので、はやくなんとかしろとのこと
* これにより、エラー出力が大量にでてきて、Nuxtの開発サーバ起動が遅くなり困った


# やったこと

## `nuxt.config.ts` の `vite -> css -> preprocessorOptions -> scss -> api` を追加

`modern-compiler` を選択すると、Dartで開発されたAPIを利用するようになるので、高速になるとのことだけど、今回は移管先がそれなので、それでトランスパイルするように固定する

```typescript:nuxt.config.ts
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          api: "modern-compiler", // 追加
          // ...
```

## `@imoprt`でインポートしている箇所を`@use`に変更する

`@import`はだいぶ前に非推奨になっているとのこと。まじか（全然知らなかった）

というわけで修正する

```scss
@use './font';
```

namespaceを作りたい場合は、こうすると良い

```scss
@use './font' as font;

body {
  font-size: font.$font-size; /** こうやって指定する */
}
```


## `nuxt.config.ts -> @/assets/index.scss -> 各種scss` をロードするようにしてたが、それをやめる

当初こういう作りにしていた

```typescript:nuxt.config.ts
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "@/assets/index.scss" as *;'
          // ...
```

```scss:./assets/index.scss
@import './base';
@import './layout';
@import './color';
@import './font';
```

じゃあこうすればいいよねってことで下記にしたが、Componentのstyleから定義されていないエラーがめっちゃでたので、認識されていないようでした

```scss:./assets/index.scss
@use './base' as *;
@use './layout' as *;
@use './color' as *;
@use './font' as *;
```

よくわからず、タイムアップしたので、色々試行錯誤してやったのがこれ


```typescript:nuxt.config.ts
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `
            @use "~/assets/color" as *;
            @use "~/assets/base" as *;
            @use "~/assets/layout" as *;
            @use "~/assets/font" as *;
          `,
        // ...
```

`./assets/index.scss` は削除した。これでうまく反映はされたが原因はまだわかっていない（確認中）


## その他

使えなくなっている関数（？）とかあったので、ちまちま修正した。`darken`を`color.adjust`にするなど


# 雑感

とりあえず修正できたよかった
Python　辞書について

## 昨日、今日始めた人レベルの悩み

東大が出してるPythonプログラミング入門なる資料で学習を進めていて辞書（dictionary）で頭がこんがらがってきたので自分の記録として残します。

### 9.1 練習
リスト list1 が引数として与えられたとき、list1 の各要素 value をキー、value の list1 におけるインデックスをキーに対応する値とした辞書を返す関数 reverse_lookup を作成してください。
以下のセルの ... のところを書き換えて reverse_lookup(list1) を作成してください。

まず問題文からして何を言ってるのか読み解くのに暫く時間がかかる感じでしたが要するに引数list1の要素をキーにして、値を対応するキーにして返す関数を作ってねってことでした。多分言ってることと書いてること同じですが初見だと悩みました。

#### リスト？タプル？辞書？な自分が最初に書いたコード
```def reverse_lookup(list1):
  for value in list1:
    fruits = value,reverse_lookup[0::1]
  return fruits
  ```

#### 結果：
```
print(reverse_lookup(['apple', 'pen', 'orange']) == {'apple': 0, 'orange': 2, 'pen':1})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-f122f1f91ede> in <cell line: 1>()
----> 1 print(reverse_lookup(['apple', 'pen', 'orange']) == {'apple': 0, 'orange': 2, 'pen':1})

<ipython-input-20-ee2800cc7bb7> in reverse_lookup(list1)
      1 def reverse_lookup(list1):
      2   for value in list1:
----> 3     fruits = value,reverse_lookup[0::1]
      4   return fruits

TypeError: 'function' object is not subscriptable
```
見返すと訳わかんないコード書いてますがChatGPT様曰く
### 問題点
reverse_lookup に対するスライス操作 reverse_lookup[0::1]:
reverse_lookup は関数であり、リストではありません。そのためスライス操作（[0::1]）は適用できず、TypeError: 'function' object is not subscriptable が発生します。
fruits の定義:
value, reverse_lookup[0::1] という形式でリストの値とインデックスを組み合わせていますが、これは意図した結果になりません。
辞書の作成方法:
辞書を作成するためには、キーと値のペアを管理する必要がありますが、その処理が欠けています。

つまりスライスは文字列やリストに使えるよ！でも自分は関数に対して使ってるからダメ。
そもそも辞書の作り方すら間違ってるよ！ってことでした。

## 解決策
辞書を作るにはからの辞書をまず作る
```
fruits = {}
```
enumerate()関数を使う(以下資料からコピペ)
Python では enumerate() 関数が用意されており、上のプログラムは以下のように書き換えることができます。
```
for i, val in enumerate(some_list):
```
繰り返させたい処理
2 つの変数 i, val が指定されています。 i には 0, 1, 2, … が順に代入されます。 val にはリストの要素が
順に代入されます。

これを踏まえて
```
def reverse_lookup(list1):
  fruits = {}
  for index, value in enumerate(list1):
    fruits[value] = index
  return fruits
```
```
print(reverse_lookup(['apple', 'pen', 'orange']) == {'apple': 0, 'orange': 2, 'pen':1})

True
```
無事解決しました！関数とかすぐ忘れちゃうからその都度確認した方がいいですね。
記事に起こすことで改めて理解が深まった気がします笑

### 以上
【GitHub】リモートリポジトリのインデントがズレる問題

## 問題
ローカルのコードをリモートにpushしたとき、インデントがズレる(大きくなる)。

![スクリーンショット 2024-11-28 0.12.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/3a2dcc18-7513-a5b0-cb2d-4227d547fa55.png)

エディタ上では問題ないのにリモートに上げるとズレてしまう...
![スクリーンショット 2024-11-28 0.14.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/5ac860b6-99b4-a710-6a64-f6b6e2c5f37f.png)


## 原因
リモートにプッシュするとインデントがずれる原因は**Tabによるインデントを行っていたからである**。また、GitHubのTabによるインデントの設定がデフォルトだと大きくなるようになっていた。

大前提、インデントには`Tab`と`Space`の2種類がある。恥ずかしながら私は、TabとSpaceの区別がついておらず、Spaceの集まりがTabだと勘違いしていた...

## 対策
### 1. Tabによるインデントを防ぐ
#### インデントの設定方法(VSCode)
① 画面下を見ると、そのファイルのインデント設定が表示されている。クリックすると画面上にアクションが表示される。
② スペースによるインデントか、タブによるインデントかを選択する。
![スクリーンショット 2024-11-28 0.24.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/091d42e9-a1f9-0ab2-267d-9312ab4de157.png)

#### Tabとインデントを視覚的に区別しやすくする方法(VSCode)
　VsCodeの設定画面で`Render Whitespace`と検索し、`selection→all`にする。この設定で、TabとSpaceの混在を防ぐことができる。ChatGPTやサイトに載っているコードをコピペした時、よくTabが入ってしまうことがあるため、おすすめの設定だ。

 ![スクリーンショット 2024-10-31 11.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/cfb9cc5f-80a8-dafa-0a18-6609aceaa49f.png)

### 2. Tabによるインデントをしても勝手にズレないようにする
　GitHubの`settings → appearance→Tab size preference`を**8→2**に変更するとズレがなくなる。デフォルトでは8になっている。そのため、1Tab = 8Spaceになってしまい、ズレてしまうというわけだ。
![スクリーンショット 2024-10-31 10.51.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/19a38087-1eb7-3e80-542f-9b2bf112c4e6.png)
【UE5/C++】UObject クラスを継承してシングルトンクラスを作る

# はじめに
常に同じ変数などを参照するために、シングルトンなクラスを作りたかったため作ってみた。

# シングルトンクラスを作る
TestSingleton クラスをシングルトンクラスとして扱う。自身のインスタンスを static で保持、コンストラクタを private にして外部からインスタンス化できなくする。

```TestSingleton.h
#pragma once

#include "CoreMinimal.h"
#include "Test.generated.h"

UCLASS()
class TESTGAME_API TestSingleton : public UObject
{
	GENERATED_BODY()

public:
	// シングルトンオブジェクトを取得するため static メソッドにする
	static UTestSingleton* GetInstance();

private:
	// シングルトンにするのでコンストラクタは private にする
	UTestSingleton();

	// インスタンスを private static で保持する
	static UTestSingleton* Instance;

	int32 Count;

public:
	void IncreaseCount();
};
```

```Test.cpp
#include "Test.h"

// 自身のインスタンスを nullptr で初期化
UTestSingleton* UTestSingleton::Instance = nullptr;

UTestSingleton::UTestSingleton()
{
	Count = 0;
}

UTestSingleton* UTestSingleton::GetInstance()
{
    // インスタンスがなければ作り、あればそれを返す
	if (Instance == nullptr)
	{
		Instance = NewObject<UTestSingleton>();
		Instance->AddToRoot(); // ガベージコレクション対策
	}
	return Instance;
}

int32 UTestSingleton::IncreaseCount()
{
	Count++;
}
```

# 使う側のクラス
このクラスがインスタンス化されたときに、シングルトンクラスのインスタンスを static メソッドにアクセスして取得する。
注意点として `UPROPERTY()` こいつを忘れるとガベージコレクションに回収されて使えなくなる可能性があるらしい。
Rider 上でこんな警告を確認した → `Object member 'TestSingleton' can be destroyed during garbage collection, resulting in a stale pointer`

```Test.h
#pragma once

#include "CoreMinimal.h"
#include "TestSingleton.h"
#include "Test.generated.h"

UCLASS()
class TESTGAME_API Test : public AActor
{
	GENERATED_BODY()

public:
	ATest();

protected:
	virtual void BeginPlay() override;

public:
	virtual void Tick(float DeltaTime) override;

private:
	UPROPERTY()
	UTestSingleton* TestSingleton;
};
```
```Test.cpp
#include "Test.h"

ATest::ATest()
{
    PrimaryActorTick.bCanEverTick = true;

    // シングルトンなインスタンスを取得している
    TestSingleton = ATestSingleton::GetInstance(); // インスタンスの取得
}

void ATest::BeginPlay()
{
    Super::BeginPlay();
}

void ATest::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    
    TestSingleton->IncreaseCount();
}
```
個人用メモ#3

ConcurrentHashMapを使用する場合でも更新をする場合はatomicなメソッドを使わないといけない
putIfAbsentで初期値を代入し、computeIfPresentで更新を行う。
``` java
package com.example;

import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class S3ParallelFileDownloaderWithCompletableFuture {
    private static final String BUCKET_NAME = "mmnkbucket";
    private static final String FILE_PREFIX = "future/"; // ファイルの連番部分以外の名前
    private static final int FILE_COUNT = 1000000; // ファイル数
    private static final int THREAD_COUNT = 10; // 並列処理数

    public static void main(String[] args) {
        LocalDateTime nowDate = LocalDateTime.now();
        System.err.println("start : " + nowDate);
        
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(THREAD_COUNT);
        int min = 1;
        int max = 5;

        Random random = new Random();
        // CompletableFutureで並列にファイルを取得
        List<CompletableFuture<Void>> futures = IntStream.rangeClosed(1, FILE_COUNT)
                .mapToObj(fileIndex -> CompletableFuture.runAsync(() -> {
                    int value = random.nextInt(max + min) + min;
                    map.putIfAbsent(Integer.toString(value), 0);
                    map.computeIfPresent(Integer.toString(value), (k, v) -> ++v);
                }, executor))
                .collect(Collectors.toList());

        // 全ての非同期処理の結果を集約
        futures.stream()
                .map(CompletableFuture::join) // 各非同期処理の完了を待つ
                .collect(Collectors.toList());

        int count = 0;
        map.forEach(1, (key, value) -> {
            System.err.println(key + ", " + value);
        });
        for (Map.Entry<String, Integer> entry : map.entrySet()){
            count = count + entry.getValue();
        }
        System.err.println(count);

        // 取得した結果を表示（または他の処理に使用）

        LocalDateTime endDate = LocalDateTime.now();
        System.err.println("end : " + endDate);

        executor.shutdown();
    }

}
```
カレーライスを作るときにウォーターフォールとアジャイルで何が違うのか

ウォーターフォールってなに？
アジャイルってなに？
を考えるときに身近なカレーライスを題材に考えてみました。

まあ戯言なので細かいことは気にせずお願いします。


# 各工程を考える

アプリ開発だと
要件定義→設計→実装→テスト→リリース
ですね。

カレーライスに置き換えると
要件定義：買い出し
設計：下ごしらえ、前半の調理
実装：火を使ったりする後半の調理
テスト：味見
リリース：実際に食べてもらう


とします。
まあ料理もアプリ開発も色々あるからそうじゃねえってのもありますけどね。。。

ではウォーターフォールのポイント

- 工程は順番通りにやる
- 工程が変わるときは工程が終わる条件を満たす
- ダメなときは所定の位置まで戻る

アジャイルのポイントは・・・
- 小さく動くものを作る
- 要件定義～リリースまでやってみる
- くりかえしやる
- 必要なものは必要なときにやる

まあ、作るものはカレーだし、ちゃんとやらなくてもカレーにはなるかなと思います。

# どういうカレーを作るの？
この記事の要件定義は買い出しなので

あらかじめカレーライスはどんなのを作るか定義しておく必要があります。

- ポークカレー
- 野菜は玉ねぎ、にんじん、じゃがいも
- カレーライスなのでご飯
- 飲み物は最近流行ってるラッシー




# ではウォーターフォールだとどうやってカレーを作るの？

要件定義は買い出しなのですべての材料を用意しましょう
- ポークカレーの豚肉、カレールウ
- 野菜の玉ねぎ、にんじん、じゃがいも
- ライスを作るためにお米と水
- ラッシーはなんだろう、牛乳とヨーグルトで良いのかな
- あ、そういえば油とか塩とかコショウとかいるかもですね。

設計の下ごしらえはいろいろな準備します
- 豚肉はちょうどいい大きさに切りましょう
- カレールウは分量を確認するだけでいいかな
- 玉ねぎは皮をむく
- 玉ねぎはみじん切り
- 人参は切ります
- じゃがいもは皮をむきます
- せっかくだから玉ねぎは、あめ色に炒めましょうか
- お米は分量を計ってといだらいいかな
‐ ラッシーはよくわからんけど分量を計ればいいかな

実装の本格調理は色々やります
- 豚肉は油で炒めます
- 野菜も一緒に炒めます（玉ねぎは下ごしらえで炒めてある）
- お米は炊飯器にいれて水を入れて炊飯器のスイッチを入れる

テストは味見
- 美味しかったらOK
- ダメだったら、調理がダメだったのか、下ごしらえがダメだったのか、買い出しがダメだったのか分析
- ダメだったところまで戻ってやり直し
・・・アプリは工数かかるけど、最初まで戻らずに、スーパーに買い出しいかなくても調理パートからやり直せるから良いですね。
カレー焦がしたら、結局スーパーに買いに行かなくてはいけません。

リリースはお客さんに実際に食べてもらいましょう

美味しい、美味しくないの感想を聞いたり
自分でも食べてみたり
ここから改善点を見つけたりして良くしていくのが運用保守かな？


# アジャイルだったらどうやってカレーを作るの？

アジャイル開発は小さいものを作って回していくやり方です。
スプリントで繰り返すやり方で完成品近づけていく
要件定義、設計、実装、テスト、リリースを1個のスプリントでやってみる

スプリント１　とりあえずカレー作ってみるか

- スーパーで豚肉とカレールウを買う：要件定義
- 豚肉を切る　カレールウの分量を決める：設計
- 豚肉を炒めてカレールウで煮る：実装
- 味見をする：テスト
- お客さんに食べてもらう：リリース

食べちゃったら、豚肉もカレールウもスーパーに買いに行かなくちゃいけないですが
アプリ開発は同じものなら使い回せるから、同じ豚肉を買うなら買わなくてもいいのです。
味見してダメだったら別の豚肉を買うからスプリント１もやり直しですね。

スプリント２　カレー＋野菜を作ってみよう
- スーパーでにんじん、じゃがいも、玉ねぎを買ってくる：要件定義
- にんじんを切る、じゃがいもの皮をむく、玉ねぎの皮を向いてみじん切り、あめ色に炒める：設計
- 具材を炒めてカレーを煮込む：実装
- 味見をする：テスト
- お客さんに食べてもらう：リリース

次はご飯を作りましょうか
スプリント３　カレーライスを作ろう
- スーパーでお米と水を買ってくる（水道でもいいけど水にもこだわりたいよね)」：要件定義
- お米をとぐ、水の分量を計る：設計
- 炊飯器のスイッチを入れる：実装
- 味見をする：テスト
- 豚肉＋野菜のカレーをご飯にかける：リリース準備（または実装の後半でも良い）
- お客さんに食べてもらう：リリース

ラッシー作って完成。スプリント４で最終スプリント、最終リリース 

スプリント４　これが最終スプリント　ラッシーってどうやって作るんだ？
- どうやらヨーグルト、牛乳、砂糖、レモンが必要らしいからスーパーで買ってくるか：要件定義
- 分量は大事だよね。計ってみよう。レモンは洗って軽く刻んてみよう：設計
- ミキサーにかけてみよう（どのくらいでいいんだろう？）：実装
- 味見してみよう：テスト
- お客さんに飲んでもらおう：リリース
- これまで作ったものもためてもらおう：リリース２

さて、結論です。

# ウォーターフォールとアジャイルどっちが良いの？
- 美味しければどっちでも良い
- 短い時間で出来ればなお良い
- お金は無駄にかからなければ良い
- 安く済めばさらに良いね

ってな感じで、どっちでも良いんですが
現実世界ではカレーライスづくりを失敗したら、途中のものは使えないのでスーパーに買い出しからやり直しですが
アプリケーションはダメなところからやり直せるから、失敗しても大丈夫（？）です。
楽しんだら勝ち

# はじめに

はじめまして。
リンクラフト株式会社でエンジニアをしています tsasaki_ ともうします。
今季は私の推しのフットボールクラブが、リーガ3部から昇格したことにより感情の昂りが抑えられません！デポル（ラ・コルーニャ）に再び栄光を！！とか、勝手に叫んでいるマイノリティです。

この記事は [LINCRAFT Advent Calendar 2024](https://qiita.com/advent-calendar/2024/lincraft) の2日目の記事となります。

# 前置き
『人生楽しんだもん勝ち』や『楽しまないと損』といった言葉は、よく耳にする普遍的なフレーズです。
そこで、なぜ人は「楽しむ」のかについて考えてみたいと思います。

:::note info
記事のPoint
* なぜ人は楽しむのか？
* 楽しくないとどうなるの？
* 楽しむためには
:::

# なぜ人は楽しむのか？
少し哲学的に聞こえるかもしれませんが、私自身はそこまで深く考えているわけではありません。楽しむのは楽しみたいからなのです！

「楽しむ」を理解するために、逆の観点から対義語を見てみましょう。

:::note warn
苦しい ⇔ **楽しい** ⇔ つまらない
:::

苦しいことやつまらないことを解消するには、楽しいことが必要です。楽しいを感じることなく、苦しさやつまらなさだけが続くのはマイナスの感情に過ぎません。

**今すぐにでも方向を変える努力をすべきです。**

一方で、苦しさを楽しさで乗り越えたり、つまらなさが後の楽しい経験に繋がるのであれば、人は楽しい方を鮮明に記憶するでしょう。
多くの楽しみを経験し、充実した人生を送ることこそが、楽しいことに価値があるという証拠です。

# 「楽しい」からの行動
「楽しい」から連想される具体的な行動について考えてみましょう。

人には欲求があるので、楽しいと感じれば継続ができ、楽しいことに対してさらに挑戦したいという意欲が湧きます。時には、さらなる上達を目指そうという思考にも繋がります。

世の中の嗜好品と呼ばれるものに例えるとわかりやすく、お酒、ゲーム、推し、etc...。

* 今日より楽しいお酒が飲みたい
* 昨日よりハイランクのステージに挑戦したい
* 去年より楽しいステージが見たい

仕事の例を挙げてみても、あの業務はキツかった...けれどあの苦難を乗り越えたから成長し、今ではより難易度の高い業務にも挑戦できている。など、楽しい要素が絡むことで前向きな印象を感じとれませんか。

# 楽しくないとどうなるの？
先ほど書いた言葉を引用すると、継続したくなくなり、挑戦もしなくなるでしょう。問題を解決できない可能性がある場合、諦める選択肢も有効かもしれません。

ただ、諦める前に「なぜ楽しくないのか」の原因分析と「どうしたら楽しむことができるのか」の問題分析を行うと、その後の選択肢が広がるはずです。
後悔や「勿体ない」が生まれないようにしましょう。勿体ないお化けが出ますよ。

# 楽しむためには
繰り返しになりますが、「なぜ楽しくないのか」の原因を探ることが大切です。この原因を解明するには、周りの人々の意見を参考にすることも有効です。自分だけが楽しくないと感じている場合でも、他の人の視点を取り入れることで、新たな気づきを得られることがあります。案外、自分が気づいていない楽しみのポイントを他の誰かが見つけているかもしれません。

例えば、次のような状況を考えてみてください。

あなたが日々、上司から厳しく指摘されていると感じているとします。その度に「どうして自分ばかりがこんな目に？」と不満に思うかもしれません。しかし、もしその上司があなたの成長を真剣に考えて、間違った方向に進まないように指導してくれているのだとしたら、どうでしょうか？「怒られる」という感覚も、実は「自分を成長させようとしてくれている」と捉えれば、見方が大きく変わります。

また、あなたが与えられた課題を「重い」と感じることもあるでしょう。でも、その課題をクリアしたときに自分がどれだけ成長したかを実感できるはずです。そのためには、課題そのものを蔑ろにするのではなく、どうやって乗り越えようかと前向きに取り組むことが重要です。

このように、同じ状況でも視点を少し変えるだけでその捉え方が大きく変わり、楽しみに変換できる可能性を見つけることができるかもしれません。

# つまりは
あなたが楽しむためには、周りの人に協力を仰ぐのも一つの手段です。あなたの視点だけでは見逃していた「楽しみ方」や「解決策」が、周りの誰かの視点にあるかもしれません。

# さいごに
ここまで読んでいただきありがとうございます！もちろん、すべてがうまくいくわけではありません。私自身も過去にたくさんの失敗を経験して、楽しめていない時期もありました。幸い今の私は周りの人に恵まれており、意見や助けをもらいながら楽しめている実感があります。皆さんもより楽しい人生を求めてください。

楽しむことこそが、有意義な人生を送るために必要なことだと考える私の独り言でした。m(__)m
Forza Dépor！（デポルよ強くあれ！）
***
一緒に働く仲間を募集中です！

リンクラフト株式会社では、組織拡大に伴い積極的な採用活動を行っています。
少しでも興味がある方はぜひご連絡ください。

▽会社ホームページ
https://lincraft.co.jp/
▽Instagram
https://www.instagram.com/lincraft.inc/
▽ご応募はこちらより
https://lincraft.co.jp/recruit

※カジュアル面談も受付中です。ご希望の方はHPのお問い合わせフォームよりご連絡ください。
【VSCode】erbをファイル保存時に整形する方法

## 拡張機能をインストール

ERB Formatter/Beautifyという拡張機能をインストールします。

https://marketplace.visualstudio.com/items?itemName=aliariff.vscode-erb-beautify&ssr=false#overview

## Gemをインストール

上記の拡張機能を使用するためにはHTML Beautifierが必要なのでインストールします。

```Gemfile:Gemfile
group :development do
  gem 'htmlbeautifier'
end
```

で`bundle install`。
または、ターミナルで以下のコマンドを実行します。

```terminal:ターミナル
gem install htmlbeautifier
```

https://github.com/threedaymonk/htmlbeautifier?tab=readme-ov-file#installation

## 設定を記述する

`settings.json`に以下の記述を追記します。

```jsonc:settings.json
"[erb]": {
  "editor.defaultFormatter": "aliariff.vscode-erb-beautify",
  "editor.formatOnSave": true
},
"files.associations": {
  "*.html.erb": "erb"
}

// invalid byte sequence in US-ASCII エラーが発生する場合
"vscode-erb-beautify.customEnvVar": {
  "LC_ALL": "en_US.UTF-8"
}
```

上記の設定を記述するとファイル保存時にフォーマットされるようになります。
BERT（単語の埋め込み表現への変換）のトークン数と推論速度の関係

BERTのText Embeddingのトークン数とCPU環境での推論速度の関係を実際に実行して調べてみました。


結果のノートブックは以下のリンクです。
https://github.com/tkeshun/bert-token-time-test/blob/main/BERT_TOKEN_time.ipynb

以下に手順を示します。

## 事前準備

```
# ライブラリのインストール
pip install torch transformers
# 実行環境の情報の確認
!cat /etc/os-release
```

- OS
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462734/d7a32a0d-d706-0780-e422-ec92c8d5ef0e.png)

- CPU
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462734/63706437-175d-bc67-8cc3-fd5467c1cf15.png)

- メモリ

使用可能メモリは大体13GBくらいでした

## Text Embeddingの実行

だんだん文章の量を増やしながら、推論していきます。


```python
import torch
import time
from transformers import BertTokenizer, BertModel

# 日本語BERTモデルのトークナイザーとモデルの読み込み
tokenizer = BertTokenizer.from_pretrained('cl-tohoku/bert-base-japanese')
model = BertModel.from_pretrained('cl-tohoku/bert-base-japanese')

# 初期の短い日本語テキスト例文
initial_text = "これはサンプルの文章です。"
max_tokens = 512  # BERTのトークン上限数

# トークン数を増やすための例文リスト
example_texts = []
current_text = initial_text

# 512トークンに達するまで例文を生成
while True:
    # トークン化とトークン数の計算
    inputs = tokenizer(current_text, return_tensors='pt', truncation=False, padding=False)
    token_count = inputs['input_ids'].size(1)

    # 現在の例文とトークン数を表示
    print(f"Text: {current_text[:50]}...")  # 長い場合は冒頭部分のみ表示
    print(f"Number of tokens: {token_count}")

    # トークン数上限に達したら終了
    if token_count >= max_tokens:
        example_texts.append(current_text)  # 最後の例文もリストに追加
        break

    # 例文をリストに追加して、テキストをさらに長くする
    example_texts.append(current_text)
    current_text += " " + initial_text  # 繰り返してテキストを追加

print(f"\nGenerated {len(example_texts)} example texts with increasing token counts.\n")

# CPUでのエンベディング計算用にモデルを設定
device = torch.device('cpu')
model.to(device)
model.eval()

# 各例文についてエンベディングを計算し、推論時間を測定
for idx, text in enumerate(example_texts):
    # トークン化してテンソルに変換
    inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True).to(device)

    # 推論時間の計測開始
    start_time = time.time()

    # エンベディング計算
    with torch.no_grad():
        outputs = model(**inputs)
        cls_embedding = outputs.last_hidden_state[:, 0, :]  # [CLS]トークンのエンベディングを取得

    # 推論時間の計測終了
    end_time = time.time()
    inference_time = end_time - start_time

    # 結果の表示
    print(f"Example {idx+1}: {len(inputs['input_ids'][0])} tokens, CLS Embedding Shape: {cls_embedding.shape}, Inference Time: {inference_time:.4f} seconds")
```


### 結果

最大に近くなっても、推論時間２秒ほどであり、CPUでもそれなりの処理速度でした。
GPUがないと使い物にならないのかな？と思ってたので意外な結果でした。
短い文章や推論速度がそれほど要求されないのであればCPUでの使用も選択肢に入りそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462734/f5c6bf6b-6ff7-df49-1f29-a2fbccad1e7f.png)
font-familyで「ヒラギノ明朝」を指定すると、Windowsでフォールバックフォントの太さが変わってしまう問題の回避策

## 問題の概要

Webサイトで「ヒラギノ明朝」を指定した際、Windows環境でフォールバックフォントの文字が予期せず太く表示される問題について解説します。

### 問題が発生するケース

```css
font-family: "YakuHanMP", "Hiragino Mincho ProN", "Yu Mincho", serif;
```

**Windowの場合に、"Hiragino Mincho ProN"を指定した箇所だけ、太さが変わる**
![font-family_2411272203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/814240/1919a86d-1bd8-6804-2441-b19b2cfc6115.png)


## 解決策

### 1. CSSの実装

```css
/* フォントファミリーの基本設定 */
:root {
  --ff-mincho: "Yu Mincho", serif;
}

/* Mac環境のみヒラギノ明朝を適用 */
.macos {
  --ff-mincho: "Hiragino Mincho ProN", "Yu Mincho", serif;
}

/* 明朝体を使用する要素に適用 */
:lang(ja-MP) {
  font-family: "YakuHanMP", var(--ff-mincho);
}
```

### 2. JavaScriptの実装

device.jsを使用してOS判定を行います。

#### スクリプトの読み込み

```html
<!-- type="module"属性が必須 -->
<script type="module" src="/js/device.min.js" crossorigin></script>
```

#### device.jsのカスタマイズポイント

1. 対象要素の変更
```javascript
// 変更前（<html>要素）
const documentElement = document.documentElement;

// 変更後（<body>要素）
const documentElement = document.body;
```

2. クラス付与の処理
```javascript
if (device.macos()) {
  addClass('macos desktop')
} else if (device.windows()) {
  addClass('windows desktop')
}
```

## 実装上の注意点

1. JavaScriptモジュールとしての読み込み
   - `type="module"`属性が必須
   - クロスオリジン制約に注意（`crossorigin`属性の追加）

2. WordPressでの利用
   - `body_class()`との併用が可能
   - テーマのURLは`do_shortcode()`等で動的に生成

```php
<script type="module" src="<?php echo do_shortcode('[topthemeurl]'); ?>/js/device.min.js" crossorigin></script>
```

3. パフォーマンスへの影響
   - JavaScriptの読み込みによるわずかな遅延
   - フォントの切り替わりによるちらつき防止の検討

## まとめ

この解決策により、以下の利点が得られます：

1. OS環境による適切なフォント制御
2. 予期せぬフォントウェイト変更の防止
3. WordPressなどのCMSとの互換性
4. 将来的なフォント追加・変更への対応のしやすさ

device.jsを使用したOS判定と、CSS変数によるフォント制御を組み合わせることで、クロスブラウザでの一貫した表示を実現できます。
[AWS #32] 請求とコスト管理はどうやって確認しましょうか？②

<!--
 <font color="c00000"></font> 赤
-->
## はじめに
皆さまお疲れ様です。
今回もAWSの <font color="c00000"> **「請求とコスト管理」** </font>について見ていきたいと思います。その中でも今回は **「AWS Budgets」** **「コスト配分タグ」** について見ていきたいと思います。従量課金であるAWSは<font color="c00000">**実際の課金額が予測しにくい**</font>点があります。EC2などのサービスは、オートスケールによってサーバーの台数が増減、ストレージ容量が増加など、当初予定していた金額と差分が生じる可能性があります。

## AWS Budgetsは「予算を管理する」
![2-aws-budgets-dashboard-1024x278.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806875/29b67425-5d6d-50e9-a0ce-ea54fac868e2.png)
AWS Budgetsは<font color="c00000"> **「予算を管理」**</font>します。予算を設定し、実績と予測に対してのアラートアクションを設定できます。**予算は金額だけでなく**、**リクエストの量、データの転送量などの使用量**にも設定ができます。

### Budgetsのメリット
**・アラートの設定が可能**
➡ 予算を設定し、予算を超えた場合にメールなどでアラートを発生させることが可能です。

**・条件に基づいた予算管理が可能**
➡ サービス毎やアプリケーションのアカウント、使用しているリージョンなど様々な単位で予算と利用実績を管理することが可能です。

**・レポートの作成が可能**
➡ 毎日、週ごと、月ごとのレポートを作成することが可能。

### コスト配分タグ
![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806875/6c20f4c6-b625-5908-75a6-c3e305fca132.png)
**コスト配分タグ**とは、コストエクスプローラーやコストと使用状況レポートなどの明細で、<font color="c00000">**タグ別に料金を表示するため**</font>に利用されるタグのことです。

**・目的**
➡ **EC2インスタンスなどに代表されるリソースの管理を容易にするため**
「何の目的で利用されるのか」「所有者は誰か」「どの環境で使用されているか」
これらの情報を付与することによって、リソースを分類、識別します。

タグを「コスト配分タグ」としてアクティブにすると、Cost Explorerでの分析や、Budegetsでのフィルターにも使用できます。特定のプロジェクトに発生した請求で分析したり、予算を管理できたりします。

## おわりに
今回のちょっとしたまとめになります。

:::note info
**AWS Budgets** は予算を設定、アラートによる通知、自動対応ができる
:::

:::note info
リソースのタグは、コスト配分タグとして**コスト分析や予算設定**に使用できる
:::

最後までお読みいただきありがとうございました！
>>**日頃より活用している参考書です。**
>> **「参考書」**
[**AWS認定クラウドプラクティショナー改定第3版（AWS Budgetsは377ページ）**](https://x.gd/19i6N)
>
> **「AWS公式サイト」**
[**AWS Budgets**](https://aws.amazon.com/jp/aws-cost-management/aws-budgets/)
[**コスト配分タグ**](https://aws.amazon.com/jp/blogs/news/cost-allocation-tag/)
>
#ヒーローズリーグ ＆ #MashupAwards の記念イベントで見た「srt.js」に関するメモ【ProtoPedia】

この記事は後から、「[ProtoPedia Advent Calendar 2024](https://qiita.com/advent-calendar/2024/protopedia)」の 3日目の記事としても登録しました（投稿していたのは 11月末）。

## はじめに
以下のイベント「[【20回記念イベント】 #ヒーローズリーグ & #MashupAwards を振り返る](https://mashupawards.connpass.com/event/335409/)」で見たとある作品が気になりすぎて、勢いでメモ的に記事を書いたものです。

https://x.com/youtoy/status/1861720406723555762

具体的には「srt.js」というもので、2016年、つまり 8年前に作られたものになります。

srt.js を使ってみつつ、場合によっては似た仕組みのものを作って試さねば！、と思ったのですが、まずは情報をたどって自分用に整理するための記事を書きました。

## srt.js
srt.js については、以下の作品紹介動画がすごく分かりやすいです！
具体的な仕組みや、複数の活用例を見ることができる動画です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/F1ZncGMngPg?si=_EgpGSdS4kKoMGYM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

仕組みとしては、上記の動画に出てくるとおり、↓このようになっているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ce64dd06-a0c6-f1ff-5edb-2b50845a37ae.png)


### 作品紹介のページ
以下は、作品紹介のページです。

●srt.js: YouTubeの映像と連動したマッシュアップ作品を簡単に作れるJavaScriptフレームワーク | ProtoPedia
https://protopedia.net/prototype/598

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/0cc68df5-f504-ce49-0303-222ec6a683d2.png)

#### 使用方法
使用方法は、以下の部分に書いてありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/fe78c346-4b4c-085e-fa55-7356c329159a.png)

自分の動画の場合は、上で書いていたように、字幕部分に処理を書きこむという使い方ができるようです。

それができない他の方の動画についても、処理用のファイルを別でアップロードして、動画と一緒に参照させるようにすれば

### ドキュメントと作例
詳細な情報は、ドキュメントに掲載されているようです。

以下のページがドキュメントの 1つのようですが、ここを見ると、上で掲載した動画には出てきていない複数の作例も見られるようです。

●Kazutaka Kurihara - 22. srt.js
　https://www.unryu.org/home/srtjs

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3209c03e-db71-9d39-1836-1d0fe22b669d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a4c0d2c1-2791-682b-7d1f-9208365499c1.png)

いろいろな作例のアイデアが面白いです。

### その他のドキュメントなど
また、他のドキュメントページや GitHub のリポジトリは以下となるようです。

●Kazutaka Kurihara - doc
　https://www.unryu.org/home/srtjs/doc

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d75eba31-d2a1-ae7f-e624-d4c872768635.png)

●qurihara/srt.js: JavaScript framework for YouTube contents augmentation as a standard subtitle format ".srt"
　https://github.com/qurihara/srt.js

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2c9a3a0d-db64-8654-bfae-b1b7b27cc39f.png)

#### 作例
そして、以下にも作例がまとめられているようです。

●qurihara/examples.srt.js: Examples for srt.js
　https://github.com/qurihara/examples.srt.js

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/e300558f-e2cd-2b58-cf5f-12b32aa749f1.png)

## 似た仕組みを作る話
そういえば前に思いつきで、p5.js と [YouTube関連の API]() の組み合わせで、以下のようなことをやったことがあったのですが、上記の時間連動に関する仕組みと似たことは、これに手を加えても少し作れるかも。
（特定の時間情報に合わせて、何らか処理をさせて、それを外部の API やガジェットと連動させる、というのは当時は全く思いついてなかったけど）

●p5.js で呼び出した YouTube動画の再生位置の時間を YouTube の IFrame Player API を使ってキャンバス上に表示 - Qiita
　https://qiita.com/youtoy/items/57cdb1c00934c0429602

https://x.com/youtoy/status/1817011942491586930
【Salesforce】自分の承認が済んでいないレコードが存在する場合、その一覧を毎朝メールする

こちらの記事でも話題に上げた件です。
[【Salesforce】【サブフロー】フロー開発でDRY原則を適用する](https://qiita.com/kagehikozaemon/items/cd20448a8d4ff0654878)


#### 要件
平日の朝に、自分の承認待ちで止まっているレコードがあれば、その一覧を
通知するメールを送る。1件もなければ何もしない。これを担当者ごとに実行する。

ここでいう承認待ちとは、承認プロセスではなく自前で用意したステータス項目です。

<br />

以下のように機能を分割して実装しました。
①スケジュールフロー：平日の朝に実行するようスケジュールを設定する
　　　　　　　　　　　処理の中で担当者の分だけ②を呼び出す
②サブフロー：①から渡された担当者に関して、承認待ちレコードが存在するか確認する
　　　　　　　1件以上存在した場合、③のURLを記載したメールを送信する
③レポート：自分（＝ログインユーザー）の承認待ちレコードの一覧を表示する


①②は前述の記事で説明しているので、③のレポートと数式項目について説明します。

まず、レポートに表示させるためのフラグとなる項目を作成します。

~~~Apex:未承認レポートへの表示フラグ
/* 「未承認者」項目に自分の名前が含まれているかを判定。（シス管は全件表示）*/
$User.LastName = 'システム管理者'
 || contains( unapprover__c, $User.LastName &        $User.FirstName)
 || contains( unapprover__c, $User.LastName & ' '  & $User.FirstName)
 || contains( unapprover__c, $User.LastName & '　' & $User.FirstName)
~~~

次に、まだ承認が済んでいない担当者を取得する数式項目を作成します。
~~~Apex:未承認者 unapprovers__c
/*承認が済んでいない担当者を改行区切りで表示*/
   if( !isBlank( eigyou_tantou__c )  && eigyou_tantou__c != '不要' && isBlank( eigyou_approve_date__c ) , eigyou_tantou__c & br(), '')
 & if( !isBlank( system_tantou__c )  && eigyou_tantou__c != '不要' && isBlank( system_approve_date__c ) , system_tantou__c & br(), '')
 & if( !isBlank( keiri_tantou__c  )  && keiri_tantou__c  != '不要' && isBlank( keiri_approve_date__c)   , keiri_tantou__c  & br(), '')

~~~
 
 これを条件にレポートを作成します。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336848/f2492028-c747-cec8-220d-8ec558c2a05e.png)


大した実装はしてませんが、機能要件は満たしているのでユーザー的には満足しているそうです。