OmniAICreator/Qiita-1.07M huggingface dataset:
リリース失敗時の避難訓練を開催してみて学んだこと

# はじめに
本記事は、先日初めてリリース作業を行った初心者エンジニアが書いています。

この記事を読んでいる人の中には「リリース後にバグが見つかっちゃった...でもどうやって修正したら良いの？」という経験があったり、今、実際にその状況に陥っていたり...といった方もいると思います。

10月某日、自身のエンジニアチームでは、上記のような状況に陥った場合に冷静に対処できるよう「チーム開発において、リリースに失敗した時の対処法」について「避難訓練」と称した実践形式の勉強会を行いました。

そこで本記事では「本番環境を元の正常動作に戻す」という観点に沿って、学んだことをまとめました。

# 推奨の対処法
### 1.　デプロイロールバック
本番環境を正常に動いていた直前のリリースの状態に戻し(ロールバック)、地道にfixします。

### 2.　git revert + 再デプロイ
```
git　revert　<option>　<commitのハッシュ値>
```
revertは指定したコミットを打ち消すようなコミットを新たに作成するコマンドです。
コミットログが残ります。ありがたいですね。

一方、コミットを一つずつ取り消すので、コミット数が多い時は不便です。
また、一括で複数のコミット履歴をrevertすることもできますが、チーム開発の場合は、他のメンバーのコミット履歴もrevertしてしまう危険性があります。
そのため、チーム開発においてrevertする場合は、慎重に事を運ぶ必要があるので注意しましょう。

#### 補足：マージコミットをrevertした場合
マージ履歴が残っているため、マージ元のブランチからマージ先のブランチに再マージできません。
そのため、revertして作成された打ち消しコミットを打ち消すためのrevertが必要になります。

# 非推奨の対処法
### git reset + 強制プッシュ
```
git　reset　<option>　<commitのハッシュ値>
```
resetは指定したコミットまで巻き戻してくれるコマンドです。
ただし、**revertと違ってコミットログが残りません。**

また、一度リモートに上がったコミットをresetで削除した場合、そのブランチをプッシュする際は強制プッシュをする必要があります。

masterブランチに強制プッシュをすると、他のメンバーのローカルのmasterブランチとコミット履歴が一致しなくなってしまう可能性があります。
こうなってしまうと、チーム開発に大きな支障が出ます。

そのような状況は避けたいですね。絶対にやめましょう。

#### 補足：resetの使いどころ
プッシュしていないコミットを削除する時や他の人が触らないとわかっているブランチ(個人のfeatureブランチなど)で作業する時に使う分には良さそうです。

# 結論
本番環境を元の正常動作に戻す方法はいくつかありますが、自身の開発環境に合わせて適宜使い分けると**GOOD**.

# 避難訓練を行った感想
実践形式で学べて良かったです。

一人で開発をしていると「コマンドを叩いてリセットしよ🎵」で済ませてしまうかもしれません(少なくとも私はそうです)。

しかし、チーム開発になると話は別です。
今回、チーム開発においてリリース失敗時にやってはいけないこととやったほうがいいことを知ることができたので、これから実務でしっかり活かしていきたいと思います。

チーム全体として「やってよかった」の声が上がったのも好印象です。
ぜひ一度、自身のチームで避難訓練を行ってみてはいかがでしょうか？
Accessのフォーム入力でつまずいた

### 前提
Accessを初めて触ったときのできごとです。
とある学習講座で、お膳立てされた環境を使ったのに、うまくいかなかった話。
オチは、しょーもなさすぎるので、本当に困った人だけ読んでください。

### やろうとしたこと
まず、リレーションシップは、以下の構成。
よくある、商品マスタテーブルの商品コード（主キー）を、売上データの外部キーとして使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/f63d0f60-a9fb-bbe5-cc06-56e70e2fd5ea.png)
参照整合性にもチェックを入れています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/e79c2bd7-7c83-b91f-bce1-c8200bb5f004.png)


この商品マスタテーブルに、フォームを使って新しいレコードを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/66ed21bf-7d84-d5fb-b080-8c65ad5ec266.png)

「商品入力」というフォームを作成。
商品マスタに、商品コード201で新規商品を追加しようとしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/5a90bffa-450f-95d9-1f0a-61ac2eca4515.png)


### 事象と解決策

「リレーションシップが設定されたレコードがテーブル'売上データ'にあるので、レコードの削除や変更を行うことはできません。」とエラーが出て、登録もできない、保存もできない、にっちもさっちもいかなくなって、その都度データベースオブジェクトを閉じては開き直して不毛な戦いをしていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/95e164c1-3a69-7918-6572-c62fbddd1154.png)

商品コードはかぶっていないし、売上データに商品マスタにない商品を追加したわけでもないし、おかしいなあ…と思っていたら気づきました。

左下が1/10になっているじゃないですか。
矢印を押して、11/11にしたら、入力フォームが空欄になって、ここに入力したら登録できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/342024ef-2780-1579-5911-0e634514718b.png)

無事、11件目の商品コード201が登録できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497105/a01fdfaf-16b8-71d4-d6bd-f05b1666787f.png)

商品マスタの1レコード目を、（すでに売上データに使用されているにも関わらず）変更しようとしたことによる、エラーでした。
よくよく考えれば、当たり前のことでした。恥ずかしい。
航空機が家に近づいてきたらお知らせしよう！

みなさんも普段から例えばラズパイなんかにアンテナ付けて、dump1090-faをインストールして航空機のADS-B受信してFlightrader24に情報提供してBusinessアカウントもらってオンラインで航空機観察を楽しんでいると思います。

しかしせっかく自宅で受信しているのだから、Flightrader24やFlightAwareに提供するだけでなく、自分専用に楽しめることがあるといいですね！

そこで私は航空機が自宅に近づいてきたら Google Home に通知してくれる仕組みを作ってみました。
例えばラズパイにdump1090-faをインストールした状態だと /run/dump1090-fa/aircraft.json に最新の受信データが入っています。
試しに中身を覗いてみるとjson形式でデータが入っています。

```
# cat /run/dump1090-fa/aircraft.json
```

こんな感じで入っていると思います。

```sh
{ "now" : 1697609623.8,
  "messages" : 2843412,
  "aircraft" : [
    {"hex":"87c823","alt_baro":3725,"squawk":"3440","mlat":[],"tisb":[],"messages":52,"seen":0.3,"rssi":-26.8},
    {"hex":"8678e2","flight":"ANA69   ","alt_baro":2150,"alt_geom":2400,"gs":209.5,"tas":206,"track":47.3,"roll":1.1,"geom_rate":1152,"squawk":"2367","category":"A0","nav_qnh":1020.0,"nav_altitude_mcp":15008,"lat":35.610833,"lon":139.795532,"nic":8,"rc":186,"seen_pos":0.7,"version":0,"nac_p":8,"nac_v":2,"sil":2,"sil_type":"unknown","mlat":[],"tisb":[],"messages":974,"seen":0.2,"rssi":-11.2},
```

aircraft要素の配列に航空機一機ごとの情報があるのでこの中から使えそうな情報を使います。
例えば私がやってみたのは、
・日本の政府専用機(2機ある)を捉えたら通知する
・自宅の10km以内に接近し、高度10000ft以下の航空機を通知する
というものです。政府専用機はFlightrader24では非表示なので重宝するはず。
高度10000ft以下に絞ったのは、低空の航空機なら目視しやすいことと、高度10000ft以上は大抵ANAやらJALやらの定期便でレア感はないし、いちいち補足して通知するほどではないと思ったからです。

以下、サンプルコードです。(xxxxxxのところは自分で調べて埋めてください。わかる人はすぐわかるはず。)
googlehome.py はGoogle Homeに音声ファイルを渡すとそれを再生してくれるスクリプトです。
機会があれば別途解説記事を作成しますが、ここをFCMなり任意の通知処理に変えれば良いです。

```python
#!/usr/bin/env python3

import json
import subprocess

json_open = open('/run/dump1090-fa/aircraft.json', 'r')
json_load = json.load(json_open)

for v in json_load['aircraft']:
    if('hex' in v):
        if(v['hex'] == 'xxxxxx' or v['hex'] == 'xxxxxx'):
            arg = '政府専用機が近くにいるよ'
            subprocess.call(f'/home/pi/googlehome.py {arg}'.split())
    if('lat' in v):
        if(v['lat'] > 34.xxxxxxx and v['lat'] < 36.xxxxxxx):
            if('lon' in v):
                if(v['lon'] > 138.xxxxxxx and v['lon'] < 140.xxxxxxx):
                    if('alt_baro' in v):
                        if(v['alt_baro'] < 10000):
                            if('flight' in v):
                                print(v['flight'])
                                flight = v['flight'].rstrip()
                                arg = f'{flight}が接近しているよ'
                                subprocess.call(f'/home/pi/googlehome.py {arg}'.split())
```

あとはこれをcronで数分置きに呼び出すことで監視が可能になります。
cronの設定については詳細触れませんが、crontab -e で以下のようにやれば1分置きにスクリプトを呼び出します。

```sh
# m h  dom mon dow   command
*/1 * * * * /home/pi/aircraft.py
```
Next.jsでuseRouter()がnullになってしまう話。

# 目的
useRouter()がnull　になってしまうエラーを解決する。
（バージョンはNext.jsの13以降のようです。）

# エラー内容
~~~
TypeError: Cannot read properties of null (reading 'useContext')
~~~


# 解決方法
どうやらuseRouterをインポートする際にnext/routerとするのは古い表記のようです。
正解は以下の通り。
~~~typescript
import {useRouter} from 'next/navigation';
~~~

# 追加の問題。
解決したのはいいものの、自分のしたかったrouter.queryは無くなってしまったようで、別のものに置き換えられておりました。

~~~typescript
import { useSearchParams } from 'next/navigation';

const searchParams = useSearchParams();
~~~

これにてデータを取得できました。

# 参考サイト

https://github.com/vercel/next.js/issues/41811
https://nextjs.org/docs/app/api-reference/functions/use-search-params
dockerfile-rails で楽をしよう！ 

# はじめに

※この文章はベータ版です。

Rails7.1.0 から プロジェクトの新規作成時に
Dockerfileの雛形を作成してくれるように
なりましたが、残念ながら、既存のプロジェクトから
Dockerfileを作成する機能は Rails本体
には含まれていません。

既存のRailsプロジェクトから Dockerfile を
作成してくれる機能があれば、便利ですよね？

# dockerfile-rails

https://github.com/fly-apps/dockerfile-rails

Fly.io 発の、既存のRailsプロジェクトから Dockerfile を
作成するジェネレータを提供してくれる gem です。

既存のRailsプロジェクトの中身を反映したDockerfileを
作成してくれます。

```
bin/rails g dockerfile
```

オプションを追加すれば、Dockerfileでの YJIT の有効化や
docker-compose.yml の追加作成 etcが可能です。

作成される Dockerfile (や docker-compose.yml)を
ベースにカスタマイズすることで、Dockerfile や 
docker-compose.yml　をゼロから作成するよりも
楽に Dockerfile (やdocker-compose.yml)を
作成できます。
APIを叩いて郵便番号を検索する

## 環境
- Ubuntu 20.4 + Vagrant
- Rails 5.2
- Ruby 2.6

## はじめに

APIを叩いて郵便番号から住所を取得するコードの紹介です。郵便番号の検索は下記のサイトから取得しています。
https://zipcloud.ibsnet.co.jp/doc/api

## 内容

routes.rbにsearchメソッドへのルートを記述します。

```routes.rb
Rails.application.routes.draw do
  get 'search', to:'search#search'
end
```
net/httpクラスを利用するので定義します。

```application_controller.rb
class ApplicationController < ActionController::Base
    require 'net/http'
end
```

画面に郵便番号の検索項目と、APIで取得された住所を表示する項目を作成します。

```search.html.erb
<%= form_with url: search_path, method: :get, local: true do |f| %>
    <%= f.text_field :postal_code %>
<% end %>
<p>
    郵便番号： <%= @zipcode %>
</p>
<p>
    住　　所： <%= @addres1 %>
</p>
<p>
    メッセージ： <%= @msg %>
</p>
```

searchメソッドでAPIを叩いたときの処理を記述します。

```search_controller.rb
class SearchController < ApplicationController
    def search
        unless params[:postal_code].nil?
            para_zip  = {zipcode: params[:postal_code]}
            para_ser = URI.encode_www_form(para_zip)
            para_url = "https://zipcloud.ibsnet.co.jp/api/search?#{para_ser}"
            respon = Net::HTTP.get_response(URI.parse(para_url))
            result = JSON.parse(respon.body)
            address = ""
            zipcode = ""
            if result["results"]
                zipcode << result["results"][0]["zipcode"]
                address << result["results"][0]["address1"]
                address << result["results"][0]["address2"]
                address << result["results"][0]["address3"]
                @msg = "郵便番号が取得できました"
            else
                zipcode << params[:postal_code]
                @msg = "郵便番号の取得に失敗しました"
            end
            @zipcode = zipcode
            @addres1 = address
        end
    end
end
```

## 解説

URI.encode_www_formの引数に、URIで渡すパラメータをハッシュで渡します。HTMLのGetで渡す形式（application/x-www-form-urlencoded）に変換してくれます。つまり、{zipcode: 5230851}と渡してやると、"zipcode=5230851"の形式に変換してくれます。

Net::HTTP.get_responseは、HTTPレスポンスの結果をオブジェクトで返してくれます。オブジェクトなので、中身を見てもこのような見え方です。#Net::HTTPOK:0x00007fc544c442d8>

respon.bodyにはAPIで取得した結果がセットされます。JSON形式で返されるので、ハッシュに変換して取得しやすいようにします。

## 結果

ブラウザのURLで、192.168.33.10/searchを入力すると次の画面が表示されます。
![画面1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94029/46612949-8d15-eb19-a638-38809ded1989.png)


テキストに郵便番号を入力すると、住所を取得してくれます。
![画面2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94029/22906311-1656-3947-aee9-ce5b76d9b66d.png)

該当する郵便番号が検索されなければ何も表示されません。
![画面3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94029/cb2a68dc-87ed-0456-8e99-3688c3bc4997.png)

## 参考サイト

https://www.youtube.com/watch?v=0fFx9BrP_8g&t=896s
ChatGPTを使用してYouTube動画のスクリプトを作成する

# はじめに
近年、AI技術の進化により、コンテンツ制作の助けとなるツールが数多く登場しています。中でも、OpenAIのChatGPTは、自然言語処理の分野で高い評価を受けているツールの一つです。このブログでは、ChatGPTを使用してYouTube動画のスクリプトを作成する方法について詳しく解説します。

# ChatGPTの概要
ChatGPTは、OpenAIが開発したGPT-4ベースの会話型AIです。テキスト入力に対して、人間らしい返答を生成することができます。そのため、質問応答、文章生成、コンテンツの提案など、多岐にわたる用途で利用されています。

# ChatGPTの特徴
+ 高い自然言語処理能力: GPT-3ベースのため、高度な文章生成が可能。
+ 柔軟性: 様々なトピックや質問に対応することができる。
+ APIの提供: OpenAIはChatGPTのAPIを提供しており、開発者はこのAPIを使用して独自のアプリケーションやサービスを開発することができます。

# ChatGPTを使用するメリット:
+ 時間の節約: 手動でスクリプトを書く時間を大幅に削減できます。
+ アイデアの提供: 新しいトピックや視点を提案してくれることがあります。
+ 柔軟性: さまざまなジャンルやテーマの動画スクリプトを生成することができます。

# ChatGPTを使用したYouTube動画スクリプトの作成手順
### 1.ChatGPT APIのセットアップ
OpenAIの公式サイトからChatGPTのAPIキーを取得します。Pythonを使用してAPIにアクセスする場合、以下のようなコードを使用します。
```python
import openai

openai.api_key = 'YOUR_API_KEY'
```

### 2.質問の定義
動画のトピックや内容に基づいて、ChatGPTに質問や指示を定義します。例えば、「自然風景に関する動画スクリプトを作成してください」という質問を定義することができます。

### 3.ChatGPTに質問を投げる
定義した質問をChatGPTに投げ、返答を受け取ります。Pythonを使用する場合、以下のようなコードを使用します。
```python
response = openai.Completion.create(
  engine="davinci",
  prompt="自然風景に関する動画スクリプトを作成してください。",
  max_tokens=150
)

script = response.choices[0].text.strip()
print(script)
```

### 4.スクリプトの調整
ChatGPTから受け取った返答を基に、動画スクリプトを調整します。必要に応じて、内容の追加や修正を行います。

### 5.フィードバックの取得
完成したスクリプトを他の人にフィードバックしてもらうことで、内容の質を向上させることができます。

### ⚠️注意点
+ ChatGPTは非常に高度な文章生成能力を持っていますが、常に完璧な返答をするわけではありません。そのため、返答の内容を確認し、必要に応じて手動で調整することが重要です。
+ APIの利用にはコストがかかる場合があります。OpenAIの公式サイトで料金プランを確認してください


# ChatGPTを使用した動画スクリプト作成のヒントと応用例

### 1.質問の具体性
ChatGPTに投げる質問や指示は、できるだけ具体的にすると良い結果が得られます。例えば、「自然風景に関する情報を提供する動画スクリプトを作成してください」という質問よりも、「日本の四季に関する自然風景を紹介する5分間の動画スクリプトを作成してください」という質問の方が、具体的な返答が得られる可能性が高まります。

### 2.複数回の質問
一度の質問で完璧な返答が得られない場合、異なるアプローチや表現で質問を繰り返すことで、より良い結果を得ることができます。

### 3.テンプレートの使用
特定のフォーマットやテンプレートを基にスクリプトを生成したい場合、そのテンプレートをChatGPTに提供することで、目的に合った返答を得ることができます。

## さらなる応用例
### 1.動画のシリーズ作成
ChatGPTを使用して、特定のテーマに基づく動画のシリーズのスクリプトを作成することができます。

### 2.インタラクティブなコンテンツ
視聴者からの質問やフィードバックを元に、ChatGPTで即座にスクリプトを生成し、インタラクティブな動画を作成することができます。

### 3.多言語対応
ChatGPTは多言語に対応しているため、異なる言語での動画スクリプトを生成することも可能です。

# おわりに
ChatGPTは、YouTube動画のスクリプト作成において強力なツールとなり得ます。その高度な文章生成能力を活用することで、効率的かつクリエイティブなコンテンツ制作が可能となります。ただし、最終的な内容の品質を確保するために、手動での調整やフィードバックの取得が不可欠です。
Prusa mini+ 印刷できないエラーで、その場で簡単に対処できないケースの切り分け

# 公式サイト
https://www.prusa3d.com/

# キャリブレーション
## Prusaキーワード：ファーストレイヤー
https://help.prusa3d.com/ja/article/1%E5%B1%A4%E7%9B%AE%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%AA%E3%83%96%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3mini-mini_229122

# エラーコード12204
- そりゃびっくりするよねというエラーはこちら
- いわゆる「プリントヘッドの熱暴走」
https://help.prusa3d.com/ja/article/thermal-runaway%EF%BC%88%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%E3%83%98%E3%83%83%E3%83%89%EF%BC%89-12204-mini_154813

# 交換するのが手っ取り早いケースもある
- ホットエンドサーミスター
- ホットエンドヒーター

## 交換する前にちゃんと確認したい場合
- マルチメーターで検査
https://help.prusa3d.com/ja/article/multimeter-usage_2117

# ホットエンドサーミスターの交換
https://help.prusa3d.com/ja/guide/how-to-replace-a-hotend-thermistor-mini-mini_147001

# ホットエンドヒーターの交換
- 体感では、メーカー問わず、このケースが多いように見受けられる
https://help.prusa3d.com/ja/guide/how-to-replace-a-hotend-heater-mini-mini_155068
LXDCLIでサクッとjavaの環境を用意する

## 目的
Javaでアプリケーションを開発しなさいというお達しが来ました。しかし、僕にはjavaの開発環境がありません。今回はLXDCLIでさくっとjava環境を手に入れましょう。

## ファイルの用意
lxdfile
```dockerfileCONTAINERNAME ubuntu-java2
FROM ubuntu/22.04

RUN systemctl restart systemd-resolved
RUN echo "nameserver 8.8.8.8" >> /etc/resolv.conf
RUN dhclient


RUN export DEBIAN_FRONTEND=noninteractive
RUN timedatectl set-timezone Asia/Tokyo && hostnamectl set-hostname ubuntu-java2
RUN apt-get -y update

RUN dpkg --configure -a
RUN echo "\n\n\n" | apt-get -y install openssh-server
RUN apt-get -y install docker docker.io docker-compose nano curl openjdk-17-jdk

RUN echo -e "root:password" | chpasswd
RUN echo -e 'PermitRootLogin  yes' >> /etc/ssh/sshd_config


RUN systemctl enable ssh
RUN systemctl restart ssh
RUN systemctl start ssh


RUN echo "JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:/bin/javac::")" >  ~/.bashrc
RUN echo "export JAVA_HOME" > ~/.bashrc
RUN echo "PATH=$PATH:$JAVA_HOME/bin" > ~/.bashrc
RUN echo "export PATH" > ~/.bashrc
RUN source ~/.bashrc

RUN mkdir -p ~/tmp/hello-java
```

## 構築
```bash
$ lxdcli build lxdfile
```

ログイン
```bash
$ lxc shell ubuntu-java
```

確認
```bash
root@ubuntu-java:~# java
Usage: java [options] <mainclass> [args...]
           (to execute a class)
   or  java [options] -jar <jarfile> [args...]
           (to execute a jar file)
   or  java [options] -m <module>[/<mainclass>] [args...]
       java [options] --module <module>[/<mainclass>] [args...]
           (to execute the main class in a module)
   or  java [options] <sourcefile> [args]
           (to execute a single source-file program)

 Arguments following the main class, source file, -jar <jarfile>,
 -m or --module <module>/<mainclass> are passed as the arguments to
 main class.

```
macOS Sonomaの言語切替時のカーソルインジケータ「あ」を無効にする方法

macOS Sonomaのアップデートで追加された「新しくなったテキストカーソル。キーボードの言語を切り替えると、テキストカーソルに現在の入力言語を示すインジケータが短時間表示されます。インジケータは、CapsLockがオンになっている時など、入力に役立つ情報もお知らせします。」を無効にする方法です。かな/英字を切り替え時に表示される「あ」「A」の吹き出しです。

私はKarabiner-Elementsを使ってCommandキーの左右を英数/かなに割り当てているので、このインジケータは邪魔だと思ったからです。

https://qiita.com/emonuh/items/ba5f12ef459f3a34f83e

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155587/ee0423cf-4da9-955a-ddc0-4363c78f55f8.png)

## 対処法

ターミナルを開いて以下の2つのコマンドを入力します。

```
sudo mkdir -p /Library/Preferences/FeatureFlags/Domain

sudo /usr/libexec/PlistBuddy -c "Add 'redesigned_text_cursor:Enabled' bool false" /Library/Preferences/FeatureFlags/Domain/UIKit.plist
```

再起動すると適用されます。

## 参考

https://stackoverflow.com/questions/77248249/disable-macos-sonoma-text-insertion-point-cursor-caps-lock-indicator

https://www.apple.com/jp/macos/sonoma/pdf/macOS_All_New_Features.pdf
Eleventy(11ty)でショートコードを含むファイルをループさせる

11tyでショートコードを含むファイルをループさせようとするとエラーが発生していたのですが、`v1.0.0`で解消できたのでご紹介します。

## エラーが生じる例
Eleventyで画像を扱う時はeleventy-imgでwebpとavif形式にも変換しています。
例えば、下記のようにショートコードを設定しています。

```html:ショートコード
{% image "./src/media/images/hoge/hoge.jpg", "width", "height", "alt" %}
```


```html:出力されるHTML
<picture>
  <source type="image/avif" srcset="/media/images/hoge/hoge.avif">
  <source type="image/webp" srcset="/media/images/hoge/hoge.webp">
  <source type="image/jpeg" srcset="/media/images/hoge/hoge.jpeg">
  <img src="/media/images/hoge/hoge.jpeg" width="width" height="height" alt="alt">
</picture>
```

記事一覧ページを作成するとして、各ブロックに上記のショートコードを設定しているファイルをincludeしてループさせたいとします。
その際、ショートコードに設定する値は配列のデータから設定するとします。

```html:archive.njk
{%- for article in articlesData %}
  {% include 'parts/archive_card.njk' %}
{%- endfor %}
```

```html:parts/archive_card.njk
<div>
  <h2>{{ article['articleTitle'] }}</h2>
  <p>{{ article['articleDesc'] }}</p>
  {% image article['imgSrc'], article['imgWidth'], article['imgHeight'], article['imgAlt'] %}
</div>
```

上記で実行しようとすると、エラーが発生してコンパイルされません。
ショートコードはやめて、直接imgタグで画像を読み込む方法に切り替えていました。

## v1.0.0 で追加された {% setAsync %} で解消
22年10月リリースの`v1.0.0`で追加された`{% setAsync %}`で解消することができました。
```html:archive.njk
{%- for article in articlesData %}
  {% setAsync 'articleImg' %}
    {% image article['imgSrc'], article['imgWidth'], article['imgHeight'], article['imgAlt'] %}
  {% endsetAsync %}

  {% include 'parts/archive_card.njk' %}
{%- endfor %}
```

```html:parts/archive_card.njk
<div>
  <h2>{{ article['articleTitle'] }}</h2>
  <p>{{ article['articleDesc'] }}</p>
  {{ articleImg }}
</div>
```
上記のように`{% setAsync %}`でショートコードを登録し、
includeするファイルで`{% setAsync %}`で設定した変数を呼び出すことでエラーを解消することができました。
【備忘録】チーム開発での改修案件のコミットメッセージの書き方

【案件のタイトル】問題を対応(改修)しました。

(※「問題を解決しました。」という表現だと
　実際に解決できていなかったときに
　責められるので好ましくない。)

こちらでも2，3回ログインログアウトを繰り返して問題がなかったのは確認しております。

(※こちらでも確認して大丈夫だという信憑性高さを伝える一言を添えておく。)

ご確認お願い致します。
[AWS] CloudFormation の差分検知に関するメモ

## 差分検知

- 差分検知は、`diff last_snapshot new_snapshot` により行われる。実際のリソースの状態は関係ない。
- あるリソースをAWS consoleから削除した場合、
   - そのリソースに対する変更をテンプレートに加えた場合には、Resource Not foundとしてエラーをはく。
   - そのリソースに対する削除をテンプレートに加えた場合には、未検証。(予想: Resource Not foundとしてエラーをはきそう)
- あるリソースの設定をAWS consoleから削除した場合、
   - そのリソースに対する別の設定変更をテンプレートに加えた場合、この設定で上書きされる。
   - そのリソースに対する削除をテンプレートに加えた場合には、未検証。(予想: 普通に問題なく削除される気がする)

## そもそも同一リソースをどう追跡しているのか？

ChatGPTに聞いた。

> Each construct within the CDK is assigned a logical ID, which is a unique identifier for the resource within the context of the AWS CloudFormation stack. These logical IDs are used to track the resources created by the CDK.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/212af8b5-85d5-ecd6-2aeb-7229821fd4f2.png)


## その他

- CDKで既存のリソースをテンプレートに組み込むには、cdk importを使う。
Application LoadBalancer を作成するのに必要なIP数

# ALB におけるサブネットの制限
AWS 公式ドキュメントより
- 少なくとも 2 つのアベイラビリティーゾーンサブネットを選択する
- サブネットは異なるアベイラビリティーゾーンに属している
- ロードバランサーのアベイラビリティーゾーンサブネットごとに CIDR ブロックを最低でも /27 ビットマスクにする
- 少なくとも各サブネットにつき 8 個の空き IP アドレスを用意

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/application-load-balancers.html#availability-zones

このあたりの詳細について動作確認する

# 検証 


## サブネットの作成
ドキュメントでは /27 以上にせよと書いてあるが、 /28 で作ってみる。
![スクリーンショット 2023-10-18 11.07.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/345e8456-298b-3563-e164-271e6dd0d11a.png)
利用可能なIPv4アドレスについては、/28では16IPアドレスがアサインされるが5つはAWSで予約されるため11となっている。

## ALBの作成

対象のサブネットにひたすらALBを作成していく。
- ロードバランサのタイプ：内部
- ターゲットグループ：対象なし
![スクリーンショット 2023-10-18 11.10.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/16632e05-d232-2ce0-25ff-aef3ed5abb2e.png)

## 結果
8個目のALBを作成したところで下記のエラーメッセージが出力された。
```
Not enough IP space available in subnet-0db772ec73ec98db6. ELB requires at least 8 free IP addresses in each subnet.
```
![スクリーンショット 2023-10-18 11.12.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/2d554834-37cc-23cd-e628-d8dbe89e51d1.png)

subnet-0db772ec73ec98db6 は ap-northeast-1a である。

## 確認
### ENIの状態
ネットワークインターフェースを確認するとこんな感じ。
![スクリーンショット 2023-10-18 11.21.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/7fc394af-9a83-b335-caec-e525451f4cf1.png)
アベイラビリティゾーンをみるとわかるが、7個作ったうち４つが1a、3つが1cにENIが作成されている。これはおそらくターゲットグループにインスタンスを紐づけていないためランダムにアサインされたと考えられる。（ALB作成順とAZに関連性はなさそう）

### サブネットの状態
利用可能なIPv4アドレスが減少しており、ap-northeast-1aの個数が7となっている。
![スクリーンショット 2023-10-18 11.24.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/6a84c794-1c66-9666-148c-f5cb705d93d7.png)
エラーメッセージでもこちらのサブネットでIPが不足と出ていたので、ドキュメント通り８IPが残っている状態でないとALBは作成できない模様。

ただ本来はAZ毎にターゲットを配置するのでおそらく５ALBでエラーになったのではと推測される。
## まとめ
- CIDR ブロックは /28 でもロードバランサは作成できる
- サブネットに8IP以上残っていないとエラーになる
- ターゲットを正しく設定してからじゃないとだめかも
Liveモジュールのベストな書き方

# Liveモジュールのベストな書き方

phx.gen.liveで生成されたLiveモジュール、heexを読みベストな記述を学んだメモ。

- [Liveモジュールのベストな書き方](#liveモジュールのベストな書き方)
  - [router](#router)
  - [mount](#mount)
  - [handle\_params](#handle_params)
  - [ライブパッチイベントごとの処理](#ライブパッチイベントごとの処理)
  - [アクションごとの表示の切り替え](#アクションごとの表示の切り替え)

## router

次のような場合、<font color="chocolate">`/`</font>にアクセスした場合、PageLiveモジュールがの[mount()](#mount)が実行される

```elixir
live "/", PageLive
```

このときURLに対して実行されるLiveモジュールに対して<font color="red">アクション</font>を指定することができる。

```elixir
live "/", PageLive, :index
live "/new", PageLive, :new
```
ここでアクションを指定したことで、後述する<font color="red">アクションごとの処理</fotn>を行うことができるようになる。

## mount
<font color="blue">`mount(params, session, socket)`</font>は<font color="red">マウント時に実行</font>される。

このとき<font color="chocolate">`live "/", PageLive, :index`</font>でマウントされたなら、アクションは:indexであるため、
socket.assigns.live_actionの値は:indexになる。

```elixir
  @impl true
  def mount(_params, _session, socket) do
    # 省略
  end
```

## handle_params

LiveViewでページ遷移を行う<font color="chocolate">`<.link navigate={ ~p"..." }>`</fotn>と<font color="chocolate">`<.link patch={ ~p"..." }>`</font>では、次のように動作が異なる。

* <font color="gray">`navigate`</font> - 新しいライブページに飛ぶ(再マウントを行う)
* <font color="gray">`patch`</font> - 現在のライブビューに**ライブパッチイベント**を送信する。

このライブパッチイベントが発生すると、**ページを再マウントせず**、参照されたURLのアクションが呼び出される。
実行されたアクションに合わせて<font color="red">現在のsocket.assigns(ページの状態)を**差分更新**</font>が行われる。

つまり、<font color="blue">`handle_params(params, uri, socket)`</font>はライブパッチイベントがあるたびに呼び出される。
また**mount()の実行後**にも呼び出される。

```elixir
  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end
```

## ライブパッチイベントごとの処理

phx.gen.liveを実行するとLiveモジュールに<font color="blue">`apply_action(socket, :アクション, params)`</font>という関数が記述されている。
apply_action()の呼び出しでは、2目の引数に**アクション**を渡している。

呼びされるapply_action()は**関数のパターンマッチ**によってアクションごとに、socketに追加されるデータが異なっている。

```elixir
  defp apply_action(socket, :edit, %{"id" => id}) do
    socket
    |> assign(省略)
    |> assign(省略)
  end

  defp apply_action(socket, :new, _params) do
    socket
    |> assign(省略)
    |> assign(省略)
  end

  defp apply_action(socket, :index, _params) do
    socket
    |> assign(省略)
    |> assign(省略)
  end
```

したがって同じLiveモジュールでも**アクションごとに**処理を変えることができる。

## アクションごとの表示の切り替え

LiveViewでは<font color="red">**:if**という機能を使用して</font>、条件によって表示を切り替えることができる。

基本的にアクションごとに表示を切り替えたい場合、heexに以下のように記述する。

<font color="green">要素の塊を切り替える</font>
```heex
<%= if @live_action == :index do %> ... <% end %>
<%= if @live_action in [:index, :new] d0 %> ... <% end %>
```

<font color="green">要素を切り替える</font>
```heex
<.button :if={ @live_action in [:index] }>Button</.button>
<div :if={ @live_action in [:new] }>...</div>
```
rex0220 計算式プラグイン テーブル内集計消費税計算

kintone のテーブルで、小計に税抜金額、消費税、税込金額を計算してみます。
計算式プラグイン、項目検証プラグイン、項目書式プラグイン、ちょっと便利なプラグインを使います。

# 概要
税抜金額、消費税、税込金額を計算し、小計行でセット毎の小計を表示します。
テーブルに、小計の計算用項目を追加して、非表示にします。
※テーブル行数が多くなると、計算処理に時間がかかります。

- 詳細画面
![2023-10-18_10h47_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/f07af344-04aa-3afa-7018-27fd2afb4add.png)

- 編集画面
![2023-10-18_10h56_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/dbbfe5df-41d7-7c72-9082-dfea482daf8a.png)

- 小計の計算用項目
![2023-10-18_10h46_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/c77eb7c1-9e5d-db51-79a3-5755da448631.png)

# 操作例

![2023-10-18_10h50_43.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/a2949362-f577-c64a-cb2e-7fb918eaeaf9.gif)

# 計算式プラグインの設定

「循環参照を許可」、「非同期計算」をチェックします。

![2023-10-18_11h05_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/a75c7319-d600-07a2-b1dd-24e8fd6b8831.png)

```.js
OPTION: 循環参照を許可, 非同期計算

// 税抜金額合計 NUMBER
SUMIF(分類="小計",税抜金額)

// 消費税10%合計:消費税10合計 NUMBER
SUMIF(分類="小計",消費税10)

// 消費税8%合計:消費税8合計 NUMBER
SUMIF(分類="小計",消費税8)

// 税込金額合計 NUMBER
SUMIF(分類="小計",税込金額)

// テーブル.No NUMBER
ROWNO(テーブル)+1

// テーブル.商品 SINGLE_LINE_TEXT (editable)
IF(分類="小計",SUBTABLE(セット名,ROWNO(テーブル)-1),
  商品
)

// テーブル.数量 NUMBER (editable)
IF(分類="明細",数量)

// テーブル.単価 NUMBER (editable)
IF(分類="明細",単価)

// テーブル.税抜金額 NUMBER
SWITCH(分類,
  "明細",単価*数量,
  "小計",SUBTABLE(税抜金額累計,ROWNO(テーブル)-1),
  ""
)

// テーブル.税区分 DROP_DOWN (editable)
IF(分類="明細",税区分)

// テーブル.消費税10%:消費税10 NUMBER
IFS(AND(税区分="10",分類="明細"),
  ROUNDDOWN(単価*数量*税区分/100),
  分類="小計",SUBTABLE(消費税10累計,ROWNO(テーブル)-1),
)

// テーブル.消費税8%:消費税8 NUMBER
IFS(AND(税区分="8",分類="明細"),
  ROUNDDOWN(単価*数量*税区分/100),
  分類="小計",SUBTABLE(消費税8累計,ROWNO(テーブル)-1),
)

// テーブル.税込金額 NUMBER
IF(分類!="概要",TR_SUM(税抜金額,消費税10,消費税8))

// テーブル.セット名 SINGLE_LINE_TEXT (hide)
IF(分類="概要",商品,SUBTABLE(セット名,ROWNO(テーブル)-1))

// テーブル.税抜金額累計 NUMBER (hide)
IF(分類!="概要",
  TR_SUM(税抜金額,SUBTABLE(税抜金額累計,ROWNO(テーブル)-1))
)

// テーブル.消費税10%累計:消費税10累計 NUMBER (hide)
IF(分類!="概要",
  TR_SUM(消費税10,SUBTABLE(消費税10累計,ROWNO(テーブル)-1)),
)

// テーブル.消費税8%累計:消費税8累計 NUMBER (hide)
IF(分類!="概要",
  TR_SUM(消費税8,SUBTABLE(消費税8累計,ROWNO(テーブル)-1)),
)
```

# 項目検証プラグインの設定
分類により、入力不可を指定します。
![2023-10-18_11h07_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/51b71add-9ad1-d4ec-7ec1-b1d4f1fa1890.png)

# 項目書式プラグインの設定
小計行の背景色を設定

![2023-10-18_11h09_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5042f27a-5ee1-cc3d-b15c-a98ca601b59e.png)

# ちょっと便利なプラグインの設定
「F21 テーブル行の追加・削除ボタンを左に表示」をチェック
![2023-10-18_11h10_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/840ad345-e66b-254b-86bf-f5f01811068d.png)
python の imshow と pcolormesh を用いた2Dヒストグラム生成時の微妙な違いを整理してみる

# はじめに

python で 2D ヒストグラムを生成する方法には、大きくは、imshow と pcolormesh を使う方法があるが、微妙な違いがあって、時々忘れてしまうのでまとめておきました。

## サンプルプログラム

縦と横を間違えやすいので、ここでは x 軸方向の方が長いデータを擬似的に生成します。

頻度分布を出しても良いのですが、ここでは、z = f(x,y) の z の平均値を 2D ヒストグラムで生成するサンプルで imshow と pcolormesh の比較をしてみます。

``` python:
# 必要なライブラリをインポート
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm

# xとyの範囲を定義
xmin, xmax = -10, 10
ymin, ymax = -5, 5

# 10000のランダムなxとyのデータポイントを生成
x, y = 20 * (np.random.rand(10000) - 0.5), 10 * (np.random.rand(10000) - 0.5)

# zのデータをxとyを基にして計算
z = 100 * np.random.normal(0.5*x + 0.5*y, 0.1, 10000)**2 + 1000

NBINS = 30
x_bins = np.linspace(xmin, xmax, NBINS)
y_bins = np.linspace(ymin, ymax, NBINS)

# xとyの範囲ごとのzの平均値を計算
z_avg = np.zeros((len(x_bins)-1, len(y_bins)-1))
for i in range(len(x_bins)-1):
    for j in range(len(y_bins)-1):
        mask = (x >= x_bins[i]) & (x < x_bins[i+1]) & (y >= y_bins[j]) & (y < y_bins[j+1])
        z_avg[i, j] = np.mean(z[mask])

# imshowを使ってヒートマップをプロット
plt.tight_layout()
plt.imshow(z_avg, cmap='viridis', origin='lower', 
           extent=[x_bins[0], x_bins[-1], y_bins[0], y_bins[-1]], norm=LogNorm())
plt.colorbar(shrink=0.4, label="a.u.")
plt.xlabel('x')
plt.ylabel('y')
plt.title('z Average Heatmap using imshow')
plt.savefig("test_imshow.png")
plt.show()

# pcolormeshを使ってヒートマップをプロット
plt.tight_layout()
mesh = plt.pcolormesh(x_bins, y_bins, z_avg.T, shading='auto', cmap='viridis', norm=LogNorm())
plt.colorbar(mesh, shrink=0.4, label="a.u.")
plt.xlabel('x')
plt.ylabel('y')
plt.title('z Average Heatmap using pcolormesh')
plt.savefig("test_pcolormesh.png")
plt.show()

```

## 結果

生成された図を見てみましょう。こちらが imshow で生成した場合です。

![test_imshow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/2693cd84-6aa0-2693-b9a4-f58a1f2a7a23.png)

これが pcolormesh を用いた場合です。

![test_pcolormesh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/294f2ead-9920-099e-0160-3cf42ba55ba6.png)

中身はどちらも同じですが、imshow の方は x : y が 2:1 の要素数であることを反映して横長になるのに対して、pcolormesh は画像サイズに従って調整していますね。


## pcolormesh の使用時の注意点:

- pcolormeshは、行と列の境界の配列を引数として取ります。これにはx_binsとy_binsが使用されます。
- pcolormeshは、Z値（ここではz_avg）を行ごとに読み取るため、z_avgを転置する必要があります。これは、`z_avg.T` で行います。

``` python:
mesh = plt.pcolormesh(x_bins, y_bins, z_avg.T, shading='auto', cmap='viridis', norm=LogNorm())
``` 

この部分で、`z_avg.T` の`.T` を忘れないようにしましょう。

## その他

``` python:
from matplotlib.colors import LogNorm
```

を用いて、 `norm=LogNorm()` というオプションを用いて z軸方向を log スケールにするのは pcolormesh も imshow も同じです。

## 関連ページ

secondary_axis の利用については下記の記事を参照ください。

https://qiita.com/yusuke_s_yusuke/items/f9601d7e5205820aabc1
プライベートなリポジトリのGithub Actionsを他リポジトリで共有して運用する

## はじめに

以前、プライベートなリポジトリのGithub Actionsを他リポジトリで共有して利用する方法を調べましたが、実際に私のチームに適用したのでどのような構成や運用としたか備忘録も兼ねて記事にします。

https://qiita.com/yusuke-sasaki/items/6e2834d24af527b201d3

Github公式のブログの情報やプライベートなリポジトリで共有可能とする設定は以前の記事参照。

## 利用・運用方法

### アクションの利用

- 以下のドキュメントにもありますが、アクションのymlファイルは制約として`action.yml` or `action.yaml` というファイル名にする必要があります。
  - [アクションの場所を選択する](https://docs.github.com/ja/actions/creating-actions/about-custom-actions#choosing-a-location-for-your-action)
- 公式ドキュメントでは、 `./.github/actions/ディレクトリ`に 各アクションのディレクトリを用意して配置する方法が紹介されています。
  - 例えば、`./github/actions/{Action名}/action.yml` といったかたちです。

アクションの定義
```yml
# ./github/actions/testaction/action.ymlとして配置しています
name: test action

runs:
  using: composite
  steps:
    - name: echo
      id: echo
      shell: bash
      run: |
        echo "sample"
```

利用箇所
```yml
# アクションが定義されているリポジトリとは別のリポジトリです
name: use action

on:
  workflow_dispatch:
  
jobs:
  call_private_action:
    runs-on: ubuntu-latest
    steps:
     - uses: UserABC/RepositoryABC/.github/actions/testaction@main
```

### ワークフローの利用

- actisonの共有はただusesで使うだけで問題ないのですがworkflowsの共有は以下の注意点があります。
  - 共有元となるワークフローのon条件には、`workflow_call`とし再利用可能ワークフローとして定義すること
  - 呼び出し側では、ワークフローはStepのタスクとしてではなくてjobsの直下で呼び出さないといけない

具体的には以下のように書かないとアクションがエラーとなってしまうため注意が必要です。

ワークフローの定義
```yml
on:
  # inputs と secrets を使用して、ワークフローへの入力やシークレットも定義できます。
  workflow_call:
  
name: test workflow
jobs:
  test_job:
    name: test workflow
    runs-on: ubuntu-latest
    steps:
      - name: echo
        id: echo
        shell: bash
        run: |
          echo "sample"
```

利用箇所
```yml
# ワークフローが定義されているリポジトリとは別のリポジトリです
name: use workflow

on:
  workflow_dispatch:
  
jobs:
  call_private_workflow:
    uses: UserABC/RepositoryABC/.github/workflows/testworkflow.yml@main
```


## リポジトリへの格納について


リポジトリには、共有して利用できるアクションあるいはワークフローを.github/workflows以下に以下の構造で配置して運用します。  
アクションやワークフローの利用で説明したように制約や条件を満たすように作成してください。

```
- .github
  - actions
    - {アクションの名称A}
      - action.yml
    - {アクションの名称B}
      - action.yml
    - ・・・
  - workflows
    - {ワークフローの名称A}.yml
    - {ワークフローの名称B}.yml
    - ・・・
```
先輩エンジニア「crontabには気をつけな... あいつは簡単に吹き飛ぶぞ...」

---

先日、本番サーバーで定期実行しているタスクのタイミングを調整したくて
```
crontab -e
```
と叩いていたら、先輩エンジニアから
```
「crontabには気をつけな... あいつは簡単に吹き飛ぶぞ...」
```
と意味深な忠告をうけた。

なぜ設定を確認しようとしただけで吹き飛ばす危険性があるのか?
不思議に思った自分はその言葉の理由を探すため、アマゾンの奥地へと向かった...


調べたら結構有名な罠らしく、もうたくさん記事も出ていたけど備忘録も兼ねて書いときます。


## crontabってなんぞや
わかったつもりになってるかもしれないから、改めてcrontabについて調べておこう。
IT用語辞典によると、

>crontabとは、LinuxなどのUNIX系OSで標準的に用いられる定時実行システムであるcronの設定を行うコマンドおよび設定ファイル。“cron table” の略。
利用者はcrontabファイルに実行したいコマンドやシェルスクリプトなどと実行日時を記述して保存すると、システムに常駐するデーモン（crond）がこれを読み込んで指定したスケジュールに従って実行してくれる。

とのこと。
定期実行したいタスクがある場合、大抵の場合はこれを使うことになると思う。
設定したcrontabファイルの中身は大体こんな感じ。
```
# 毎週日曜日の午前1時にユーザーのホームディレクトリのログファイルを削除
0 1 * * 0 rm /home/taioji/*.log > /dev/null 2>&1

# 月曜日から金曜日の午後11時30分にシェル・スクリプトbackup.shを実行
30 23 * * 1,2,3,4,5  /root/backup.sh
```
viエディタで「分 時 日 月 曜日 実行するコマンド」みたいに書けば勝手にやってくれるみたい。
しかもLinuxに標準搭載。お手軽！

crontabには`-e`,　`-l`,　`-r`,　`-u`などのオプションが渡せて、それぞれ意味はこんな感じ
```
-u  ユーザ名	ユーザを指定してcrontabを実行（rootユーザのみ使用可）
-l	現在の設定内容を表示
-e	設定を行う
-r	現在の設定を削除
```

`crontab -e`を実行すると上記のcrontabファイルがviエディタで開かれて、確認と編集ができる。
crontabでやりたいことは大体`-e`で出来るので、今まで正直`crontab -e`以外は使ってない。

でもviエディタで間違えて記述を削除しちゃったらUndoすればいいし、そんな簡単にcrontabファイルが吹き飛ぶことなんてないんじゃね？



## crontabが孕む危険性その１
と思って調べてるとこんな記述を発見。
>注意 –
誤ってオプションを指定しないで crontab コマンドを入力した場合は、使用しているエディタの中断文字を入力してください。この文字を入力すると、変更結果を保存せずに crontab コマンドを終了できます。この場合に変更結果を保存してファイルを終了すると、既存の crontab ファイルが空のファイルで上書きされます。

なるほどオプションなしの`crontab`がファイルの新規作成にあたるのか。

確かにviエディタ慣れてない時は、急にviエディタが開いたらパニクってとりあえず':wq'みたいなこともあったかもしれない。
crontabコマンドで何をやってるかがよくわかってないと、知らないうちに空ファイルで上書きして無事死亡する可能性もあるのかも。

でもviにパニクるような人がcrontabをローカル以外で実行することなんてそんなないよな...?


と思いつつ引き続き調べていたら、ついにそのリードエンジニアが言っていたであろう、crontabの一番ヤバいところを発見した。


## crontabが孕む危険性その2（本命）

先ほど挙げたcrontabのオプションの`-r`。
crontabファイルを消したい時は`crontab　-r`で削除できるとのこと。
試しにテスト用環境で動かそうと、キーボードで打ち込もうとした時に気付いた。

crontabで一番よく使うであろうオプションは`-e`。
`e`と`r`って....   
キー隣同士じゃねえか！！！

しかも`crontab　-r`実行時は、警告とかなく一発で消してくれるらしい。 危険すぎる。

自分はだいたい5分に一回タイポしているので、これは吹き飛ばすのも時間の問題だった... あぶねえ...


## 対策もちゃんとあった
調べたら`-r`の罠にやられた先人は数多存在するみたいで、その対策もいくつか挙げてあったからまとめる。

### 間違えて`crontab　-r`しても警告してくれるようにする
Linuxコマンドでオプション`-i`をつけると、何か削除が伴うコマンドを実行するときに確認してくれるようになる。
なのでbashのaliasを設定してcrontab実行時に勝手に`-i`をつけて貰えば、間違えて`crontab　-r`しても警告を挟んでくれるようになる。
localとかでbashrcをいじれる状況ならこれで良さそう。
```
$ vi ~/.bashrc
alias crontab='crontab -i'
$ source ~/.bashrc
```


### そもそもcrontabを使うのをやめる
crontabは導入も楽だしわかりやすいからつい使いがちだけど、冗長化とか負荷分散とかに対応ができなかったり、rの罠が危険すぎたり色々と問題が目立つそう。
個人開発ならまだしも、ちゃんとしたサービスを動かそうとなったときにはcrontabの採用自体を考えた方が良さそう。
調べたら結構代替手段はたくさんあった。

https://qiita.com/shrkw/items/5c3d53358b0016a09504


他にも`crontab　-e`を使わない運用にしたり、権限をキツくしたりといった対策があったけど、今回の自分のケースではできなさそうだったので割愛。



## やらかす前にできる手はちゃんと打っておこう
万一crontabファイルを吹っ飛ばした場合は、ログを遡って頑張ってサルベージする必要があるみたい。
動いてるサービスでこれやったら終わるな....　ｿﾞｿﾞｿﾞ

今はバックエンドをLaravelで書いているので、今後はLaravelのタスクスケジュールで既存のcrontabを置き換えていこうと思います。

先輩エンジニアから忠告を聞いていなかったら、100%どこかでやらかしてた自信があるので、本当に助かりました。
危うく腹を切るところでした。




## 参考記事
[crontab -e は「絶対に」使ってはいけない](https://ozuma.hatenablog.jp/entry/20120711/1342014448)

## 関連資料
https://blog.cles.jp/item/3303
RGB24からRGB16に変更

小型のLCDに画像を表示するためにcairoのメモリバッファに書いて、それをSPIでLCDに転送しています。

LCDに転送する前にRGB24からRBG16に変換していました。

```c
    for (x = 0; x < data->width * 4; ++x) {
#if BYTE_ORDER == BIG_ENDIAN
      color = (*(framedata + 1) >> 3) << 11;
      color |= (*(framedata + 2) >> 2) << 5;
      color |= *(framedata + 3) >> 3;
#else
      color = (*(framedata + 2) >> 3) << 11;
      color |= (*(framedata + 1) >> 2) << 5;
      color |= *(framedata + 0) >> 3;
#endif
      framedata += 4;
      
      txbuf[i] = color >> 8;
      ++i;
      txbuf[i] = color & 0xff;
      ++i;
```

こういう処理ってSIMDで出来るのかもしれませんが、MIPS DSPでコピペできるような適当なコードが見つかりません。またAtherosの24KにはDSPがありません。

はたと考えました。そもそもcairoのsurfaceをRGB16で作れば変換しなくてもいいはずです。

cairo_image_surface_create()のパラメータをCAIRO_FORMAT_RGB24からCAIRO_FORMAT_RGB16_565に変換して、コピーのコードを以下のようにしてみました。

```c
#if BYTE_ORDER == BIG_ENDIAN
      txbuf[i] = *framedata++;
      txbuf[i + 1] = *framedata++;
#else
      txbuf[i + 1] = *framedata++;
      txbuf[i] = *framedata++;
#endif
      i += 2;
```

SPIの転送がボトルネックで劇的には早くなりません。
ストアドにてクエリの結果を変数に格納する

ストアドにて、クエリの結果を変数に格納する方法を記載します。

		-- 出力する変数宣言
		DECLARE @GET_RESULT NVARCHAR(50) = ''
		-- クエリ結果の変数宣言
		DECLARE @PARAM NVARCHAR(50) = '@VAL NVARCHAR(50) OUTPUT'
		-- クエリ
		SET @SQL_CMD = 'SELECT @VAL = COUNT(*) FROM M_TABLE'
		-- SQL実行
		EXECUTE sp_executesql @SQL_CMD, @PARAM, @VAL = @GET_RESULT OUTPUT
`bat` コマンドのテーマを変更する

# 使えるテーマ一覧を確認する
```zsh
bat --list-themes
```

# Monokai Extendedを設定する場合
```zsh
# 設定ファイルのディレクトリ作成
mkdir -p $(dirname $(bat --config-file))

# 設定ファイルにテーマの指定オプションを書き込む
echo '--theme="Monokai Extended"' >> $(bat --config-file)
```
Python 時系列データを「年度ごと」に集計する

# 何がしたいのか？

時系列データがあって、これを「年ごと」ではなく「年度ごと」に集計したい。

例えば下記のような「請求情報」があって、年度ごとに合計したい。

```py
import pandas as pd

# サンプルデータ
data = {
    '請求月': ['2020-02-01', '2020-05-01', '2021-02-01', '2021-04-01'],
    '請求ID': [1, 2, 3, 4],
    '請求金額': [1000, 2000, 1500, 2500],
    '取引先名': ['AA', 'BB', 'CC', 'DD']
}
df = pd.DataFrame(data)

# 請求月を日付型に変換
df['請求月'] = pd.to_datetime(df['請求月'])

df
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/8e56261e-2461-b9d3-af9e-77aa9f549ae9.png)


# こうするといいようだ

***★コメントいただき、もっといい方法がありました！(コメント欄参照)***

```py
# 年度を計算する関数
def calc_fiscal_year(date):
    if date.month >= 4:
        return date.year
    else:
        return date.year - 1

# 年度カラムを追加
df['年度'] = df['請求月'].apply(calc_fiscal_year)

# 年度ごとの請求金額の合計を計算
result = df.groupby('年度')['請求金額'].sum().reset_index()

result
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/a10d94ab-f59c-8e01-a08d-e056d67d8418.png)


calc_fiscal_year() がポイントで、気づいちゃえば「そゆことな」て思うけど、始めは「年度の計算、面倒かも」って思ったのです。
アウトプットのハードルを下げるヒント5つ



# 目次

[1. はじめに](#1はじめに)
[2. 本記事の対象者](#2本記事の対象者)
[3. ハードルを下げるヒント5つ](#3ハードルを下げるヒント5つ)
[3-1. 投稿のレベル感への対策](#3-1投稿のレベル感への対策)
　[①気にしない](#気にしない)
　[②小さくやってみる](#小さくやってみる)
　[③詳しく書くことでカバーする](#詳しく書くことでカバーする)
[3-2. 投稿のテーマへの対策](#3-2投稿のテーマへの対策)
　[④Qiitaのイベントを活用](#qiitaのイベントを活用)
　[⑤今やっていることで書く](#今やっていることで書く)
[4. まとめ](#4まとめ)
[5. おわりに](#5おわりに)


# 1.はじめに

私はこれまで、何度かアウトプットをテーマにLTで発表をしてきました。

以前、勉強会で知り合った方から **「これを投稿して良いのかな？って思ってしまうんですよね」** と相談をいただきました。
とても共感しましたし、同じように思う方もいらっしゃるのではないかと思いました。

そこで、私個人が思う **「アウトプットのハードルを下げること」** についてまとめていきます。

:::note warn
本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありませんのでご了承ください。
:::


# 2.本記事の対象者

- アウトプットに苦手意識がある方
- アウトプットをしてみたい方

:::note
本記事でのアウトプットは **「Qiitaへの投稿」** を指します。
:::


# 3.ハードルを下げるヒント5つ

ご相談を深堀りして、 **「投稿のレベル感」「投稿のテーマ」** の2点が良いのかどうか、というところが気になるポイントなのかなと考えました。

## 3-1.投稿のレベル感への対策

### ①気にしない

いきなり身も蓋もないことを書きましたが、まず投稿をしてみることだと思います。

自分もとても気にしていましたが、「いいねが来たら嬉しい、編集リクエストが来たら素直に修正する」という感じで良いのかなと思います。

### ②小さくやってみる

投稿内容の一部は勉強会で発表して、Qiitaに投稿したものです。
※頂いたフィードバックをもとに「ブラッシュアップしてQiitaにも投稿しようかな」という感じです。

発表の際にスライドを作るので、スライドで省略したところを深堀りしたりしています。

### ③詳しく書くことでカバーする

状況や原因・対策まで書く、調べた内容とリンクも貼るなどして「1記事内で完結する」ように努めています。

## 3-2.投稿のテーマへの対策

### ④Qiitaのイベントを活用

Qiitaには「記事投稿キャンペーン」というものがあります。
※**「ホーム画面 > 公式イベント」** で確認できます。
![スクリーンショット 2023-10-18 9.39.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/a653f6c6-c781-19e9-e61a-d9f1cd90fdad.png)

また、12月にはアドベントカレンダーというイベントもあり、お題に沿って記事を投稿します。
（自分自身でカレンダーを作成することも可能です）

テーマが決まっているため、「テーマに悩む」という悩みは解消するのではないかと思います。

### ⑤今やっていることで書く

例えば、試験に向けて対策している場合は以下の記事を作成できます。
- 試験対策中：何度解いても詰まってしまうところをまとめる
- 試験合格後：学習方法、当日の試験の様子、試験を受けての気づきなど
    
投稿のヒントは身近にあるのかもしれないですね😄


# 4.まとめ

アウトプットすることの良さはズバリ、反応をいただく機会があるということだと思います。
最初の一歩を踏み出してみましょう〜！


# 5.おわりに

記事の感想や意見、ご指摘等あれば伝えていただけますと幸いです。
読んでいただき、ありがとうございました。
tet

最終的な目標：相手に行動させるようにする

テーマ：chat GPTとは何か？をわかりやすく解説する

レベル感：以下の種類の人物が存在している状況
①chatGPTという単語を聞いたことがない
②chatGPTという名前だけは知っている
③業務の中でchatGPTを使用している(調べものに利用する程度)


どういう構成で組み立てるか
・chatGPTとは何か
・chatGPTの仕組み
・chatGPTでどんなことができるか
・chatGPTを用いた基本的な例
・chatGPTを用いたちょっと発展させた例
・chatGPTを用いた応用例(プラグイン)

上で挙げた構成にするので、それぞれで書けそうなことをピックアップ

●chatGPTとは何か
OpenAIによって開発された自然言語処理モデルの一つです。
ChatGPTは、GPT（Generative Pre-trained Transformer）
というモデルアーキテクチャをベースにしており、
大量のテキストデータを学習して、テキストに基づいて質問に答えたり、
テキストを生成したりすることができます。

自然言語処理モデル（Natural Language Processing Model、略称：NLPモデル）は、
コンピュータが人間の言語を理解し、生成、分析、操作するために設計された人工知能（AI）のモデルです。
これらのモデルは、テキストデータを処理し、文章の理解、言語生成、
機械翻訳、質問応答、感情分析、文書要約、テキスト分類などのタスクを自動化するのに役立ちます。

　→著作権(悪用)の問題書いてもよさそう



●






●chatGPTの仕組み

・5兆語のテキストデータからパターンを学習し、それをもとに新しい文を作成する
・次にどの単語が来るかを確率分布に基づいて予測している。



●chatGPTでどんなことができるか(可能性)
・作業効率化
・秘書の代わり
・プラグインを使用して機能を拡張することができる
・Webブラウジング機能

・文章で表現できることならなんでも可能。
・プログラミングもできますよっと
・文書の校正などもできる
・文章の要約もできる
・APIを用いてあれこれできる(応用)

●chatGPTにさせるには向いてないこと
・最新情報の解析
・感情や意識の理解
・機密事項の入力

●注意事項
・ハル市ネーション(これは注意事項)
・オプトアウトと著作権侵害
・攻撃的・偏見を含む内容

●基本的な使い方
・会話
・タスクの指示
・翻訳
・要約
・エクセルの関数とかね
・営業ロールプレイができる

●使いこなすためのこつ
・指示を整理して詳細に伝える
・出力形式を丁寧に指定する
　→GPTに想像力はない
・回答例を与えて指示する
・段階的に指示する
・不足情報を質問させる
・プロンプトエンジニアリング
　→この単語、どこかに組み込みたい
・



●chatGPTを用いた基本的な例
・MSのBingではchatGPT→AIアシスタント


●chatGPTを用いたちょっと発展させた例


●chatGPTを用いた応用例(プラグイン)


●参考：chatGPTの使い始め方
神話レベルのエンジニア達




# 製品を作った人

## Jeff Dean

Googleのすごい人。
すごすぎて「全盛期のxx」シリーズの一角になってしまっている。

https://qiita.com/umegaya/items/ef69461d6f4967d5c623

- Jeff Deanのキーボードには２つしかキーがない。１と０だ。
    - 解説:Jeff DeanはC言語でもアセンブリ言語でもなく2進数でコードを書く。というジョーク


## 岩田聡

ゲーム開発者。任天堂の社長にもなる。

https://tomcat.2ch.sc/test/read.cgi/livejupiter/1614937386/

- 詰まりに詰まっていたMOTHER2の開発を再スタートさせて僅か1年弱で完成させる

>  「このままでは出来ないと思います。今あるものを使って完成させるなら2年かかります。
しかし、私に1から作らせてくれるというのなら、半年で完成させます。どちらにします？」

- 任天堂経営企画室長時代に大乱闘スマッシュブラザーズDXのデバックに参加

> コードを読んでは「ここがバグ」と言い当てたりと脅威的なデバック作業を見せ、予定通り大乱闘スマッシュブラザーズDXは2001年11月21日に発売。










# 発信力のある人


こういう道もある

## @Yametaro(無職 やめ太郎（本名）)

関西型言語の研究をしています。

https://qiita.com/Yametaro


https://qiita.com/Yametaro/items/a39ffcd65d77ed8b29a0






## もち猫風刺画さん(from twitter)

IT業界のあるあるネタを毎日描いているアカウント

https://x.com/MochinecoNonato?s=19

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">エンジニアのお給料が上がる方法 <a href="https://t.co/g-1fgKUdqH5">pic.twitter.com/g0fgKUdqH5</a></p>&mdash; のなと@もちねこはｻﾗﾘｰﾆｬﾝ🐈💨 (@MochinecoNonato) <a href="https://twitter.com/MochinecoNonato/status/1713479836591944073?ref_src=twsrc%5Etfw">October 15, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



## しよたろさん(from twitter)

せめて機関車らしく。

https://x.com/_siyotaro?s=19

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">エンジニアなら休日は勉強に決まってますよ、ええ <a href="https://t.co/NX7ZQXMZXS">pic.twitter.com/NX7ZQXMZXS</a></p>&mdash; しよたろ (@_siyotaro) <a href="https://twitter.com/_siyotaro/status/1558296258132922368?ref_src=twsrc%5Etfw">August 13, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## エンジニアの猫(@neco_engineer)

猫です。

https://twitter.com/neco_engineer

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">このツイートはご覧のスポンサーの提供でお送りしております。<br><br>　　　　/| ／|<br>　　　く K 」<br>　　, “　　　　`ヽ<br>　_r　︵　　　　 `、<br> ﾐ( ω　๑　=≡　　 ヽ<br> ,i&#38;‘‘　“ヾ｀ヽ ス タ ジ オ ス ベ リ<br> i’^^ﾍ　 `ヽ　 STUDIO SUBERI<br>|　　^^　　ヽ</p>&mdash; エンジニアの猫 (@neco_engineer) <a href="https://twitter.com/neco_engineer/status/1342440212924833794?ref_src=twsrc%5Etfw">December 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
[Python] typerのbool型オプションにTrue/Falseを与えて使いたい

# はじめに
よく忘れるのでメモ。

typerのオプションをbool型にすると、argparseの`action="store_true"`みたいな挙動になります（厳密には違う）が、単純にTrueまたはFalseを与えたいことがあるかもしれません。

```python:tmp.py
import typer

def main(
    input_text: str = typer.Argument(...),
    print_flag: bool = typer.Option(False)
):
    if print_flag:
        print(input_text)

if __name__ == "__main__":
    typer.run(main)
```

```
% python tmp.py test --print-flag
test

% python tmp.py test --print-flag False
Usage: tmp.py [OPTIONS] INPUT_TEXT
Try 'tmp.py --help' for help.

Error: Got unexpected extra argument (False)
```

# `is_flag=False`にするだけ
`typer.Option`で`is_flag=False`を指定するだけです。
```python
print_flag: bool = typer.Option(False, is_flag=False)
```

```
% python tmp.py test --print-flag False

% python tmp.py test --print-flag True 
test

% python tmp.py test --print-flag     
Error: Option '--print-flag' requires an argument.
```

# おわりに
argparseに別れを告げてtyperに移行しています。大変つかいやすいのでおすすめ。まだ情報が少なくて「typer オプション」と検索するとシビックTYPE-Rが出て思わず笑うなどしますが。

以下の記事がよくまとまっています。

https://qiita.com/iisaka51/items/18bde4dada0827fbe81e
B2C Commerceで扱うサイトデータとは？

# はじめに
開発で更新していくディレクトリは、cartridgeで行うが、サイト固有の設定データも存在する。それがsitedataというものだ。
cartridges以外にも設定が必要な場合があるので、その差分を適切に反映していく必要がある。

また、VSCodeのProphetではアップロードされない（アップロードされるのはCartridges）ので、Business Managerのボタンでインポートしていく必要がある。

Business Managerのボタンで設定した項目もあるはずなので、まずは、現状のサイトデータをエクスポートし、その後、手元で差分を追加して、再度インポートするという流れになる。

公式サイト

https://trailhead.salesforce.com/ja/content/learn/modules/architecture-of-commerce-cloud-digital?trail_id=develop-for-commerce-cloud

以下、公式サイトより
サイトのインポート/エクスポートは、サイト固有の設定などの情報をあるインスタンスから別のインスタンスに移動することです。

# サイトデータのエクスポート
管理 >  サイトの開発 >  サイトのインポート & エクスポート

アーカイブ名の入力。(拡張子 .zipを入力しないとエラー発生)
エクスポートするデータユニット　に全てチェック（個別にチェックを入れても良い）
エクスポート
![スクリーンショット 2023-10-18 9.25.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2674841/bc9e8512-a5e1-8ef2-34af-50b7fb114e49.png)

ステータスが成功になったら、OK.
.zipファイルをダウンロード

# サイトデータにファイル差分を反映
ダウンロードした.zipファイルを展開。
追加したい差分を反映。
その後、zip化する

# サイトデータのインポート

ファイルを選択で、作成したzipファイルを選択し、アップロード
![スクリーンショット 2023-10-18 9.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2674841/3c653c2e-157e-f292-28b1-3ad4dd4358ba.png)
アップロードされたら、対象のzipファイルにチェックを入れ、インポート

ステータスが成功になったら、OK
サイトでの動作確認

(7,056 データ エラー)と表示されるが、完了なので問題ないようだ。
![スクリーンショット 2023-10-18 9.34.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2674841/1cb76ebd-ae4b-051f-d074-d13d8109654e.png)

また、再度サイトデータをエクスポートして、差分反映されているかを確認しても良い。
AZ-104試験対策のまとめ「コマンド編」

## はじめに
Microsoft Azure Administrator(AZ-104)合格に向けた勉強でまとめた内容を本記事では投稿しています。
試験範囲の内容を私が勉強していく中で、
・忘れてしまっていた内容
・他の知識と絡めて理解した方が良い内容
・Microsoft Learnの読解に苦戦した内容
などをできるだけ分かりやすく簡潔にまとめることを意識しています。
Azureの勉強を一から始め、AZ-104取得を目指している方の参考になれば幸いです。
※本記事の内容が必ず試験に出るわけではないこと、ご注意ください

## 試験対策のまとめ
### **作成**
* `Add-AZVhd`：オンプレのマシンからVHD(火葬ハードディスク)をAzureにアップロード
* `New-AzVM`：新しいVMの作成
* `New-AzDisk`：マネージドディスクの作成
* `New-AzDataShare`：Azureデータ共有の作成

### **データ移行**
* `Azcopy`：ファイルシステムとStorage Account間、Storage Account間でデータのコピーが可能
    - `-recursive`：指定したディレクトリの内容を再帰的に(全てのサブフォルダとサブフォルダ内のファイルも)アップロードするオプション
* `Get-ChildItem`：1つ以上の指定された場所から項目/子項目を取得
* `Set-AzureStorageBlobContent`：ローカルファイルをStorage BLOBにアップロード

* 参考
    - https://learn.microsoft.com/ja-jp/azure/storage/common/storage-use-azcopy-migrate-on-premises-data?tabs=linux
    - https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7.3
    - https://learn.microsoft.com/ja-jp/powershell/module/servicemanagement/azure.storage/set-azurestorageblobcontent?view=azuresmps-4.0.0

### **Azure Kubernetes Service(AKS)**
* `Set-AzAksCluster`：自動スケーリングの最小/最大ノード値などAKSクラスターを更新/作成
* `Start-AzAksCluster`：停止したAKSクラスターを起動
* `Update-AzAksNodePool`：AKSクラスター内のノードプールを更新
* `Set-AzAksClusterCredential`：既存のAKSクラスターのサービスプリンシパルをリセット

## 次の試験対策のまとめ
* **AZ-104試験対策のまとめ「ポート番号編」**

https://qiita.com/fsd-jume/items/9dba4d104c93d0207143

## 学習内容をまとめたページの一覧

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
フラグメントのライフサイクル(テスト)

フラグメントのライフサイクル
本記事では、フラグメントのライフサイクルを紹介します。

まず、フラグメントとActivityの違いは？
フラグメントとActivityは、Viewを返すので一見すると同じように感じるかもしれません。
しかしフラグメントとActivityのライフサイクルの違いは大きく分けて2つあります。
表現の範囲と再利用性です。

表現の範囲
Activity:画面全体を表現する
フラグメント:画面内の部分的な部品を表現する
Activityは画面の基本単位なのに対して、フラグメントは部分的なUIコンポーネントです。

再利用性
Activity:異なるActivity(ホーム画面、設定画面)の切り替えに使われるため再利用性は高くない
フラグメント:異なるActivity内でUIコンポーネントとして使用出来るため再利用性は高い
フラグメントは、Activity内で様々な部品を組み合わせることが出来るため、Activityと異なり複雑なUIを効率的に実現しやすいという特徴を持っています。



フラグメントのライフサイクルは、Activity以上のコールバックメソッドを持っています。
画像

今回はその中でも、Activityに無い以下の6つのメソッドを紹介します。

onAtached()

onCreateView()

onActivityCreated()

onViewCreated()

onDestroy()

onDetach()

onAtached()
onAtached()が呼ばれるタイミングは、フラグメントがActivityにアタッチする直前です。
アタッチとは、フラグメントが特定のActivity内で使用されることを意味しています。通常フラグメントは、Activityの上に乗っており使用するにはActivityからの参照が必要です。

onAtached()の主な役割は、Activityとの参照の確立、フラグメントの初期化です。

onAtached()は、Activityとフラグメントの間の重要なコミュニケーションの開始のポイントとして機能します。
フラグメントの初期設定がこの中で行われることがあり、リソースの取得、初期化、Activityとの参照の確立など該当します。

参照の確立とは？
onAtached() は、引数にcontext(アプリケーションの情報)を持っており、contextオブジェクトを利用してActivityやその他の操作が行えます。
参照出来たらActivityやデータのアクセス、
参照出来なかったらエラー処理をおこなうなど
参照を保証することができます。

override fun onAttach(context: Context) {
    super.onAttach(context)

    if (context is MyActivity) {
        // アクティビティへの参照を確立
        val myActivity = context as MyActivity

        // アクティビティのメソッドやデータにアクセスできる
        myActivity.doSomething()
    } else {
        // キャストできない場合、エラー処理を行う
        throw ClassCastException("This fragment must be attached to MyActivity")
    }
}


onCreateView()
onCreateView()が呼ばれるタイミングは、Viewを表示する直前です。
その主な役割は、フラグメントのUIを作成することです。
onCreateView()ではインフレートを行い、UIの作成や初期化を行うのが主な役割です。

Tips:インフレートとは？
インフレートとは、XMLレイアウトファイルを解釈してUIを生成する事を意味します。
お気づきの方もいると思いますが、Activityでも
SetContentViewでUIを生成してますよね。
大きな違いは、簡単に言えば、インフレートは部分的なビューを動的に生成し、セットコンテントビューはアクティビティの主要なレイアウトを設定するのに使われます。アクティビティは通常、セットコンテントビューを使用してその画面全体のレイアウトを定義し、フラグメント内で個別のビュー要素を生成するためにインフレートが使用されます。

引数の説明

1.	resource: インフレートするXMLレイアウトファイルのリソースIDを指定します。このファイルにはUI要素のレイアウトが記述されています。
	2.	root: 生成されたビューをアタッチする親ビューを指定します。通常、この親ビューはアクティビティやフラグメントのルートビューです。ただし、この引数を null に設定することもでき、生成されたビューは親を持たない状態になります。
	3.	attachToRoot: ブール値で、通常は false に設定します。true に設定すると、root にビューが直接アタッチされます。通常は false で十分です。


onActivityCreated()
呼ばれるタイミング:ActivityのonCreate()が完了した直後
役割:Activityに関連する設定、他のフラグメントとの連携
onActivityCreated()は、関連するActivityが完全に作成され、初期化された後に呼ばれます。
そのため、このメソッド内では、Activityに関連する設定(フラグメント間へのデータの共有など)などが行うことができます。



onViewCreated()
呼ばれるタイミング:フラグメントのViewが初めて表示された時
役割:Viewの初期化
onViewCreated()は、UIが利用可能になった直後に呼ばれViewに関連する初期化はこのメソッドで行われます。
よく使われるリスナーの設定やリストビュー内のアイテムの設定などは、この中で行われます。

onDestroyView()
呼ばれるタイミング:フラグメントのViewが破棄される時
役割:Viewの解放
onDestroyView()は、フラグメントそのものではなくフラグメント内でインフレーとしたViewが破棄される時に呼ばれます。例えば、新しいトランザクションによって別のフラグメントが表示されるときに発生します。
主にViewの解放をすることが目的で、不要になったUI要素などを解放しメモリリークを防ぎます。onDestroyView()が呼ばれても、フラグメント自体は残っているため完全に削除する際は、onDestroy()メソッドが呼ばれます。

onDetach()
呼ばれるタイミング:フラグメントがActivityから切り離される時
役割:リソースの解放
onDetach()は、フラグメントとActivity間の参照を終了し、フラグメントがActivityの管理外になることを意味します。
参照を終了することで、フラグメント内で使用されていたリソースの解放や後片付けを実行するために使われたりします。特にフラグメントのリソースの管理や再利用する場合に必要になってきます。

最後に
簡単ですが、フラグメントのライフサイクル紹介でした。どのライフサイクルで何を行うかというのを意識してコーディングが重要です。