OmniAICreator/Qiita-1.07M huggingface dataset:
KMPのandroidMainの中でContextを取得する

sharedモジュールのandroidMainの中でContextを取得する方法がわからなかったため、調べてみました。

# 概要
JetpackライブラリのApp Startライブラリを使うと、アプリの起動時にコンポーネントを初期化することができるようです。  
今回はこの仕組みを使って、`applicationContext`のグローバル変数を作成します。

# コンポーネント初期化子を実装する
`Initializer<KLocationContext>`を実装したクラスを作成します。
```kotlin
package klocation

import android.content.Context
import androidx.startup.Initializer

// androidMainの中で参照するContext
internal lateinit var applicationContext: Context
    private set

public object KLocationContext

/**
 * applicationContextを初期化する
 */
public class KLocationInitializer: Initializer<KLocationContext> {
    override fun create(context: Context): KLocationContext {
        applicationContext = context.applicationContext
        return KLocationContext
    }

    // KLocationInitializerは他に依存しているクラスがないので空のリストを返す
    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}
```

# マニフェスト エントリを設定する
composeAppモジュールのandroidMainに格納されているAndroidManifest.xmlに以下を追記します。

```xml
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false">

        <meta-data android:name="klocation.KLocationInitializer"
            android:value="androidx.startup" />
    </provider>
</application>
```
`<meta-data android:name`には先ほど作成したコンポーネント初期化子の完全修飾クラス名を指定します。

これでApp StartupのInitializationProviderというコンテンツプロバイダによって、コンポーネント初期化子が呼び出されます。

# applicationContextの利用
applicationContextはグローバル変数として定義されているので、そのまま参照することができます。
```kotlin
class AndroidTextSpeech: MyTextSpeech, TextToSpeech.OnInitListener {
    private lateinit var textToSpeech: TextToSpeech
    // iOSと共通のインターフェースを持つメソッド
    override fun speak() {
        // Contextを使って、インスタンスを取得する
        textToSpeech = TextToSpeech(applicationContext, this)
    }
    // 以下省略
}
```

# まとめ
他にもContextをDIしたり、applicationContextを初期化するのではなくContextを使用するライブラリをApp Startupで初期化するなど、やり方は色々あるようです。
正解は分かりませんが、この方法が一番分かりやすくて気に入っています。

# 参考
https://stackoverflow.com/questions/66269937/get-applicationcontext-from-a-kmm-module

https://proandroiddev.com/how-to-avoid-asking-for-android-context-in-kotlin-multiplatform-libraries-api-d280a4adebd2

https://developer.android.com/topic/libraries/app-startup?hl=ja
WordPressにおける動的ルーティングの実装方法

# はじめに

WordPressで動的ルーティングを実装する方法のサンプルです。

# 問題

WordPressの修正案件。
地域の出し分けをカスタム投稿でやってほしいとのこと。
差分少ないし、記事を何十も作るのダルメシアンだったので、動的ルーティング使って解決。
ただLaravelやReactの感覚でいたから、予想の10倍は苦労した。

# 解決方法

```functions.php

function custom_rewrite_rule()
{
	add_rewrite_rule(
	  '^area/([^/]*)/([^/]*)/?',
	  'index.php?pagename=city&prefecture=$matches[1]&city=$matches[2]',
	  'top'
	);
}
add_action('init', 'custom_rewrite_rule');

function custom_query_vars($vars)
{
	$vars[] = 'prefecture';
	$vars[] = 'city';
	$vars[] = 'area_redirect';
	return $vars;
}
add_filter('query_vars', 'custom_query_vars');

function custom_template_include($template)
{
	if (get_query_var('prefecture') && get_query_var('city')) {
		$new_template = locate_template(array('city.php'));
		if (!empty($new_template)) {
			return $new_template;
		}
	}
	return $template;
}
add_filter('template_include', 'custom_template_include');

function custom_wpseo_title($title)
{
	if (!get_query_var('area_redirect')) {
		$city = get_query_var('city');
		$title = urldecode(esc_html($city));
	}
	return $title;
}
add_filter('wpseo_title', 'custom_wpseo_title');
```

# 解説

## 1
WordPressの起動時に実行。
ルーティングを行うURLをURLパラメーターに変換する。

```php
function custom_rewrite_rule(){}
add_action('init', 'custom_rewrite_rule');

```

## 2

変換したパラメーターを受け取る

```php

function custom_query_vars($vars){}
add_filter('query_vars', 'custom_query_vars');

```

## 3

URLパラメーターを持つ時にテンプレートファイルの呼び出し。
今回はcity.phpに、地域ごとに出し分ける記述を書く。
（例えば市町村ごとの事例とかお客様の声とか）

```php

function custom_template_include($template) {}
add_filter('template_include', 'custom_template_include');

```

## 4

titleタグの修正。
これを書かないと、404扱いになります。
filterのキーはSEOプラグインによって変わるので調べてください。

```php

function custom_wpseo_title($title) {}
add_filter('wpseo_title', 'custom_wpseo_title');
```

# おわりに

全体像は掴んでるけど、細かい処理までは追ってないです。
正直、なんで動くか厳密には把握してないし、あくまで参考程度にしてください。

個人的には4でドツボったので、誰かの役に立てば。
Next.jsで画面遷移を行う方法

# 概要
Next.js(バージョンは14.2.3)で画面遷移を行うのに少し詰まったのでそのような事が今後ないように備忘録として記録を残しておきます。

# やりたいこと
**今回はボタンを押したらほかのフォルダにあるtsxファイルへ遷移できるようにしたい**

フォルダ構造は以下のようになっている
```
.
└── app/
    ├── page.tsx                      
    └── AnotherPage/
        ├── page.tsx
```

最初はapp/page.tsxにいる。そのページに設置してあるボタンをクリックするとapp/AnotherPage/page.tsxへ遷移するようにしたい。

![Qiita 遷移方法の説明用.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3758326/25a3a96c-7651-3d5d-b186-b4fda49034ce.gif)

# やるべきことは2つ

## 1つ目・[useRouter()]を記述する

まずapp/page.tsxに以下のような記述を行う
```
const router = useRouter();
```
そしてapp/page.tsxのbuttonタグに以下のような記述を書く

```
<button 
  onClick={() => {
      router.push("/AnotherPage")
  }}
```

このように記述することでボタンをクリックするとアドレスバーに「(現在のURL) + (/AnotherPage)」が打ち込める。

## 2つ目・遷移を行いたいファイルの名前は必ず[page.tsx]にする
遷移を行いたいファイルの名前は必ず[page.tsx]にしなければならない。そうしないと[404エラー]が出てしまう。

### この2つを行えばできるようになったはず・・・！
mysql memo

### docker composeでデータベースを設定する
https://blogs.jp.infragistics.com/entry/docker-mysql

### 文字化け解消
https://proengineer.internous.co.jp/content/columnfeature/6653

### "unknown variable 'default-authentication-plugin"
https://sakaik.hateblo.jp/entry/20240430/mysql_8_4_0_lts_is_coming
PC で利用できるマイクなどのオーディオ入力のリストを p5.js Web Editor上で見てみる（ブラウザの MediaDevices）

過去に、以下の記事を書いた際にやった内容の、マイクバージョンというような内容です。

●p5.js で利用する内蔵／Webカメラ映像を特定のデバイス名のものに設定する（配列メソッドと MediaDevices.enumerateDevices() を利用） - Qiita
　https://qiita.com/youtoy/items/a77d193fcd50a8151f17

オーディオの入力を、JavaScript の処理で取得してみるというものです。それを p5.js Web Editor上で行ってみます。

## オーディオの入力のリストを確認する
### 既存の Webサイトで確認する
まずは、既存の Webサイトを使い、オーディオの入力のリストを確認してみます。

例えば [WebRTC samples](https://webrtc.github.io/samples/) のマイクなどが使われるサンプルで、以下などを別タブで開いてみます。

●Select audio and video sources
　https://webrtc.github.io/samples/src/content/devices/input-output/

自分の場合は、オーディオの入力ソースとしてバーチャルなものを含め、11個がリストに出てきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f3b0de2d-684b-4854-eff6-1a760fad30e2.png)

### JavaScript のプログラムで確認する
今度は JavaScript のプログラムで確認してみます。

具体的には、以下を用います。

●MediaDevices: enumerateDevices() メソッド - Web API | MDN
　https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/enumerateDevices

処理のベースは、以下となります。

```javascript
navigator.mediaDevices.enumerateDevices().then((devices) => {
  // console.log(devices);
  console.log(devices.filter((device) => device.kind === "audioinput"));
});
```

#### p5.js Web Editor上で確認してみる
上記の処理を使って、p5.js Web Editor上で確認してみます。

少し処理を足して、以下のようにしました。

```javascript:sketch.js
function setup() {
  createCanvas(400, 400);

  navigator.mediaDevices.enumerateDevices().then((devices) => {
    // console.log(devices);

    const inputList = devices.filter((device) => device.kind === "audioinput");
    console.log(inputList);

    const labels = inputList.map((device) => device.label);
    console.log(labels);
  });
}

```

デバイスの名前が出てくる部分を出力する処理も足してみました。

コンソールの出力は、以下となりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9ebc90aa-2c9f-0f72-8cc7-9303e475c549.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3d6fdeb3-dbff-716f-ca02-ca3bf76965ec.png)

事前に確認していた 11個のオーディオ入力が、同様に情報出力されるのを確認できました。

## 余談
今回の話と直接は関係ないですが、カメラ・オーディオ入力つながりという内容なので、余談として以下を紹介してみます。

●Chrome で利用可能なカメラの解像度・フレームレートやデバイス名を手軽に確認する（＋余談）：「chrome://media-internals/」 - Qiita
　https://qiita.com/youtoy/items/bfc5325d118dad11e1d7

●Chrome で PC に接続されたマイクなどの情報を手軽に確認する（ chrome://media-internals/ ） - Qiita
　https://qiita.com/youtoy/items/5a0660f9cffef58333fe

これらは、Chrome上でカメラやオーディオ入力のパラメータなどを簡単に確認できる、という話です。
【CSS】行数を指定して三点リーダーで省略する方法

以下のように記述すると行数を指定して三点リーダーで省略できます。

```css
p {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden; 
}

```

上記の場合には4行を超えると三点リーダーで省略されます。
Macでスクリーンショットを取る際にアスペクト比（縦横比）を維持する方法

現在のmacOSでは、スクリーンショットを撮る際にネイティブな方法で縦横比を固定することはできません。しかし、以下の手順でスクリーンショットツールを使いこなすことができます。

## 修飾キーの使い方
- **Shiftキー**を押しながらドラッグ：キャプチャエリアの一方の辺だけを移動する
- **Optionキー**を押しながらドラッグ：キャプチャエリアの全ての辺を中心から均等に拡大縮小する
- **スペースキー**を押しながらドラッグ：キャプチャエリアをそのまま移動する

## 追加オプション

**Shift+Command+5**を押すと、画面上にキャプチャコントロールが表示されます。これにより、キャプチャエリアのサイズを変更するためのいくつかのオプションが利用できますが、縦横比を固定するオプションは含まれていません。

## 縦横比を維持するための他の方法

ネイティブの方法ではありませんが、以下のような方法で縦横比を固定することもできます。

1. **サードパーティ製アプリ**を使用する：いくつかのスクリーンショットアプリでは、縦横比を固定するオプションが提供されています。例えば、「Snagit」や「Lightshot」などのアプリを検討してみてください。

2. **画像編集ソフト**で調整する：スクリーンショットを撮った後、PhotoshopやGIMPなどの画像編集ソフトを使って、縦横比を固定しながらトリミングすることができます。
3・楽観的強さ

楽観的感性が何より人生において大事なんだとこの数日考えている。
最近趣味にしている筋トレもしかりだ。
メンタルが私はあまり強くない。人の意見に振り回されるかと言われたらそんなことはないが、自分でマイナスな方に考えてしまうのである。

今日はprogate初級編が終わった。
明日からは中級編に入ることとする。
私は諦めない。
どんなに恥をかいても良い。醜く最後まで足掻くとする。
私は皆に背中を見せるのだから。
私はまだwebサイトを作ることができない。だが、1ヶ月後の今日や1年後の今日はこの記事を見て私は笑っていることだろう。
皆も私に着いてくれば間違いない。先を行くのは暗い道をただ走っているかのような気分になる。
だが、いつか石ころに転んだとしてもその洞窟は抜けることを皆が知っている。だが、頭でわかっていても全力疾走できる人は何人いるのだろうか。
そんな人間に私は必ずなる。

まぁ、そんな無駄なことを考えつつも私は一足先に眠るとするよ
明日も戦うために
この指南書は人のためになることを願って
pandasのpandas.DataFrame.queryの使い方

# この記事について

pandasで使うデータフレームはいろいろな方法でデータを整理することができますが、なんとなく使ってるquery()メソッドはよくみたら指定できる引数があったので確認しておきます。

# 記事の対象者
pandasのquery()について基礎的な知識をさらーっと見たい方。

<br>
pandasのデータフレームは下のものを例にして以降の説明していきます。

```python
import pandas as pd

data = {'name': ['田中', '鈴木', '佐藤', '高橋', '安藤'],
        'age': [25, 32, 28, 19, 50],
        'address': ['東京都', '大阪府', '福岡県', '北海道', '東京都']}

# データフレームを作成
df = pd.DataFrame(data)

print(df)

df
#    name  age address
# 0  田中  25  東京都
# 1  鈴木  32  大阪府
# 2  佐藤  28  福岡県
# 3  高橋  19  北海道
# 4  安藤  50  東京都

```



# query()ってなに
データフレームに対して条件文を記載することで、指定するデータを抽出するメソッドです。

## 条件式の書き方
例えば上で書いたデータに対して、"age"が30未満のデータを取得したい場合には下のように書くことができます。


```python
df.query('age < 30')

#  name	age	address
# 0	田中	25	東京都
# 2	佐藤	28	福岡県
# 3	高橋	19	北海道
```

ちなみに書き方としてはquery()を使わないで
```python
df[df['age'] < 30]
```
と書いても結果は同じです。

## 変数の参照
queryメソッド内では変数を指定することができます。
上の例でageが30未満としてましたが、borderという変数を導入してみます。

```python
# 変数を指定
border = 30

# @変数名　でqueryメソッド内で参照
df.query('age < @border')
# name	age	address
# 0	田中	25	東京都
# 2	佐藤	28	福岡県
# 3	高橋	19	北海道
```

## 文字列の指定
queryメソッド内で文字列を入れたい時にはダブルクオーテーションで囲みます。

```python
# addressが"県"の文字で終わるデータを抽出
df.query('address.str.endswith("県")')
# name	age	address
# 2	佐藤	28	福岡県
```

## 複数条件の追加
and, orで条件文を追加できます。

```python
# addressが東京都　かつ　ageが40より大きい
df.query('address == "東京都" and age > 40')
# name	age	address
# 4	安藤	50	東京都

# addressが東京都　または　ageが30より大きい
df.query('address == "東京都" or age > 30')
# name	age	address
# 0	田中	25	東京都
# 1	鈴木	32	大阪府
# 4	安藤	50	東京都
```


# よくわかってないこと。
## parser
queryで条件を指定するときキーワード引数parserで指定されていて、デフォルトだとpandasのパーサーを使うことになります。


## engine
勉強してるとengine='python'というキーワード引数が設定されているものがある。
これはデフォルトでは(インストールされていれば)numexprという値が指定されていて推奨はこちららしいです。
文字列メソッドを使う場合はpythonを指定する必要があるというのを読んだが、numexprを指定しても動いてる気がする。。

勉強中です。


# 参考文献

- [pandas.DataFrame.query](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html)
PowerShellメモ

```
# powershell -v 2 -c .\replace.ps1

$dic = @{
  '${TEST1}' = 'テスト1';
  '${test2}' = 'テスト2';
}

$content = @(
    '--テスト--',
    '--${TEST1}:${TEST2}:${test1}:${test2}--')

$vars = @()
[RegEx]::Matches($content, '\${[^}]+}') | ForEach-Object {
  if ($vars -contains $_.Value) { return }
  $vars += $_.Value
}

$content | ForEach-Object {
  Write-Host $_
}
$vars | ForEach-Object {
  $content = $content -replace [Regex]::Escape($_), $dic.$_
}
$content | ForEach-Object {
  Write-Host $_
}
```
実行結果
```
--テスト--
--${TEST1}:${TEST2}:${test1}:${test2}--
--テスト--
--テスト1:テスト2:テスト1:テスト2--
```


```
function Rewrite($key, $value, $filename, $outfilename) {
    Get-Content $filename |
    ForEach-Object { $_ -replace "^\[item $($key)\(""[^""]*""\)\]$", "[item $($key)(""$($value)"")]" } |
    Set-Content $outfilename
}

Rewrite "Key" "TEST" file1.cs file2.cs
```
Vimに入門した自分用のチートシート

## はじめに

iOSエンジニアをしている [Nao-RandD](https://x.com/Nao_RandD) です.

”Vimに入門するなら人生の前半が良い” という天啓を得て、Vimに入門しました。

そんな自分用のチートシートです。

今後も必要に応じて更新していこうと思います。

## チートシート

| コマンド | 効果 |
| --- | --- |
| c | VisualModeで選択した範囲を消してInsertModeで終わる |
| ci | 括弧の中身だけを消す(例:[ci"] "この中だけが消えます") |
| ciw | カーソルの下にある１単語を消してInsertModeに入る(英語・カタカナ・ひらがな・漢字で分けられる) |
| cit | htmlのタグに囲まれている中を消します |
| dw | カーソルから右にある単語を消す（NormalModeで終わる） |
| daw | カーソルの下にある単語を消す |
| gd  | 定義元に飛ぶ |
| fx | x にジャンプ |
| tx | x の一文字前にジャンプ |
| D | カーソルから右にある文を全て消す |
| S | カーソルのある１行を全て消してInsertModeにはいる |
| カーソル移動 |  |
| gg | ページの一番上に行く |
| G | ページの一番下に行く |
| H | 表示されている画面の一番上に行く |
| M | 表示されている画面の真ん中に行く |
| L | 表示されている画面の一番下に行く |
| { | 前の段落の先頭に移動する |
| { | 次の段落の先頭に移動する |
| ページ内検索 |  |
| /hoge | [/]のあとの文字列を検索する(Control-f的なあれ) |
| n | [/]で検索した文字列の次の検索候補に飛ぶ |
| N | [/]で検索した文字列の前の検索候補に飛ぶ |
| 文字入力の応用 |  |
| A | カーソルのある行の最後にInsertModeで飛ぶ |
| I | カーソルのある行の最初にInsertModeで飛ぶ |
| . | 直前にやったことを繰り返す |
| カーソル移動 |  |
| $ | カーソルのある行の最後にNormalModeで飛ぶ |
| ^ | カーソルのある行の最初にNormalModeで飛ぶ |
| VisualMode |  |
| v | インサートモードに入る |
| v+[移動キー] | 範囲選択ができる(1) |
| v+(1)+y | 選択した範囲をコピー |
| v+(1)+d | 選択した範囲を削除 |



## おまけ

Xcodeでは Xcode 13以降で Vim Mode が導入されており、Vimをより気軽に利用することができます。

Xcode 15では、`Editor >> Vim Mode` からすぐに有効にできます :sparkler: 

> To improve access to the Vim Mode, Xcode replaced the Enable Vim key bindings preference with an Edit > Vim Mode menu item. (75491567)

https://developer.apple.com/documentation/xcode-release-notes/xcode-13-release-notes


ターミナル、VSCode、Xcode でVimを使い倒していきます:tada:
二分探索法(バイナリサーチ)

## 二分探索法(バイナリサーチ)とは

探索する範囲を半分に絞り込みながら探索を進めるアルゴリズム。
あらかじめ昇順か降順に整列されているデータが対象となる。

### 対象データ

以下のように昇順か降順に整列されているデータ
```php
$array = [11, 13, 17, 19, 23, 29, 31];
```

### フローチャート



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733055/8a75e4d2-1df8-47f8-51be-13b0cc8d445f.png)

### 解説

以下の変数を用意する
`head`：先頭のデータの添字
`center`：真ん中のデータの添字
`tail`：末尾のデータの添字

①真ん中の要素を選ぶ
→`(head + tail) / 2`で求め、`center`へ代入する
②真ん中と目的のデータを比較する
→データが目的のデータと一致した場合は、探索終了
→データが目的のデータと一致しない場合は③へ移る
③探索の範囲を半分に絞る
→目的のデータが真ん中のデータよりも大きい場合、変数`head`に`center + 1`を代入して再び真ん中のデータを割り出す手順を繰り返す
→目的のデータが真ん中のデータよりも小さい場合、変数`tail`に`center - 1`を代入して再び真ん中のデータを割り出す手順を繰り返す

#### 目的のデータが存在しない場合

探索の範囲を半分に絞り続け、headがtailより大きくなった時点で目的のデータが存在しないみなし、処理を終了する。
アーキテクチャを突き詰めるオンラインカンファレンスへの参加

# きっかけ

* ミノ駆動さん、成瀬さん、増田さんと今までセッション聞いてきて興味ある人が登壇すること
* より変更しやすいシステム開発にするための取り組みの新しい情報が欲しかった

# どんなカンファレンスだったか
アプリケーション開発に関する開発手法のセッションがメインでした。
書籍を書いてる人たちや現場で開発をリードする人たちが
より開発スピードをあげるため、より安全に変更できるようにするために
どのようにシステムを設計するのかその観点だったり、手法を紹介してくれました。

カンファレンスに来てる人たちが関心がありそうなワードはこの辺り。
これらのワードに関心がある人はカンファレンスの資料をみてもいいかもしれない。

* DDD
* クリーンアーキテクチャ
* レガシーコード

# セッション
## 聴講したセッションと概要
* 大きな泥団子に立ち向かうためのソフトウェア設計　本格入門
https://speakerdeck.com/masuda220/big-ball-of-mud
開発が積み重なっていくと、変更が難しいシステムになっていってしまう。
そのために関心ごとを分離して、考える範囲を小さく切り出すことが必要である。
広めたいのは考える範囲を小さくするために挙げられた手法を**常識**としてとらえられる今の時代だと最低限求めたいなとは感じた
* CQRS+ESアーキテクチャ解体新書
https://speakerdeck.com/nrslib/cqrs-es-disassembly-book
スケーラブルなシステムの方式としての案
ES（イベントソーシング）とメッセージ駆動（CQRSを使ってやりたいこと）を組み合わせることによって、リソースの待ち少ない仕組みにつながる
タスクが細分化できるようになるので、クラウドサービスのスケーラビリティにもしやすくなる
口頭説明が多くて資料だけだとわからないので、youtube見た方がいいです。
https://www.youtube.com/watch?v=NhDj6Ep9SyA
* 見えないものに着目すると上手くいく、モデリングの勘所
業務をモデルに落とし込むときは目的を観点に切り分ける。
さらに踏み込んで、禁止事項などの観点を用いて、設計に活かせる観点を挙げてくれます。
https://speakerdeck.com/minodriven/invisible-driven-design

## 発表資料だけ見たセッション
共有がてら貼っておきます。

* ビジネスの構造をアーキテクチャに落とし込みソフトウェアに可変性を注入する
https://speakerdeck.com/monotaro/bizinesunogou-zao-woakitekutiyaniluo-tosiip-misohutoueanike-bian-xing-wozhu-ru-suru
* サービスを立ち上げを終えて追い求める美しいアーキテクチャ
https://speakerdeck.com/takuyatezuka/sabisuli-tishang-gewozhong-ete-zhui-iqiu-merumei-siiakitekutiya
* RASKULに見るアーキテクチャの変遷　複数サービス展開への道
https://speakerdeck.com/yusukekishino/raksulnijian-ruakitekutiyanobian-qian-fu-shu-sabisuzhan-kai-henodao
＊ ユーザフレンドリーな取引明細のアーキテクチャ　VISAカードの適用事例
https://speakerdeck.com/shoheimitani/yuzahurendorinaqu-yin-ming-xi-noakitekutiya-visakadonofu-za-xing-nixiang-kihe-ushi-jian-li

# 各セッションで得て広めたいこと
全部書きたいですけど、一部セッションだけ書きます。

## 大きな泥団子に立ち向かうためのソフトウェア設計
大きな泥団子は負債がたまっていて、開発が足をひっぱられてる状態。
足を引っ張られる分、変更は大変です。
ただ、その大変さを超えて開発し切ると無駄に仕事した感じは得られるんです。
でも、それってやりたいことするために本来不要な手間が発生してしまってるだけです。
同じような大変さを味わうのであれば、変更しやすいようにリファクタしたり設計を見直していくことに力を注ぐべきですよねってことが根底にあります。

変更しやすくするための手法として、イミュータブルを挙げてました。
スライド１ページが数秒で流れました。それくらい常識だよね。説明する必要ないよねって。
イミュータブルはJavaで言えば全てのメンバ変数に`final`がついた状態になってることを指してます。
これが今の変更容易性を求める現場では常識になってます。
他にも挙げられていますが、挙げられてる手法と自分達が開発しているシステムにギャップがあるのであれば、世の中の常識との差分です。
変更しにくいままでいいシステムは存在しないので、差分に気付き、その差を詰めることに目を向けて欲しいです。

## CQRS+ESアーキテクチャ解体新書
イベントソーシングはステートソーシングと比べて、情報が残りやすくするメリットくらいしかわかってなかった。
セッションであったようなイベント駆動、メッセージ駆動の考えにはステートソーシングだと対応できない。
申込、キャンセルとされた場合、それぞれで必要な処理は別だが、最後の状態のキャンセルしかないので、それぞれのイベントに同期して処理完了させないといけない。
イベントソーシングなら、申し込みとキャンセルがそれぞれのイベントとして永続化されているので、非同期で申込イベントに対して、申込処理ができる。
イベントソーシングには、非同期処理ができるメリットがありました。
非同期処理ができて、細かいタスクになっていくと、空いてるリソースを有効に使いやすいのと、
障害が起きた時の影響範囲が縮まるなどの他のメリットにもつながったりします。


## 見えないものに着目すると上手くいく、モデリングの勘所
モデリングに限らず使える設計の観点になると思います。
セッションの中であげてましたが、道路交通法は交通事故（異常系）を防ぐことを目的として、法律ができてます。
異常系を防ぐために守ったり、決めておいた方がいいことを法律（仕様）になっている訳です。
支払い系のシステムであれば、ユーザが不利益を被る二重請求を防ぎたいという目的があって、
シーケンシャルなチェックIDを使って重複チェックしたり、キュー処理が一度しかされない仕組みを使う設計にもつながると思います。
紹介されている観点をモデリングに限らず、データ駆動のシステムの設計をするときに、深掘りする観点として取り入れるだけでも
今まで考えてなかった観点に気づけるきっかけになるので、ぜひ試して取り入れてほしいです。
100days of code #day1

# 自分について
理系大学卒
2年目
メーカー技術職
ソフトエンジニアにあこがれて勉強中です

## 記事を書くモチベーション
pythonの学習をしたくていろいろ調べて結局Udemyで出てきたこのコースを受講することにした。
https://www.udemy.com/course/100-days-of-code/?couponCode=LEADERSALE24A
名前が100daysで飽き性の自分もなんだかコツコツ続けれそうという安直な気持ちで
やったことのアウトプットもかねてqiitaで書いてみることに。

# #1day
メインはprintとinputの使い方や構文のルールについて。
ゴールは変数とprintとinputを駆使して、バンド名を考えるプログラムを作ること
この辺は学生時代の知識もあり詰まることなくできた。

```
print("バンド名メーカーへようこそ\nあなたにピッタリなバンド名を考えるよ！")
user_pet_name = input("あなたのペットの名前はなんですか？\n")
user_city_name = input("あなたの出身地は？\n")
print("あなたのバンドの名前は" + user_city_name + user_pet_name + "です！")
```

# さいごに
day100を目指してとりあえず続けたい
Gitのresetの種類について~間違えてコミットしちゃった場合どれ使えばいいの？~

# Gitにおけるreset(リセット)とは？
コミットする前の状態までGit上には履歴を残さずに戻すということ.

:::note warn
 *revert(リバート)との違い*
 どちらもコミット前の状態に戻す時に用いられますが，revertでは，Git上に戻したことについての履歴が残ります．
:::

:::note alert
これはあくまでも間違えてしてしまった時の処理です．
出るだけ使わないように正しくcommitしましょう！
特にresetはresetしたことも履歴に残らないので注意しましょう!!
:::

# resetの種類
では，次にresetの種類について見ていきましょう．
resetには以下の3種類があります．
1. Soft
1. Mixed
1. Hard

それぞれについて詳しく説明していきます

## Soft reset
最新のコミットを保持したまま，ステージングエリア（インデックス）に変更を戻す場合に使用します．これにより，最後のコミットが取り消され，その変更がステージングエリアに戻されます．
簡単にいうと，現在のローカル上にある情報を持っておきながら，commitしたということをなかったことにします．再度commitすれば，まったく同じ状態まで戻せるというゆる〜いresetです．
addしたものの中身を変えることはできなく，追加でaddする（ステージエリアに入れる)ことができます．
```
git reset --soft HEAD^
```
## Mixed reset
最新のコミットを取り除きながら，そのコミットに含まれていた変更をワーキングディレクトリに保持します．
commitとindexの2つを戻すということです．適当にaddしてしまったものを取り除きたい時に使えます．
softとの違いはステージングエリアを消去することです．
```
git reset --mixed HEAD^
```
```
git reset
```

## Hard reset
最新のcommitを完全に取り消し，ワーキングディレクトリの変更も破棄したい場合に使用します．これにより，最後のcommitが取り消され，ワーキングディレクトリも以前のcommitの状態に戻されます．
つまり，commitしてから今までの情報を全て消し去り，commit前まで戻すという作業です．
今までの努力なんてどうでもいいから，どうしても，commit前まで戻したいという時に使う非常に強いresetです．
commit,index,file全てがなくなってしまうので，使う時は注意が必要です．
```
git reset --hard HEAD^
```

ここまで聞いた人ならそれぞれの違いについてだいぶ理解したと思います．それでも，SoftとMixedの違いはかなりややこしいと思いますので，わかりやすい例を一つ挙げておきます！

## SoftとMixedの違い例
現在のコミット履歴が以下のようになっているとします。
(HEAD -> master) Commit 4
Commit 3
Commit 2
Commit 1
### --soft:
git reset --soft HEAD~3を実行すると、以下のようになります。
HEADがCommit 1を指すように移動します。
インデックス（ステージングエリア）は変更されません。つまり、Commit 2、Commit 3、Commit 4の変更はすべてステージングされたままです。
ワーキングディレクトリも変更されません。Commit 2、Commit 3、Commit 4の変更はすべて保持されます。
```
$ git status
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file1.txt
modified: file2.txt
new file: file3.txt
modified: file4.txt
```

### --mixed
git reset --mixed HEAD~3を実行すると、以下のようになります。
HEADがCommit 1を指すように移動します。
インデックス（ステージングエリア）がHEADと同じ状態にリセットされます。つまり、Commit 2、Commit 3、Commit 4の変更はすべてステージングが解除されます。
ワーキングディレクトリは変更されません。Commit 2、Commit 3、Commit 4の変更はすべて保持されます。
```
$ git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.txt
modified: file2.txt
modified: file4.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
file3.txt
```

--softオプションは、HEADを移動しても、インデックスとワーキングディレクトリを保持します。つまり、Commit 2、Commit 3、Commit 4の変更はすべてステージングされた状態で残ります。これにより、3つのコミットを1つにまとめたり、変更を調整したりすることが容易になります。
--mixedオプションは、HEADを移動し、インデックスをHEADと同じ状態にリセットしますが、ワーキングディレクトリは保持します。つまり、Commit 2、Commit 3、Commit 4の変更はすべてステージングが解除されますが、ワーキングディレクトリには残ります。これにより、過去のコミットに戻った後、変更を選択的にステージングし、新しいコミットを作成できます。

## 資料
参考にさせてもらった

https://qiita.com/kmagai/items/6b4bfe3fddb00769aec4

resetとrevertについて詳しく

https://qiita.com/tarakokko3233/items/38de3bf197fc2a262d4b
SwiftLint 0.55でSwiftPM Commandプラグインに対応した！

## 背景

2024/2/2に [mobile.stmn #4](https://stmn.connpass.com/event/307941/) という名古屋のモバイルアプリ開発勉強会で、Swiftでのアプリ開発に必要な各種CLIツールもSwiftPMに寄せる話をしました。

https://speakerdeck.com/temoki/iospatukezimaneziyafen-dou-ji-wan-jie-bian

![SwiftLintに関するスライド](https://files.speakerdeck.com/presentations/14ba4b47c755479a9525c4a18e4ecbd6/slide_11.jpg?29053546)

しかし、このタイミングの SwiftLint [0.54](https://github.com/realm/SwiftLint/releases/tag/0.54.0) ではCommandプラグインが提供されておらず、`--fix`機能での自動修正をSwiftPM経由でできませんでした。そこで、やむなく次のようなスクリプトで運用していたのですが、それがとても心残りでした（「完結編」と言ったのに完結していない...）。

```shell
cd ${PACKAGE_DIR} && swift package resolve
cd ${PACKAGE_DIR}/.build/checkouts/SwiftLint && \
 	swift run swiftlint --config ${PACKAGE_DIR}/.swiftlint.yml --fix
```

## SwiftLint 0.55リリース :tada:
SwiftLint 0.54のリリースから約半年経過したこの2024年5月に、新バージョン [0.55](https://github.com/realm/SwiftLint/releases/tag/0.55.0) がリリースされました。そしてこのバージョンで待望のCommandプラグインが提供されました。

SwiftLintをインストールしているPackageディレクトリで以下のコマンドによりSwiftLintを実行することができます。もちろん`--fix`オプションなど各種パラメータを指定可能なので、この方法で自動修正も行えるようになります。

```shell
swift package plugin swiftlint
```

:::note warn
`--fix`オプションを使う場合は、pluginのオプションに`--allow-writing-to-package-directory`を追加してファイルへの書き込みを許可しておこう！
:::


### 注意点①：Build Toolプラグインの名前が変わった
Commandプラグイン提供に伴い、従来から提供されているBuild Toolプラグインの名前が変わったので、Package.swiftの修正が必要となります。

```diff:Package.swift
-  .plugin(name: "SwiftLintPlugin", package: "SwiftLint")
+  .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint")
```

### 注意点②：Build ToolプラグインのConfigファイルの参照方法が変わった
Build Toolプラグインにはパラメータ等を指定することができません。そこで SwiftLint 0.54 ではREADMEで以下のような説明がされており、Configファイル `.swiftlint.yml` はルートディレクトリにあるものが参照されるようになっていました。

> Due to limitations with Swift Package Manager Plug-ins this is only
recommended for projects that have a SwiftLint configuration in their root directory as
there is currently no way to pass any additional options to the SwiftLint executable

それが今回のバージョン 0.55 では、次のような説明に変わっており、ビルド方法によって参照されるConfigファイルの場所が変わっていますので注意してください。

> The build tool plugin determines the SwiftLint working directory by locating 
the topmost config file within the package/project directory. If a config file
is not found therein, the package/project directory is used as the working 
directory.

**Xcodeプロジェクトをビルドする場合**
* Xcodeプロジェクトファイルのディレクトリ配下にあるConfigファイルのうち、最上位にあるもののディレクトリがワーキングディレクトリとなり、そのConfigファイルが参照される
* つまり、基本的にはXcodeプロジェクトファイルと同じ位置に配置しておくのが良さそう

**Swiftパッケージをビルドする場合**
* パッケージディレクトリ配下にあるConfigファイルのうち、最上位にあるもののディレクトリがワーキングディレクトリとなり、そのConfigファイルが参照される
* つまり、基本的にはPackage.swiftファイルと同じ位置に配置しておくのが良さそう

## まとめ
SwiftLint 0.55でSwiftPM Commandプラグインに対応したことで、私の「iOSパッケージマネージャ奮闘記」が本当に完結しました！めでたい:tada:

https://speakerdeck.com/temoki/iospatukezimaneziyafen-dou-ji-wan-jie-bian
Amazon Prime Videoの新規エピソード更新通知を行おうとして挫折した話

# 概要

Prime VideoのHTMLを取得して解析し、新規エピソード更新通知を送ろうとしたらCAPTCHAに阻まれました。
もし今後同じようなことをされる方がいらっしゃったときのために、備忘録として残しておきます。また、解決策があればコメント等でご教授いただけますと幸いです。

## 実装しようとした背景

ご存じの方もいらっしゃるかと思いますが、Amazon Prime Videoにはエピソード更新通知機能が無く、私は常々これを不便に感じています。
そこで、指定したコンテンツが更新された際に通知が来るようなシステムを実装しようと考えました。

## 実装しようとしたこと

私は、以下のサイトを参考に更新通知を実装しようとしました。

https://zenn.dev/gdtypk/articles/amazon-prime-video-notify-line

フローは至ってシンプルで、

1. Googleスプレッドシートに通知させたいコンテンツの情報を入力する
1. GAS (Google Apps Script) から定期スクレイピングし、エピソード数がシートの記録より多ければLINEを通じて通知する

というものです。

## 発生した問題

HTMLを取得するために`UrlFetchApp.fetch(URL)`を行ってHTMLを取得したところ、本来意図したような「エピソード数を含むHTML」ではなく、CAPTCHAページが返ってきており、プログラムを通じた自動アクセスができないようになっていました。
そこでPrime Video側にエピソード数を取得できるようなAPIが用意されていないか調べてみたのですが、どうにも見当たらず、エピソード更新通知機能の実装を断念しました。
思えば、似たようなことを実装していないか検索した際にヒットしたサイトはどれも古い記事でしたから、その時からWebページ側の実装が変わってしまったのかもしれません。

## 後書き

以上、私がPrime Video更新通知を実装しようと試みてから断念するまでの流れです。
これから似たようなものを作ろうとしている方にとって、少しでも有用な情報源になれていたら幸いです。
もしかしたら、使えるAPIを見逃していたり、現在のWebページにも対応した方法があったりするかもしれませんが、その際はコメントで教えていただけると助かります(私が見つけた場合も追記する予定です)。

2024/06/27追記
Prime Video APIの現状について書いてある記事を見つけました。
やはりAPIは公開されていないようです...

https://data.reelgood.com/prime-video-api-for-developers/
最近知ったJS Tips

## オブジェクトのプロパティを操作する際エラーを検知する方法
オブジェクトのプロパティに対して値を設定更新する際、仮にエラーが発生した場合に今までであれば以下のような形式でハンドリングを行っていました。

```js
const user = {};

try {
    user.name = '太郎';
    console.log('Success!');
} catch (e) {
    console.error('Failed...');
}

console.log(user.name); // 太郎
delete user.name;
console.log(user.name); // undefined
```

上記でも十分わかりやすいですが、わざわざtry-catchを記述しなくても[Reflect](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Reflect)を利用すればプロパティの変更や削除に成功したかどうかはブール値として取得することが可能です。

```js
const user = {};

if (Reflect.set(user, 'name', '太郎')) {
    console.log('Success!');
} else {
    console.error('Failed...');
}

console.log(Reflect.get(user, 'name')); // 太郎

if (Reflect.deleteProperty(user, 'name')) {
    console.log('Success！');
} else {
    console.error('Failed...');
}
```

## オブジェクトのプロパティ操作をカスタマイズする方法
オブジェクトのプロパティに対して何らかの操作を実施する際、付随して何か特定の処理を実行する場合は以下のように記述するかと思います。

```js: オブジェクトからプロパティを参照・変更するたびにログを出力する処理
const user = {
    name: 'Alice',
    age: 25
};

const name = user.name;
console.log(`user.nameが読み込まれました`);

user.age = 26;
console.log(`user.ageが更新されました`);
```

上記のようにオブジェクトのプロパティが読み込まれたり、参照されるたびにログを記述する必要があるのですが、[Reflect](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Reflect)と[Proxy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)を利用することで以下のように記述することができます。

```js
const user = {
    name: 'Alice',
    age: 25
};

const handler = {
    get(target, prop) {
        console.log(`user.${prop}が読み込まれました`);
        return Reflect.get(target, prop);
    },
    set(target, prop, value) {
        console.log(`user.${prop}が更新されました`);
        return Reflect.set(target, prop, value);
    }
};

const proxy = new Proxy(user, handler);

const name = proxy.name; // 「user.${name}が読み込まれました」 と出力される
proxy.age = 26; // 「user.${age}が更新されました」 と出力される
```

上記の機能を利用することでバリデーションの実装なども可能になります。

```js
const user = {
    name: 'Alice',
    age: 25
};

const handler = {
    set(target, prop, value) {
        if (prop === 'age' && typeof value !== 'number') {
            throw new TypeError('数字で入力してね！');
        }
        console.log(`${prop} を ${value} に変更したよ`);
        return Reflect.set(target, prop, value);
    }
};

const proxy = new Proxy(user, handler);

try {
    proxy.age = 'twenty-six'; // 「TypeError: 数字で入力してね！」がスローされる
} catch (e) {
    console.error(e.message);
}

proxy.age = 26; // 「age を 26 に変更したよ」と出力される
console.log(proxy.age); // 26
```
【Rails】newメソッドとcreateメソッドの違い

## new メソッド
- オブジェクトを生成しますが、それをデータベースに保存しません。
- オブジェクトを保存するためには、明示的に save メソッドを呼び出す必要があります。


## create メソッド
- オブジェクトを生成し、すぐにデータベースに保存します。
- 内部で new と save の両方を行います。

<br><br>

### create アクションのnewメソッド
以下のコードは new メソッドを使用しているため、save メソッドを呼び出す必要があります。
```ruby:Ruby
def create
    @question = Question.new(question_params)
    if @question.save
      redirect_to @question
    else
      render 'new', status: :unprocessable_entity
    end
  end
```

<br>

### Answer コントローラの create アクション
一方、Answer の登録では create メソッドを使用しているため、save メソッドを明示的に呼び出す必要がありません。
```ruby:Ruby
def create
    @question = Question.find(params[:question_id])
    @answer = @question.answers.create(answer_params)
    
    if @answer.persisted?
      redirect_to question_path(@question), notice: '回答が正常に作成されました。'
    else
      render '表示画面', status: :unprocessable_entity
    end
  end
```
`persisted?`
説明：保存済みかチェック
ドキュメント：https://railsdoc.com/page/model_persisted_q

<br><br>

## create アクションと create メソッド
createアクションとcreateメソッドは別物と考える
### create アクション
- Rails コントローラ内のアクションの一つで、通常は新しいレコードをデータベースに保存するために使用されます。<br>

- フォームから送信されたデータを受け取り、新しいオブジェクトを作成し、それをデータベースに保存する処理を行います。<br>
- アクションは、ブラウザからのリクエストを処理し、ビューをレンダリングしたりリダイレクトしたりする役割を持ちます。


### create メソッド
- ActiveRecord のメソッドで、新しいオブジェクトを作成し、データベースに保存するために使用されます。
- 内部で `new` と `save` を組み合わせた処理を行います。すなわち、新しいオブジェクトを生成し、それをすぐにデータベースに保存します。
- モデルに関連付けられたメソッドとして、簡潔にオブジェクトを作成および保存するために使用されます。

<br><br>

## まとめ
- `new` メソッドを使用した場合は、生成したオブジェクトを保存するために `save` メソッドを呼び出す必要があります。
- `create` メソッドを使用した場合は、オブジェクトの生成と保存を同時に行うため、`save` メソッドを明示的に呼び出す必要はありません。
WordPressでJavaScriptを埋め込む方法（再掲）

## はじめに
今回はWordPressでモーダルを実装する際にJavaScriptをどこに埋め込めばいいか悩んだのでその解決策をまとめる。
## 問題点
WordPressでJavaScriptのコードをどのphpファイルに埋め込めばいいのか。
## 解決策①
footer.phpの```</body>```の直前に埋め込むことで解決する。
ただしこれは非推奨な方法な模様。
```footer.php
	<?php wp_footer() ?>
	<script>
         //ここにJavaScriptの処理
	</script>
</body>
</html>
```

## 解決策②
もう一つの方法がheader.phpの```head```タグ内の```<?php wp_head(); ?>```より上に外部ファイルを読み込む関数を記述するというもの。
```header.php
	<script src="<?php echo get_template_directory_uri(); ?>{jsファイルのパス}"></script>

	<?php wp_head(); ?>
```
```php
<?php echo get_template_directory_uri(); ?> 
```
上記の関数は、テーマフォルダ内のパスを取得するための関数である。

## 解決策③
この方法がWordPressによって推奨されている。

functions.phpに以下のように記述する。
```function.php
function my_method() {
	wp_enqueue_script(
		'js_script',
		get_template_directory_uri() . '/myscript.js',
	);
}
add_action('wp_enqueue_scripts', 'my_method');
```
```wp_enqueue_script```関数の第一引数には固有のハンドル名（ファイルを区別するための名前）を記述し、第二引数にはスクリプトのパスを指定する。第二引数にはパスを取得できる```get_template_directory_uri()```を使用。



参考：https://developer.wordpress.org/reference/functions/wp_enqueue_script/

以上の方法でWordPressにJavaScriptを読み込むことができる。
ubuntu 22.04をインストールした～vmware workstation pro~

# 概要
vmware workstation proでubuntu 22.04をインストールした時の備忘

# OS
https://releases.ubuntu.com/jammy/
↑から.isoファイルをダウンロード。desktop版もある。

## OS セットアップ
基本ウィザードにしたがって設定ポチポチしていくだけ。簡単。
k8sのテスト環境作りたかったので、３台分つくる。
１台セットアップして、あと２つはスナップショットを利用して建てると楽。


# 設定に使ったコマンド

#### タイムゾーン変更
```
$ timedatectl set-timezone Asia/Tokyo
```
確認
```
$ timezonectl
```


#### ホスト名の変更
```
$ hostnamectl set-hostname hogehoge
```
確認
```
$ hostnamectl
```

#### sshログイン
よしなに設定してくれてるが故に直ぐにアクセスできる。

```
$ ssh hoge@hogehoge
```



## 参考
https://www.server-world.info/query?os=Ubuntu_22.04&p=hostname
相対パスと絶対パスの違い

ウェブ開発において、画像やその他のリソースを参照する際に使用される相対パスと絶対パスには、それぞれの利点と欠点があります。

これらのパスの違いと、それがSEOにどのように影響するかについて詳しく説明します。

# 相対パスとは？

相対パスは、現在のページの位置を基準にしてリソースへのパスを指定する方法です。
例えば、`img src="img/xxxx.png"`という記述は、現在のページのURLを基準にして`img`フォルダ内の`xxxx.png`という画像を参照します。

## 例
- **現在のページ**: `https://example.com/xxxx/index.html`
  - `img src="img/xxxx.png"`は`https://example.com/xxxx/img/xxxx.png`として解釈されます
- **現在のページ**: `https://example.com/xxxx/`
  - `img src="img/xxxx.png"`は`https://example.com/img/xxxx.png`として解釈されます

このように、相対パスは参照するページの位置によって異なる結果を引き起こすため、場合によってはリソースが正しく表示されないことがあります。

# 絶対パスとは？

絶対パスは、サイトのルート（ドメイン）を基準にしてリソースへのパスを指定する方法です。絶対パスを使用すると、どのページから参照しても同じリソースを正確に指定できます。

## 例
```html
<img src="https://example.com/xxxx/img/xxxx.png">
```

この記述により、`https://example.com/xxxx/img/xxxx.png`という特定のURLを参照するため、どのページからでもリソースが正しく表示されます。

# ローカル開発環境での問題

ローカル環境で`index.html`を使用して開発する際、相対パスが正しく機能しているように見えても、本番環境ではURLパスに`index.html`が省略されることがあり、これにより、画像やその他のリソースが見えなくなる場合があります。

### 例
- **ローカル環境**: `http://localhost:8080/xxxx/index.html`
  - `img src="img/xxxx.png"`は`http://localhost:8080/xxxx/img/xxxx.png`として解釈され、画像が正しく表示される
- **本番環境**: `https://example.com/xxxx/`
  - 同じ`img src="img/xxxx.png"`が`https://example.com/img/xxxx.png`として解釈され、画像が表示されない

# 絶対パスの利点

1. **一貫性**:
   - 絶対パスはURLがどのページから参照されても一貫して正しいリソースを指すため、リンク切れが発生しにくいです。

2. **キャノニカルURL**:
   - 絶対パスを使用することで、検索エンジンがコンテンツの正確な位置をより簡単に認識し、キャノニカルURLの設定が正確になります

3. **クロールの効率化**:
   - 絶対パスは検索エンジンのクローラーがサイト内のページを効率的にクロールしやすくなります。特に、サイト内リンクをたどる際にクローラーが混乱しにくくなります

# 相対パスの利点

1. **柔軟性**:
   - サイトのドメインやフォルダ構成を変更する際に相対パスはそのまま機能するため、メンテナンスが容易です

2. **短くて読みやすい**:
   - 相対パスは通常短くて読みやすく、HTMLのサイズも小さくなります

# まとめ

サイトの構造や運用方法に応じて、相対パスと絶対パスを適切に使い分けることが重要です。ローカル環境と本番環境での動作確認を怠らず、特にパスの指定方法に注意することで、リソースが正しく表示されるようにしましょう。
[過去記事]Amazon AppFlowで不正な値をスルーする

## 今回やること

今回やりたいことはもはや言うまでも無くいつもどおりのS3からS3に対してCSVを送ることです。

CSVを送る過程でageの値に負の値があったレコードはスキップして見ようと思います。
今回使用するCSVはいつもとは若干異なり、下記の様になります。

```test.csv
id,first_name,last_name,password,age,
1,Tarou,Yamada,abcd1234,20,
2,Hanako,Yamada,abcd1234,22,
3,Tarou,Satou,abcd1234,24,
4,Hanako,Satou,abcd1234,-16,
```

## 前提

初回の記事を参考にS3バケット間でCSVのやり取りができる状態になっていることを前提とします。

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

AWSにログインしAppFlowの当該のフローの編集ページまで移動します。
手順3「データフィールドを編集」のページを開きます。
妥当性確認にて「妥当性確認を追加」をクリックします。

![Amazon_AppFlow-34-1024x960.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/77c7bc80-3e3f-c2c6-2b68-2a604660b786.png)

下記の様に設定します

- フィールド名：age
- 条件：
- アクション：レコードを無視

内容を確認して保存をクリックします。

![Amazon_AppFlow-35-1024x294.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/794aba18-1d32-76bb-6dae-59a13315a3c2.png)

## 実行

設定ができたらフローを実行してみましょう！
フローが正常に実行されたことを確認し、S3のフォルダを確認します。

![Amazon_AppFlow-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/99b8c3cb-0c14-612f-a2eb-0941ed2fc088.png)

CSVが格納されたディレクトリに移動し移動先のCSVをダウンロードします。

![miriwo-test-b_-_S3_bucket-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/e472fc33-ee03-073f-951f-4cf512a8fdc6.png)

内容を確認するとidが4のレコードの情報だけスキップされ、CSVが送られていました！これで無事完了です！

- 送信元CSV

    ```test.csv
    id,first_name,last_name,password,age,
    1,Tarou,Yamada,abcd1234,20,
    2,Hanako,Yamada,abcd1234,22,
    3,Tarou,Satou,abcd1234,24,
    4,Hanako,Satou,abcd1234,-16,
    ```

- 送信先CSV

    ```csv
    "id","first_name","last_name","password","age"
    "1","Tarou","Yamada","abcd1234","20"
    "2","Hanako","Yamada","abcd1234","22"
    "3","Tarou","Satou","abcd1234","24"
    ```

## おわりに

ある程度の以上値をAppFlowでフィルターすることも簡単にできました！これで送信先に異常なデータが渡ることを防げそうです！
[過去記事]Amazon AppFlowでCSVの内容を一部変更してみる part3

## 注意点 

「Amazon AppFlowを触ってみた」の記事でも紹介しましたが、Amazon AppFlowはフローを実行するたびに料金が発生します。

ご注意ください。また詳しい内容は下記の「注意点」をご確認ください！

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 今回やること

今回やりたいことは恒例のS3からS3に対してCSVを送ることです。

CSVを送る過程でfirst_nameとlast_nameの2つの値を合体してnameとして送ってみようと思います。
今回使用するCSVもいつもどおり一緒です。下記に記載しておきます。

```test.csv
id,first_name,last_name,password,age,
1,Tarou,Yamada,abcd1234,20,
2,Hanako,Yamada,abcd1234,22,
3,Tarou,Satou,abcd1234,24,
4,Hanako,Satou,abcd1234,16,
```

## 前提

初回の記事を参考にS3バケット間でCSVのやり取りができる状態になっていることを前提とします。

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 方法

AWSにログインしAppFlowの当該のフローの編集ページまで移動します。
手順3「データフィールドを編集」のページを開きます。
下記のフィールドにチェックをつけます。

- first_name
- last_name

「式を追加」をクリックします。

![Amazon_AppFlow-29-1024x712.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f5e40dce-d439-86bf-a316-0f0abf4e401d.png)

「式を選択」で「連結」を選択します。

![Amazon_AppFlow-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/1e78e4c9-28fe-5368-53d4-1d402226a342.png)

「新しい送信先フィールドを入力」に「name」と入力し「式を使用してフィールドをマッピングする」をクリックし式を保存します。

![Amazon_AppFlow-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/7203bfe0-f6a9-77ab-3134-a93ace44cf8d.png)

フローそのものの設定も保存しましょう。
ちなみに設定が完了したあとのマッピングフィールドの内容は下記の様になりました。

![Amazon_AppFlow-32-1024x415.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/a7040d86-63fa-8469-242c-60e4c00e88d1.png)

## 実行

早速当該のフローを実行してみましょう！
フローが正常に完了したことを確認してS3のフォルダを確認します。

![Amazon_AppFlow-33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/69517f94-2b26-2cd7-0eb0-2913c566bfec.png)

新規作成されたディレクトリに入りファイルを選択しダウンロードします。![miriwo-test-b_-_S3_bucket-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f50afbb5-c6df-32b1-7cc2-ca1309006999.png)

内容を確認すると既存の情報にプラスして、nameの値が追加されていました！今回も無事終了です！

- 送信元CSV

    ```test.csv
    id,first_name,last_name,password,age,
    1,Tarou,Yamada,abcd1234,20,
    2,Hanako,Yamada,abcd1234,22,
    3,Tarou,Satou,abcd1234,24,
    4,Hanako,Satou,abcd1234,16,
    ```

- 送信先CSV

    ```csv
    "id","first_name","last_name","password","age","name"
    "1","Tarou","Yamada","abcd1234","20","Tarou Yamada"
    "2","Hanako","Yamada","abcd1234","22","Hanako Yamada"
    "3","Tarou","Satou","abcd1234","24","Tarou Satou"
    "4","Hanako","Satou","abcd1234","16","Hanako Satou"
    ```

## おわりに

まさかこんなに簡単にCSVの内容を連結する事ができると思っていませんでした、、！複雑なことはできませんがある程度ならアプリケーションではなくAppFlowに式を記載することで対応できそうです、、！
[過去記事]Amazon AppFlow でCSVの内容を一部変更してみる part2

## 注意点 

「Amazon AppFlowを触ってみた」の記事でも紹介しましたが、Amazon AppFlowはフローを実行するたびに料金が発生します。

ご注意ください。また詳しい内容は下記の「注意点」をご確認ください！

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 今回やること

今回やりたいことは前々回「Amazon AppFlowmを触ってみた」や前回「Amazon AppFlow でCSVの内容を一部変更してみる part1」の時と同様にS3からS3に対してCSVを送ります。

part1はこちら↓

https://qiita.com/miriwo/items/55faf37612096ef0e9b5

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

CSVを送る過程で、情報をfirst_nameとlast_nameのみに絞って送ってみようと思います。
今回使用するCSVは前回と同じ下記です。（今気が付きましたがfirst_nameとlast_nameが逆になってますね、、すみません。このまま行きます。）

```test.csv
id,first_name,last_name,password,age,
1,Tarou,Yamada,abcd1234,20,
2,Hanako,Yamada,abcd1234,22,
3,Tarou,Satou,abcd1234,24,
4,Hanako,Satou,abcd1234,16,
```

## 前提

初回の記事を参考にS3バケット間でCSVのやり取りができる状態になっていることを前提とします。↓

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 方法

AWSにログインしAppFlowの当該のフローの編集ページまで移動します。
手順3「データフィールドを編集」のページを開きます。
下記のフィールドにチェックをつけます。

- id
- password
- age

「選択したマッピングを削除」をクリックします。

![Amazon_AppFlow-26-1024x710.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/628e28f5-91bf-414c-1a4c-ba3cd38424df.png)

これで送信されるCSVの情報がfirst_nameとlast_nameのみになりました。設定を保存しておきます。

![Amazon_AppFlow-27-1024x537.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/62ace4a0-59a5-44e1-d106-c5a4acf9c460.png)

## 実行

当該のフローを実行して結果を見てみましょう！
フローが正常に完了したことを確認して、S3のフォルダを見に行きましょう。

![Amazon_AppFlow-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/56b3061e-c1fb-e3e5-0702-07b23a0308d8.png)

ディレクトリができているのでその中に入り、格納されているファイルをダウンロードし、確認します。

![miriwo-test-b_-_S3_bucket-4-1024x403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/e8848ae5-839c-dee3-be7e-8c5cd69fe870.png)

![miriwo-test-b_-_S3_bucket-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/2956764a-906e-2925-7920-1d875fbb0fea.png)

内容を確認すると限定された情報だけが送信先のCSVに反映されていました！今回も無事完了です！

- 送信元CSV

    ```test.csv
    id,first_name,last_name,password,age,
    1,Tarou,Yamada,abcd1234,20,
    2,Hanako,Yamada,abcd1234,22,
    3,Tarou,Satou,abcd1234,24,
    4,Hanako,Satou,abcd1234,16,
    ```

- 送信先CSV

    ```csv
    "first_name","last_name"
    "Tarou","Yamada"
    "Hanako","Yamada"
    "Tarou","Satou"
    "Hanako","Satou"
    ```

## おわりに

一部の値のみ抜粋してCSV化する事ができました！ちょっと覚えちゃえばめっちゃ簡単ですね！
見やすいPRの書き方まとめ ~pull requestをしっかり書こう！！！~

# この記事を読めば何ができるようになるのか
- 見やすいPRの書き方がわかります
- レビュワーが見やすいレビューを書くための心得がわかります

# 対象読者
- よりよりPRの書き方を知りたい
- レビュワーからわかりにくいと言われたことがある

# PRの書き方
ここでは、是非PRに書いて欲しいこと、使って欲しい表記方法について紹介します！
またこれから

**レビュアー** : レビューする側(依頼されたPRをレビューする人)
**レビューイ** : レビューされる側

とします

## 今の現状を言語化して記載しよう！
変更前の状態を言語化しましょう。デザイン変更や新規画面作成ならスクショもあると、よりわかりやすいです。
これをすることで、コードを見る前にレビュアーは変更前の大体の概要を予想できます。

## 変更理由/作成理由を記載しよう！
レビュアーは、今回の変更/作成の理由を全部知っているわけではないです！(もちろん知っている場合もあるでしょう)

これがない時、レビュアーは「このコードはなんのために変更/作成されたんだ？？」となってしまい、レビューの際に、「コードの整合性、綺麗さ、エラー確認、挙動確認」等しか判断できません。

「別にそれでよくない」ったかも知れません。
ここで例を見てみましょう！

ex) Buttonコンポーネントを作成したPR
```typescript
interface ButtonProps {
    onClick?: () => void;
    title: string;
}

const Button: React.FC<ButtonProps> = ({onClick,title}) => {
    return (
        <button type="button" onClick={onClick}>{title}</button>
    );
};

export default Button;
```
作成理由を書いていない場合、レビュワーはおそらくlgtmを出すでしょう(コード的に問題はないと判断して)

では、ここに作成理由として以下があった場合、どうなるでしょう？
**「今後form等でも使うので、components化した」**

こうなった場合、話は変わってきます。
レビュワーはchanged requestを出して、
**「typeをpropsで渡せるようにした方がよさそうですね〜」**
とするでしょう。

もし、変更/作成理由を書いていなかったら、form内でこのcomponentsを使うとなったときに修正PRを作成する必要が出たでしょう。少なくとも手戻りが発生します。

**変更理由を書くことは、レビュワーだけでなく、レビューイにもメリットがあります！**

## ただ画像を貼り付けない！
作成した画面、修正した画面、apiからのレスポンスの中身...
これらをただスクショして貼り付けるだけになっていませんか？？？

確かに、証拠として収めているからいい。そうです、貼っているだけでも十分レビュワーのことを考えられていると思います。
(スクショすら貼ってない人は、貼りましょう。しっかり自分の環境では動いているよ〜、の証明になるので)

### tableをつかって見やすくしてあげましょう！
「わざわざつくるのめんどくさい」　「毎回tableの作り方調べて、コピペするのだるい」
なんて思ってませんか？、**それgithubが提供してくれているんです。**

![画面収録 2024-05-22 19.01.54.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3304659/c461c4be-51b7-8833-4dda-7a69ec0afc99.gif)

/ (半角スラッシュ)を打つと、選択肢が出てくるので、そこからtableを選択してあげれば終わりです。
あとは自分が作りたいtableの形を選択してあげるだけ。
めちゃくちゃ簡単でしょ笑

最後にtableを使っている例と使っていない例を比較して(どっちの方が見やすいでしょうか？比較しやすいでしょうか？)

<details><summary>例</summary>

| tableなし | tableあり |
|:-:|:-:|
| ![FireShot Capture 008 - New Issue · kazuki1023_kazuki1023 - github.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3304659/751ff3f2-a42c-c73a-068f-43260d119f68.png) | ![FireShot Capture 009 - New Issue · kazuki1023_kazuki1023 - github.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3304659/84892ccb-85c3-5379-57cb-896c3637c1ab.png) |



</details>

# まとめ
以下の２点を行なって見てください
1. 変更/作成理由を記載する
2. tableを使って、見やすくしてみる

最初はめんどくさいと思うことでしょう笑。時間かかるし別にやらなくても良くない？って思うかもしれません。
**しかし、これらのちょっとした気遣い、配慮が、後々の手戻りや勘違い、認識の齟齬を減らす行為に繋がってます**(と自分は信じてます。)

是非是非実践して見てください！！！



# 最後に
## ちょっと自分について
都内大学３年生(26卒)。大学に入って、もともと興味のあったプログラミングを[プログラミングコミュニティPosse](https://posse.community/)に入って勉強。コミュニティ内のハッカソンやチーム開発を行ってきた。去年から長期インターンを行なっている。

## どうして書こうと思ったのか
長期インターンを通して、メンターさんにレビューされにくいPR、されやすいPRがあることに気づきました。最初は

変更箇所が多い→見るのに時間かかるからレビューされにくい。
変更箇所が少ない→見るのに時間かからないのでレビューされやすい。

だと思っていた。

確かにそれもあるのだが、自分がチーム開発等で **レビューする側になった時、レビューしやすいPR,レビューしにくいPRがあることに気づいた**

レビューしてもらうPRを書いてもらうために、そしてメンターさんにレビューしてもらえる、気持ちよくレビューできるためのPRを書けるようになるための方法をまとめて忘れないようにしたかったので、この記事を書くことにしました。

参考になれば幸いです...
(間違っていることもたくさんあると思います。すぐ修正したいので連絡お待ちしてます🙇‍♀️)
【ネタ】関数のreturn文を1つにしたい

`return`文が複数ある関数を作ったら、静的解析で次の警告が出て、直すのに困ったことがある。

``` text
MISRA-C:2004 Rule 14.7
関数では、関数の最後に唯一の出口がなくてはならない。
```

実際のコードを簡略化したのが、次のコード。
`return 1`のところを直そうとすると、その下の`moreFunction(arg2)`が邪魔になって、ややこしくなりそう...。`goto`文も使いたくないし...。

``` c
// 例：return文が4つある関数

int function(int arg1, int arg2){
    int var = -1;
    
    if(arg1 == 1){
        var = someFunction(arg2);
        if(var == 1){
            return 1;     // return文 (1つ)
        }else if(var == 2){
            arg2 += 2;
        }
        moreFunction(arg2);
        return 4;         // return文 (2つ)
    }else if(arg2 == 2){
        return 2;         // return文 (3つ)
    }
    
    return 0;             // return文 (4つ)
}
```

次のコードのように、全体を`do～while()`で囲めば、`break`で最後の`return`にジャンプ出来るので、簡単に直せるのではと思ったけど、レビューで怒られそうな気がしてやらなかった。

``` c
// 例：return文が4つあった関数

int function(int arg1, int arg2){
    int var = -1;
    int ret = 0;          // 追加

    do{                   // 追加
        if(arg1 == 1){
            var = someFunction(arg2);
            if (var == 1) {
                ret = 1;  // 修正
                break;    // 追加
            }else if(var == 2){
                arg2 += 2;
            }
            moreFunction(arg2);
            ret = 4;      // 修正
            break;        // 追加
        }else if(arg2 == 2){
            ret = 2;      // 修正
            break;        // 追加
        }
    }while(0);            // 追加
    
    return ret;           // return文 (4つ→1つ)
}
```

...が、数年後、別の仕事で全体を`do～while()`で囲んだコードに出会った！
同じことを考える人がいるのかと思ったけど、自分が知らないだけで、有名な方法なのかも。
WSR-1166DHPのファームウェアをアップデートする方法

NICTER 解析チームから、自宅で使っているのと同じBuffalo社製WiFiルータ「WSR-1166DHP」がボットに感染しているとのツイートがあった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/5d429d45-36e0-55ba-4795-34857e65fe7e.png)

自宅では ローカル側で アクセスポイント として使っているので問題ないと思われるが、ファームウェアのアップデートを忘れていたので、ファームウェアをアップデートすることにした。

手順がややこしいので、情報共有する。
なお、本資料の読者の前提は、WSR-1166DHPをLAN側でAPとして利用している人向けである。

ファームウェアのダウンロード

https://www.buffalo.jp/product/detail/software/wsr-1166dhp.html

ダウンロードしたら、wsr1166dhp-118.exeをダブルクリックして自己解凍させる。
解凍先は選べず、C:\BUFFALO\wsr1166dhp-118 固定。

WSR-1166DHPの管理画面にパソコンから接続する準備

安全のため、ルーターとWSR-1166DHPを接続してしてるLANケーブルを外す

ディップスイッチ２つとも変更
AP →　ルーター
マニュアル　→　AUTO

勝手に再起動するので１分くらい待つ

パソコンのWiFiが断線するので、再接続する

管理画面にログイン
ブラウザから 192.168.11.1 にアクセスすると、NICTERのツイートにあるようなログイン画面が表示される。

ログインしたら、まず、外部から侵入を防ぐ設定を確認しておく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/34cbf866-ef88-cf05-2b45-ef25e59add37.png)

「Internet側リモートアクセス設定を許可する」のチェックが外れていることを確認

ファームウェアの更新

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/e07d53b8-3003-8270-f562-9a2329ac0f4f.png)

ファイルを選択して、更新実行ボタンを押すだけ
パソコンのWiFiが断線するので、再接続する
成功すると、↓こうなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/7650def3-d0a6-b032-5041-0b37981f9cde.png)


最後に

ルーター →　AP
AUTO　→　マニュアル
ルーターとWSR-1166DHPを接続してしてるLANケーブルを接続

でもとに戻る。

あとは　A　のSSIDで接続（誤って G で接続すると、くっそ遅い。）

以上、健闘を祈る。
reCAPTCHA v2エンタープライズを簡単に解決する

# reCAPTCHA v2エンタープライズを簡単に解決する

NextCaptchaを使用すれば、reCAPTCHA v2エンタープライズ CAPTCHAを簡単にバイパスできます。支払いが必要なCAPTCHAソルバーサービスとして最もコスト効率の良いサービスを提供します。

1. [NextCaptchaにサインアップし](http://nextcaptcha.com/)、CAPTCHAソルバーサービスの無料トライアルを開始します

2. ターゲットのウェブサイトに対するAPIリクエストタスクを作成するか、CAPTCHAソルバーSDKを使用します

3. CAPTCHAで保護されたページにg-recaptcha-responseを提出し、reCAPTCHA v2エンタープライズのウェブサイトやAPIデータをスクレイプします

## ステップ1： NextCaptchaのclientKeyを取得する

NextCaptchaにサインアップして、無料のAPIキーとすぐに利用できる無料トライアルクレジットを受け取ります。

## ステップ2： あなたのreCAPTCHA v2エンタープライズソルバータスクを作成する

サインアップしたら、ターゲットのウェブサイトのwebsiteKeyやその他のパラメータを見つけることができます。

例えば：

websiteKey:  6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-

**タスクオブジェクト構造**を作成します：

```json
{
    "clientKey":"clientKey",
    "task": {
        "type":"RecaptchaV2TaskProxyless",
        "websiteURL":"https://www.google.com/recaptcha/api2/demo",
        "websiteKey":"6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
    }
}
```
これをリクエストSDKで送信するか、cURLオプションを使用してリクエストを実行します。

PythonのCAPTCHA解決サービスSDKを使用して送信します

```python
from nextcaptcha import NextCaptchaAPI

api = NextCaptchaAPI(client_key="YOUR_CLIENT_KEY")

result = api.recaptchav2enterprise(website_url="https://example.com", website_key="SITE_KEY")

```

それはうまくいきましたか？ 必要であればリクエストを再試行してください。これは99.7％のケースで十分です。

## ステップ3：すべてのCAPTCHA保護ページをスクレイプする

CAPTCHAで保護されたページにg-recaptcha-responseを提出し、reCAPTCHA v2エンタープライズのウェブサイトやAPIデータをスクレイプします。
Column等のchildrenに特定の条件でだけ複数のWidgetを渡したい時に使えるテクニック

ある条件の時だけColumnに追加でWidgetを表示したい時がある

それが一つのWidgetの時は以下のようにすればいい

```dart
class SampleView extends StatelessWidget {
  const SampleView({super.key, required this.hoge});
  final String? hoge;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('ガハハ'),
        // hogeがnon nullの時Textを表示する
        if (hoge != null) Text(hoge!),
      ],
    );
  }
}
```

例えば他のWidgetとの間にスペースを取りたい時どうすればいいか
Paddingを追加したり、SizedBoxとTextをまとめたWidetとして切り出すこともできるが、スプレッド演算子でも対応できる

```dart
class SampleView extends StatelessWidget {
  const SampleView({super.key, required this.hoge});
  final String? hoge;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('ガハハ'),
        // hogeがnon nullの時Textを表示する
        if (hoge != null) ...[
          // hogeを表示する時だけSizedBoxも表示したい
          const SizedBox(
            height: 4,
          ),
          Text(hoge!),
        ],
      ],
    );
  }
}
```

これはSizedBoxとTextを持つList<Widget>を作り、スプレッド演算子でそれを一気に追加している
Salesforceでレポートを作成しよう～日付項目マイナス日付項目の差を計算する方法

本橋孝昭です
Salesforceレポートを作成し、日付項目-日付項目を確認する方法をお伝えします

今回は例として
・取引先にカスタム項目（日付）A__c項目がある
・取引先にカスタム項目（日付）B__c項目がある
・A項目とB項目の差を数値で反映
（例　A項目が2025年1月10日　B項目が2025年1月1日の場合、差の9を出す）


ではやってみましょう

## ステップ１　取引先のレポートを作成、「行レベルの数式を追加」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/d434fac7-c5ad-1732-2962-e0951a62ea16.png)

## ステップ2 数式を作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/d1429532-6286-346d-7d32-26dbfb014fed.png)

今回の数式は
Account.A__c-Account.B__c
となります

以上で設定は完了です

実際にレポートを見ると

A項目が2025年1月10日　B項目が2025年1月1日の場合、差の9

を出していることが分かりますね


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/1cbfc0f9-1ce5-ec63-7470-1361010ad606.png)