OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonの"=="演算子と"is"演算子の違いについて

　いつもは勉強したことのまとめ的な記事とかネタっぽい記事を書いている事が多いのですが、PythonのアドベントカレンダーっぽくPython言語の小ネタ的な事を記事にしてみようと思います。

## "=="演算子と"is"演算子の違い

　具体的に2つの違いとしては
　・"=="演算子はオブジェクトの**同等性**をチェックするもの
　・"is"演算子はオブジェクトの**同一性**をチェックするもの
　です。

　ここから先はコードで実際に示した方が分かりやすいので、コードに移ります。Pythonのインタプリタモードを使って説明します。

　まずは変数list_aというリストを作って、そのlist_aを指す変数list_bを作ります。

```
>>> list_a = [0, 1, 2, 3]
>>> list_b = list_a
```

当然、中身を見ても全く同じものが入っています。

```
>>> list_a
[0, 1, 2, 3]
>>> list_b
[0, 1, 2, 3]
```

　list_a, list_bを"=="演算子と"is"演算子を使って比較すると

```
>>> list_a == list_b
True
>>> list_a is list_b
True
```

　どちらもTrueが返ってきました。

　次に変数list_aと全く同じ中身の変数list_cを作成します。

```
>>> list_c = list(list_a)
>>> list_c
[0, 1, 2, 3]
```

　これを同じように"=="演算子と"is"演算子を使って比較すると

```
>>> list_a == list_c
True
>>> list_a is list_c
False
```

　"is"演算子ではFalseが返ってきました。list_aとlist_cは中身が同じであったとしても、これらは別のオブジェクトだからです。試しにlist_aの中身を更新してlist_b, list_cがどうなったのかを確認してみます。

```
>>> list_a[0] = 1
>>> list_a
[1, 1, 2, 3]
>>> list_b
[1, 1, 2, 3]
>>> list_c
[0, 1, 2, 3]
```

　list_aの中身を更新することで同じオブジェクトを指しているlist_bは更新され、別のオブジェクトであるlist_cはそのままです。

　このように**"=="演算子はオブジェクトの中身が同じ場合にTrue**を返し、**"is"演算子はオブジェクト自体が同じ場合にのみTrue**を返します。


## 補足1 Nanの比較について

**※これにハマる人が多いので、記載しておきます。**
　PythonにおけるNanに関してはどの比較演算子(==, !=, <, >, <=, >=)を使ってもFalseが返ってくるという性質があるので、Nanかどうかを判定するには~~"is"演算子か~~numpyのisnan()を使いましょう。
(Nan自体が実態の無いものだから比較はできないってことでしょうかね？いや、知らんけど)

```
>>> a = np.nan
>>> a == np.nan
False
>>> a is np.nan
True
>>> np.isnan(a)
True
>>> np.nan == np.nan
False
>>> np.nan is np.nan
True
>>> np.isnan(np.nan)
True
```

(2021/12/21 追記)
コメントで指摘して頂いたのですが、以下のようなケースもあるのでNanの比較は"is"演算子ではなくisnan()を使った方が良いようです。

```
>>> b = float('Nan')
>>> b is np.nan
False
>>> np.isnan(b)
True
>>> c = np.nan + 1
>>> c is np.nan
False
>>> np.isnan(c)
True
```



## 補足2 多次元配列について

**※これもハマる人が多いので、記載しておきます。**
　ちょっと今回の趣旨とは若干ずれるのですが、これもハマる人が多いので、記載しておきます。

　多次元配列、いわゆるオブジェクトの中にオブジェクトがある場合ですが、外側のオブジェクトと内側のオブジェクトがある事に注意が必要です。

　今度は二次元配列でlist_aを作成し、先ほどと同じようにlist_cをlist_aと同じ内容で新規に作成します。

```
>>> list_a = [[0, 1], [2, 3]]
>>> list_c = list(list_a)
>>> list_c
[[0, 1], [2, 3]]
```

　同じように"=="演算子と"is"演算子で比較してみましょう。

```
>>> list_a == list_c
True
>>> list_a is list_c
False
```

　先程と同じ結果になりました。次はオブジェクトの中のオブジェクトを比較してみましょう。

```
>>> list_a[0] == list_c[0]
True
>>> list_a[0] is list_c[0]
True
```

　見ての通り内側のオブジェクトの比較で"is"演算子でもTrueが返って来ました。どうやら外側のオブジェクトは別でも内側のオブジェクトは同じオブジェクトのようです。
　なので、list_aの内側のオブジェクトを変更すると

```
>>> list_a[0][0] = 1
>>> list_a
[[1, 1], [2, 3]]
>>> list_c
[[1, 1], [2, 3]]
```

　list_aだけでなくlist_cの内容も更新されました。このように多次元配列の場合、外側のオブジェクトと内側のオブジェクトがある事に気をつけましょう。

　このように、外側のオブジェクトのみを新しく作成して内側のオブジェクトは元のオブジェクトを参照する事を**シャローコピー(浅いコピー)**、外側も内側も新しいコピーを作成する事を**ディープコピー(深いコピー)**と言います。

　ディープコピーはcopyモジュールで定義されているdeepcopyを使って深いコピーを作成します。

```
>>> list_a
[[1, 1], [2, 3]]
>>> import copy
>>> list_d = copy.deepcopy(list_a)
>>> list_d
[[1, 1], [2, 3]]
>>> list_a is list_d
False
>>> list_a[0] is list_d[0]
False
```

## まとめ
・"=="演算子は2つのオブジェクトの中身が**同等**の場合にTrueを返す。
・"is"演算子は2つのオブジェクトが**同一**の場合にTrueを返す。
・Nanとの比較には~~"is"演算子~~isnan()を使おう。
・多次元配列には外側のオブジェクトと内側のオブジェクトがある事に気をつけよう。
【Python】Jinja2で表を作る

## はじめに

画像などいくつデータがあるかわからないものを表にしたい場合，
一行にいくつデータを表示したいかを決め，
あとはよろしく表を作ってほしい

例えば下のような表
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/d534f053-9bc6-637f-78e3-230950b63686.png)
ご参考までに，チャートの描き方は[こちら](https://qiita.com/tnozoo/items/000528b2001fe629ced4)

Jinja2 を使うと楽だったので備忘録として残しておく

## python

本サンプルでは，画像ではなく0～25までの数字を表示しています
数字の画像が26枚あるとでも思ってください

26個の数字を5列の表にして，タイトルがtestのtest.htmlを作成します

```python
import os
import jinja2 as jj

# html ファイルへの出力
def to_file(dataDic, filename):
    # テンプレートファイル読み込み
    je = jj.Environment(loader = jj.FileSystemLoader('.', encoding = 'utf8'))
    tpl = je.get_template('jinjatest.tpl')
    # 出力
    with open(filename, 'w', encoding = 'utf-8') as fp:
        st = tpl.render(dataDic)
        fp.write(st)

if __name__ == '__main__':
    # 0-25の数字
    contentList = [{'no': v} for v in range(26)]
    # タイトルは"title"，列数は5
    dataDic = {'contentList': contentList, 'title': 'test', 'num_of_columns': 5}
    # html 出力 ファイル名は test.html
    to_file(dataDic, 'test.html')
```

## Jinja2 テンプレート

```html:jinjatest.tpl
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>{{ title }}</title>
</head>
<body>
<center>
<h1>{{ title }}</h1>
<table border=1>
{% for content in contentList -%}
{% if loop.index0 % num_of_columns == 0 -%}
<tr>
{% endif -%}
<td><center>{{ content.no }}</center></td>
{% if loop.index0 % num_of_columns == num_of_columns-1 -%}
</tr>
{% elif loop.last -%}
{% for i in range(num_of_columns-loop.index % num_of_columns) -%}
<td>&nbsp;</td>
{% endfor -%}
</tr>
{% endif -%}
{% endfor -%}
</table>
</center>
</body>
</html>
```

データの数（本サンプルでは26回）まわるループです
`-%}` のように `-` をつけることで出力される html に余計な空行が入りません

```html
{% for content in contentList -%}
・・・
{% endfor -%}
```

行の先頭では `<tr>` を書きます
`loop.index0` は `0` から始まるループのインデックスです
ちなみに `loop.index` は `1` から始まるループのインデックスです

```html
{% if loop.index0 % num_of_columns == 0 -%}
<tr>
{% endif -%}
```

行の終わりでは `</tr>` を書きます

```html
{% if loop.index0 % num_of_columns == num_of_columns-1 -%}
</tr>
```

行の終わりではないところで，データの終わりになったら，
行末まで空白スペース `&nbsp;` を書きます

```html
{% elif loop.last -%}
{% for i in range(num_of_columns-loop.index % num_of_columns) -%}
<td>&nbsp;</td>
{% endfor -%}
</tr>
{% endif -%}
```

## 出力された html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/b1d06520-6324-9733-31ad-8e06585d41fb.png)

## おわりに

最初は python の中で html を一生懸命書いていたが，
Jinja2 を使うと楽だった
RSpec始めよう！簡単セットアップ

# 1. rspec-railsをbundle install

```Gemfile.
group :development, :test do
  gem 'rspec-rails'
end
```


# 2. Railsアプリにrspecをインストール
Railsアプリにrspecをインストールする

```rspec.
$ bundle exec rails generate rspec:install
```

RSpecの設定ファイル（`.rspec`)と、
specフォルダの中に`rails_helper.rb`と`spec_helper.rb`のファイルがインストールされます。


# 3. --format documentation追加

必須ではないですが、`.rspec`ファイルに以下の記述を追加することでテスト結果が見やすくなります。

```.rspec
--require spec_helper
--format documentation #追加
```


# 4. テストファイル記述

実際に処理を行うファイルを記述します。
`require 'rails_helper`を記述すること
とりあえずrspecが上手く動作しているかを確認したいのでテストの外枠（itからendまで）だけ作って中身は書いていません


```post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  
  it "投稿内容、ユーザーidがあれば有効な状態であること" do
  end
end
```

`bundle exec rspec`してみます。
![スクリーンショット 2021-12-14 21.03.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1293293/ef84e49d-254d-92e2-3237-054b915e6135.png)


緑の文字で出てきたら成功です。
今更 WindowMaker


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221128/aeb5ba9d-b3a3-00c9-05bc-c0abb00a0941.png)
---

:::note info
今更ながら WindowMaker を使いやすくするための Tips をまとめる。
ひっそりと 15 年位使い続けていても、別に Tips と言える程のものはさして増えていないが、まぁ備忘録的な。
:::

:::note warn
対象は Debian, Ubuntu, LinuxMint 等の debian 系。
Redhat 系は未対応。
:::

# ◎ WindowMaker インストール
> ```
> apt install wmaker wmaker-common wmaker-data
> ```

# ◎ Docklet (Dock Apps)

## ■ 時計
![wmclockmon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221128/afcdf2d1-8b5b-f29a-a4a7-43b528193b04.png)

### インストール
> ```
> apt install wmclockmon
> ```

### 設定
> ```bash:~/GNUstep/Library/WindowMaker/autostart
> #!/bin/sh
> wmclockmon &
> ```

## ■ 引き出し
![wmdrawer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221128/9f49e48c-d5f3-9706-1964-5820d113ad3c.png)

### インストール
```
apt install wmdrawer
```

## ■ システムトレイ
![wmdocker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221128/5044d994-6e2f-dbf1-4e55-473cafbc30c8.png)

### インストール
> ```
> apt install wmdocker
> ```

### 設定
何もしないとアイコンがはみ出てしまうので、実行コマンドを変更する。

1. wmdocker 右クリック
1. 「設定（ドックの登録アプリケーション設定）」
1. 「アプリケーションのパスと引数」の値を以下に変更する

> ```
> wmdocker -wmker
> ```

---
# その他

## 10 分程度で画面がスリープしてしまうのを防ぐ
> ```bash:~/GNUstep/Library/WindowMaker/autostart
> #!/bin/sh
> xset m 20/10 4
> xset s off -dpms
> ```

---
リモートの時こそ会議後のふりかえり 〜コミュニケーションのきっかけにも〜

[ふりかえり Advent Calendar 2021](https://adventar.org/calendars/6327) の 14日目の記事です。

組織内でのコミュニケーションのきっかけ作りに、
ふりかえりを活用してみては？というお話。

## どんな内容？

Covid-19 でリモートワーク、増えましたね。
リモートワーク中の雑談話とかありますけど、結構チームでの取り組み多いですよね。
Slack だったり Teams だったりって、同じチームの人としか話せなかったりしますよね。

チーム外の仲間、隣の部の部長など、
チームのような近い距離よりもう一歩遠い人とのコミュニケーションに
「会議のふりかえり」を使うご紹介です。

## オフィス通いの頃のチーム外の人との雑談って？

オフィスでのチーム外との雑談、どんな時にしてましたか？
大きく、以下の3ケースかなと思います。

1. オフィスをうろついてて、なんとなく話しかけられそうだから雑談開始。
1. 廊下、道端でばったり雑談。
1. **たまにの一緒に話す会議で、会議室からの帰り道で雑談。**

そう、今回話したいのは3つ目のケースです。
(1つ目、2つ目だとオンラインバーチャルオフィスを使う、とかそんな話になると思います)

<img width="200" alt="ふりかえり" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286147/3c8be459-11fd-2cbb-61e3-b8e477b0aec2.png">

## ではどうする？

オンラインになると、会議が終わったら会議室がぶつんと閉じられて終わってしまう。
帰り道も何もないですね。

ではどうするか。
会議後の最後5分をふりかえりを取り入れてみましょう。

ふりかえり手法は、簡単なものでいいです。
例えば、以下のようなものだと3項目で簡単に書けます。
<img width="400" alt="ふりかえり" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286147/54b2ec74-b230-cfb4-5c38-e33021cab818.png">

こうして書いてみると、会議の内容に関連してふと思ったことを
それぞれが書いて伝えることができるわけです。

上のふりかえり手法だと、実体験としては「もやもや」が結構良いネタの宝庫になるイメージです。

- 「今日の会議と関連して、こんなの最近気になってるんだよな」
- 「さっきは脱線するから黙ってたけど、実はこんなこと思ってて」

5分間、ふりかえりながら「帰り道雑談」と同じようなことができてしまいます。
もちろん、会議の内容もふりかえって記憶に定着。一石二鳥です。

5分で話し足りなかったら、「後で改めて話そう」ということにも。

## 「ふりかえりはいいから会議したい」と言われるなら

雑談を目的に「ふりかえりを導入したい」って提言してみたけれど、
「いや、最後の1秒まで会議したいからそんな時間はない」って言い返されそうと
思ったあなたへ。

そこは、本来の目的である「ふりかえり」の価値を伝えてみましょう。

### 会議の内容を定着させる

オンラインになって、「会議」「会議」「会議」と続くと、
会議室での会議が連続していた時以上に、何話したか覚えてなかったりしませんか？

これ、今までは会議室の移動時間が効いてたという人もいるのだと思います。

- 移動時間に、直前の会議内容を反芻できる。 (一人ふりかえりしてる！)
- 移動時間に、会議の出席者と話してる。(みんなでふりかえりしてる！)

そう。会議室で会議してた頃は、移動時間使って自然とふりかえりしてたんです。

だからこそ、オンラインでこれを再現すると考えれば良いわけです。

### 会議を改善する

こちらは、オンラインでなくても共通の話。

会議が終わってみて、こんな気持ちになることありますよね。

- あのパート、うまく議論できなかった。
- 用意したアジェンダ消化できなかった。

一人が思ったところで、次の会議で改善するのは難しかったりします。
思いを全員に共有することで、次の会議が良くなっていく。
5分の会話で、次回からの会議の効率が上がっていくなら安いものではないでしょうか。

## 最後に

オンライン会議のふりかえりを、うまく活用しようというお話でした。

個人的には「とりあえず、ふりかえりを入れておこう」から始めてみただけだったのですが
思ってなかった効果につながったなー、ということで記事にしました。

皆様の糧になれば幸いです。
gonum.org/v1/plot が良かった話

# はじめに

この記事は Go Advent Calendar 2021 の 14 日目の記事です。(執筆時点で 15 日になりそうなのは内緒)
今回は以前簡易的なグラフを作る際に良かった `gonum.org/v1/plot` の紹介をしたいと思います。

# 背景

普段は Go を使ってバックエンドの開発を行っているのですが、たまにグラフをプロットするときがあります。
この辺のプロット周りのライブラリはどの言語にもあると思いますが、Go でも簡単にできないかなと思い探したところ、この plot に出会いました。
細かいグラフを出すのは結構たいへんですが、自分が思っているような簡単なグラフはすぐ生成できました。


# gonum.org/v1/plot で作ったグラフ

まず最初にこの plot で作成したグラフを紹介します。

### 近似数値微分法 を使った 3・5・7・9 点近似数値微分の誤差

![image_08-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216929/c0209b16-b6e4-0ff2-bbc6-c726392e2e22.png)

### 減衰振動

![image_11-01_dump.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216929/696c5326-b2ba-dd4d-fe89-f4a61d281496.png)


グラフの詳細は割愛しますが、手軽に良さげなグラフを描画することができます。

# グラフ生成方法

自前で軽くラップしているのでわかりにくいかもしれないですが、以下のようになります。
plot.New()で初期化でき、その中に情報を格納していきます。
プロットするグラフの種類ですが、折れ線グラフや散布図、ヒストグラムなど普通に使う分には問題ないくらいには用意されています。


```go
func main() {
	plps := printPlot.NewPlotLinePoints()
	plps.GeneralForPlot.Path = "image.png"
	plps.Plot.X.Label.Text = "x"
	plps.Plot.Y.Label.Text = "y"
	plps.Pts = append(plps.Pts, solver(yFunc))
	plps.PrintPlotLinePoints()
}


// printPlotパッケージ(ラップ)

type PlotLinePoints struct {
	GeneralForPlot GeneralForPlot
	Plot plot.Plot
	Pts []plotter.XYs
}

// New LinePoints 新規作成
// v0.9.0 から plot.New() でエラーを返さなくなったので注意(このコード書いたのが結構前です。)
func NewPlotLinePoints() (*PlotLinePoints){
	p, _ := plot.New()
	pts := make([]plotter.XYs, 0)

	plps := &PlotLinePoints{
		GeneralForPlot: GeneralForPlot{
			Path: "image.png",
		},
		Plot:           *p,
		Pts:            pts,
	}
	return plps
}

// PrintPlotLinePoints 描画
func (ps *PlotLinePoints)PrintPlotLinePoints() {
	ps.Plot.Add(plotter.NewGrid())
	for key, _ := range ps.Pts {
		graph,_, err := plotter.NewLinePoints(ps.Pts[key])
		if err != nil {
			panic(err)
		}
		graph.Color = randomColor()
		ps.Plot.Add(graph)
		ps.Plot.Legend.Add("line:"+strconv.Itoa(key), graph)
	}
	if err := ps.Plot.Save(6*vg.Inch, 6*vg.Inch, ps.GeneralForPlot.Path); err != nil {
		panic(err)
	}
}
```

どの方法で描画するのかなど毎回設定するのが面倒だったのでオレオレラッパーを作成し、実装しています。
使う場合は、main 関数で使っているように最低限の情報を入れてグラフの画像を生成するようにしています。
`plps.Pts = append(plps.Pts, solver(yFunc))` というように描画したい点の集合を append することでいい感じに表示してくれます。
プロットは `ps.Plot.Save(6*vg.Inch, 6*vg.Inch, ps.GeneralForPlot.Path)`で行っています。


# 最後に

簡単になりましたが以上になります。
グラフのプロットというと Python などを思い浮かべることが多いと思いますが、凝らなければ(というより大抵のグラフは) Go でも同じように描画できると思います。

最後までご覧いただきありがとうございました。
ソフトウェアエンジニアという人種（または私という一人）についてつらつらと思ふ

### ご挨拶
こんにちは、READYFOR でエンジニアをしています [@otaizo](https://qiita.com/otaizo) です。
この記事は [READYFORアドベントカレンダー2021](https://qiita.com/advent-calendar/2021/readyfor) 、1４日目の記事です。

### はじめに（前振り）
愚文を少々・・・
コンピュータソフトウェアをつくる人、いわゆる「ソフトウェアエンジニア」という職業について、およそ３５年の歳つきが流れました。
思い起こせば鼻腔を膨らませて意気揚々と“世界を変えてやるんだ”と勇ましく社会に出てから、僅かな成功と、その何倍もの挫折とを経ながら、私は今もソフトウェアをつくる人でいます。
その間には、今となっては、「革命」ともいえるような途轍もないソフトウェア界での変化もありました。インターネットとブラウザの登場はいま考えると「あぁ、あれはこういう未来をつくる事になる始まりだったのかぁ・・・」と幾分感傷的にさえ思うこともありました。

今日は、そんな私におこったソフトウェアの技術的なあれやこれやはそっと作業台の隅に寄せ置いて、エンジニアという、多分多くのそうでない人にとっては一種独特のものの考え方や生き方をしているように見える人種について、私なりに辿り着いた「思ふところ」をつらつらと書いてみたいと思います。
私ごときが大上段に「エンジニアとは、かくありき！」と言えるはずなどありませんので、これはあくまでも「私の思うところ」であり、それはすなわち私の経験/体験を省みた独りよがりな自己反省文の域を超えません。要するに「妄想」の類だと思われまので、その辺りのところは何卒ご容赦ください。
あまり、いえ、相当にこの場には場違いの文章になるのではないかと危惧していますが、いつもの年よりも激動度の高かったこの年の瀬のアドベンドカレンダー記事の中で、ちょっとした息抜きと思っていただけたらありがたいです。

### フンド ノ カワヲ ワタレ
私はいつも抗っていました。他の人が、ソフトウェア上でのひとつのアイデアをみんなの前で意気揚々と開陳したとき、チームでつくるソフトウェアに採用すべき技術的な方針を、数ある候補の中から民主主義的に選択しようとしたとき、ある問題を解くのに、すでに確立された「定理」ともいうべきアルゴリズムを疑うことなく使う実装を見たとき、などなどに接するたびに。抗うだけではなく、それは怒っていたようにも思います。時として口角泡をとばして知らず知らずのうちに大きな声で槍の先で突き刺すが如き勢いを持って対していたことしばしばでした。今となって思えば、「議論」や「論戦」というよりは、もうただ「攻撃ー打ちまかした方が勝ち」だけだったような気さえしています。もちろん,エンジニアですから、無謀無策に勢いだけで攻めることはありませんでした。何が「正しいのか？」、「どんな技術的バックグランドに裏打ちされているのか？」、「どうしてこちらではダメなのか？」、「それは古臭くないか？」、「それから何を学べるのか？」、といったことを懸命に調べ、それらを武器として「論破」することが良いことなんだ、という攻撃でした。でも、それが「エンジニア」という職業の「あるべき姿」だったと考えていたことは確かです。それが「切磋琢磨」だと思っていました。

### ショギョウ ハ ムジョウ
世は無常、ある時の真理は別の時にはそうではなくなる、いえ、ある時の真理は「そのときの精一杯の真理」であって、それより後の時には以前以上に精一杯度がパワーアップされたアップグレード版真理が颯爽とあわられる、ということを何度も目の当たりにしてきて、「あの時の自分は何をよりどころとしていたのだろう？」「私がよって立つ地盤はかくも変わりやすいものなのか」と感じるわけです。精神が不安定な時などは「穴があったら入りたい」と思うこともしばしばです。

私は、“エンジニアはサイエンティストではない”、と思うわけです。「技術者」であって「科学者」ではない、真理を追い求めてはいけない、と。少し前の時代でいう「職人」という言葉が一番よく本質を表しているような気がします。そこには「技（ワザ）」や「芸（ゲイ）」を極めるための「競争」や「論争」「切磋琢磨」、もしくは「伝承」や「共有」はあっても「何が正しいか」ということを争うのはちょっと身分違いな気がするのです。
真理を求めることをやってはいけない、というつもりは毛頭ありません。自分にとって「正しい」と思える基礎の上に「技術」が積み上げられていくわけで、その根っこの部分はむしろ熱い情熱を持って追い求めるに十分の価値と根拠があります。ただし、静かに追い求めるのが良いのでは、と思うわけです。
繰り返しますが、決して自虐視点ではありません。エンジニアとして私がみたい景色・世界は「物事の心理が解明された様」ではなく「技術が創り上げた役に立つ様」なんだと気がついた次第です。（なんと、ン十年もかかってかい！というのはサテ置いておいて・・・）

### オコラズ シカシ オコレ
そう、私は「誰かに」「何かに」、「外に向かって」怒っていました。その原因がそうした私の外にある「誰か」「何か」といった「自分ではないナニモノか」ででもあるかのように。
・・・・・違ったんですね。「慍る」という字があります。これも「おこる（もしくは「いかる」）」と読みます。「怒る」が外向きのベクトル（荒ぶった感情だったり、きつい言葉だったり）をビシビシ出しまくってある意味「責め立てる」様に使われるのに対して、「慍る」は、自分の内側に対して「怒り」の感情を向けること、今の状況を自分の責任のもとに責める様をいうそうです。技術を磨く道（＝エンジニアの道）において、他の人と違った、往々にして「自分には到底受け入れらない」様に思える考えだったり視点に接したとき、「それは間違っている！」とか「それは馬鹿げている！」と（正直に言えば、こんな直接的な言い方はしませんよ。たいがいは「それはどうかなぁ？」とか「賛成はできないなぁ」といったソフトな言い回しになりますが、奥底では結構辛辣だったりしてることがしばしばです）「怒る」のではなく「慍る」ことにしたい、と思うのです。

### 謝文（おわりにかえて）
やはり、取り止めのない、無価値な駄文になりました。ご容赦ください。
もし、ここまで読んでくださったとしたならば、感謝感激です。最大限の感謝をどう伝えて良いかわかりません。また、貴重なお時間を浪費させてしまったことを深くお詫びいたします。

明日は、我らが PDM [lauraさん](https://qiita.com/laura_ko)のお話ですので損はさせません！ お楽しみに
gethとjsonrpc-proxyを組み合わせて使う

## はじめに

Web3なDappを開発している時にローカル環境にEthereumノード([geth](https://geth.ethereum.org))を立ててテストすると思います。
ローカルのノード使うと割とサクサク開発できて、[Infura](https://infura.io/)とか[Alchemy](https://www.alchemy.com/)とかのリクエスト数も気にする必要がなくなるので、いい感じです。
EthereumノードとHTTPなJSON-RPCでやり取りする箇所をさらにキャッシュとかしてもっとキビキビ動かないかなぁと思ったので、[jsonrpc-proxy](https://github.com/tomusdrw/jsonrpc-proxy)を試してみることにしました。

ちなみにこのツールが存在することを知ったのは、お手伝いさせていただいてる会社のCTOが共有してくれたのがきっかけでして、この方Web3な知識から金融の知識、デザインまわりも出来て無敵な感じな人です。すごいなぁと尊敬してます。
おれもさらに頑張らねば。

ちょっと脱線しましたが、さっそく試してみます。

## 環境

Rust 1.57 stable
MacOSX 11.6 (Intel Mac)
Geth (Go Ethereum) 1.10.13-stable

## 準備

gethのインストールは省略。
他にたくさん記事ありそうなので。

jsonrpc-proxyのインストール。

```shell-session
$ git clone https://github.com/tomusdrw/jsonrpc-proxy.git
$ cd jsonrpc-proxy
$ cargo build --release
```

virtual manifestの複数プロジェクトっぽいので、ビルドだけして`./target/release/`配下に作成されたバイナリをそのまま使います。

`rpc-proxy`、`ethereum-proxy`、`substrate-proxy`があって、今回は`ethereum-proxy`を使う。
substrate用もあるのね。いいね。Substrate用もどこかで試したい。

## 使い方

geth起動は参考まで。

```
$ geth --config geth.conf --syncmode fast --http --http.corsdomain '*' --http.api personal,eth,net,web3 --ws --ws.api personal
,eth,net,web3
```

`geth.conf` は特に特別なことはしていません。

proxy->gethはWebsocketで接続するので、Websocketの設定が必須です。
ポートはデフォルトのままなので、それぞれ8546。

そして、jsonrpc-proxy起動してみます。

```shell-session
$ ./target/release/ethereum-proxy --http-cors all --websockets-origins all
[WS] Connecting to: "ws://127.0.0.1:9944/"
WS listening on 127.0.0.1:9945
[2021-12-14T11:26:13Z ERROR ws_upstream] WebSocketError: "IoError(Os { code: 61, kind: ConnectionRefused, message: \"Connection refused\" })"
HTTP listening on 127.0.0.1:9934
TCP listening on 127.0.0.1:9955
IPC listening at ./jsonrpc.ipc
```

CORS突破するために、 `--http-cors all`オプションと `--websockets-origins all`オプションを付与しています。

HTTP用のプロキシは`http://127.0.0.1:9934`に、Websocket用のプロキシは`ws://127.0.0.1:9945`にそれぞれアクセスすればOKです。

`WebSocketError` が表示されているのですが、これはupstream(geth)側のWebsocket用バインドアドレスがgethと不一致になってるので、エラー出力されています。
gethのWebsocket用のアドレスは`ws://127.0.0.1:8546`なので、 `--upstream-ws ws://127.0.0.1:8546`をオプションとして渡してあげるとエラーがでなくなります。
~~ただこの場合はどのように動作しているかがよくわからず :sweat_smile:
プロキシがHTTPもWebsocketもどちらも吸収してくれてたので、特に`--upstream-ws`オプションは指定しなくても問題ないはずです。~~

以下のjsonrpc-proxyリポジトリのイメージ図を見ると動作がわかりやすいと思うのですが、先に書いた通りproxyとupstreamの通信はWebsocketだけなので、このオプションを指定して、エラーがでないようにしておく必要があります。
<img src="https://raw.githubusercontent.com/tomusdrw/jsonrpc-proxy/master/overview.svg" />

あとはDappからWeb3なプロバイダを指定する箇所を`http://127.0.0.1:9934`や`ws://127.0.0.1:9945`にすればいい感じに動きます。

## おわりに

キャッシュコントロールとかもできるみたいなので、次回あればそのあたりも探ってみます。
ファイルの更新時間を使ってLivenessProbeを判定する

# 概要
Kubernetesでは稼働しているpodの稼働状態に応じて再起動やサービスアウトを行うヘルスチェック機能として[LivenessProbeとReadinessProbe](https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)があります。

これらのヘルスチェック条件は柔軟に設定する事が可能です。
http通信を受け付けるpodでは下記の様に`httpGet`を使ったヘルスチェック用のpathを使って判定する事が多いと思います。

```yaml
readinessProbe:
  httpGet:
    path: /health-readiness
    port: 80
  initialDelaySeconds: 60
  periodSeconds: 5
  timeoutSeconds: 4
  successThreshold: 1
  failureThreshold: 3
livenessProbe:
  httpGet:
    path: /health-liveness
    port: 80
  initialDelaySeconds: 60
  periodSeconds: 5
  timeoutSeconds: 4
  successThreshold: 1
  failureThreshold: 3
```

今回http通信を受け付けないpodを運用する事になり、ファイルの更新時間を使ったLivenessProbeの判定を行ったのでご紹介します。

# ヘルスチェックの要件
今回運用するpodは[KinesisDataStream](https://aws.amazon.com/jp/kinesis/data-streams/)に対してポーリングを行い、取得したキュー数に応じて処理を行うアプリケーションです。

このpodはキューを処理する際にpod内の特定ファイル(/tmp/healthcheck)を更新する挙動になっています。
システムの役割上ほぼ毎秒毎にキューが発生しているはずなので、2~3回ポーリングする時間内に特定ファイルが更新されていなければ該当のpodは正常に処理ができてない状態と判断する事ができます。
このような状態になったpodは再起動を行うことで新たに処理を開始する運用を想定していました。

まとめると以下の通りになります。

- ヘルスチェックエラー時の挙動が再起動なので`LivenessProbe`にて実装する
- `/tmp/healthcheck`の更新時間をヘルスチェック条件とする
- `/tmp/healthcheck`の更新時間はポーリング間隔の2~3倍以内でなければ異常とする

# 実装内容
上記のヘルスチェック要件を受けて下記の実装を行いました。

```yaml
livenessProbe:
  exec:
    command:
      - sh
      - -c
      - find /tmp/ -name "healthcheck" -mmin -`echo $POLLING_IDLE_TIME_BETWEEN_READS_MILLIS | awk '{print $1/60000*3}'` | grep /tmp/healthcheck
  initialDelaySeconds: 180
  periodSeconds: 5
```
※ポーリング間隔(ミリ秒)を変数`POLLING_IDLE_TIME_BETWEEN_READS_MILLIS`にて指定しています

ファイルの更新時間をチェックするために、今回はcommandを使ったシェルにおける確認方法を選択しました。

```shell
find /tmp/ -name "healthcheck" -mmin -`echo $POLLING_IDLE_TIME_BETWEEN_READS_MILLIS | awk '{print $1/60000*3}'`
```

こちらで特定ファイル(/tmp/health)の更新時間が既定値以内かをチェックしています。
findコマンドのmminオプションが分単位で`POLLING_IDLE_TIME_BETWEEN_READS_MILLIS`の指定がミリ秒単位なので`awk '{print $1/60000*3}'`にて単位を合わせています。

```shell
grep /tmp/healthcheck
```

commandでは実行したコマンドのリターンコードにて判定を行うので要件を満たしている場合はリターンコード0を、要件を満たしていない場合はリターンコード１となるために『要件を満たしたファイルの有無』にて判定を行う形にしました。

# 最後に
これまではAPIとなるpodの運用が多く、`httpGet`以外のLivenessProbe/ReadinessProbeの実装を行ったのは初めてでした。
複雑な処理を行う場合はコンテナ内にヘルスチェック用のスクリプトを設置してLivenessProbe/ReadinessProbeにて実行してリターンコードを取得する方が良いですが、シンプルな確認であれば今回の様にコマンド実行が便利でした。

本記事が誰かの助けになれば嬉しく思います。
iframe内でvuex-persistedstateを使ったページが動作しない場合がある件

本記事はパロニム Advent Calendar 2021の13日目の記事です。

# 概要

webStorage (localStorage, sessionStorage) を使うページが、シークレットウィンドウ(プライベートウィンドウ)内のiframeで読み込まれた時に、以下のような例外が出てしまって上手く動作しない場合があります。

**Chromeの場合**

`SecurityError Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document.`

**Safariの場合**

`SecurityError The operation is insecure.`

Vue + vuex + vuex-persistedstate で作っていたページでこの問題が起きていたので対応を考えます。

# 原因

いきなり結論ですが、ブラウザのプライバシー設定が関係しており、サードパーティのCookieをブロックする設定になっているときに発生します。

**Chrome**

>設定 → プライバシーとセキュリティ → Cookieと他のサイトデータ
「サードパーティーのCookieをブロックする」 or 「シークレットモードでサードパーティのCookieをブロックする」 or 「すべてのCookieをブロックする」

**Safari**
>設定 → プライバシーとセキュリティ
「すべてのCookieをブロック」

この設定が有効になっているとき、iframe内で `window.localStorage` を参照しただけで例外が出てしまいます。
( `sessionStorage` でも同様) 

特にChromeでは「シークレットモードでサードパーティのCookieをブロックする」がデフォルト設定になっているため、
シークレットウィンドウ(プライベートウィンドウ)のiframe内で発生するパターンが大半です。

vuex-persistedstate では永続化のためのストレージとしてデフォルトで localStorage が内部で利用されるため、そのままだと動作しなくなってしまいます。
永続化は無理だとしてもせめて例外によって動作が止まるのはなんとかしたいところです。

# 対応案

`window.localStorage` を参照出来るかチェックし、出来ない場合は localStorage を利用しないようにします。

初期化時オプションの `storage` で利用ストレージを指定出来るのでここでlocalStorageが利用出来ない場合はMapを使ったダミーに差し替えます。
もちろん永続化は出来ないものの、止まることは無くなります。


```javascript
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'
export default createStore({
  //...,
  plugins: [createPersistedState({
    storage: (() => {
      try {
        if (window.localStorage) {
          return window.localStorage
        }
      } catch (e) {
        console.log("localStorage is unavailable.")
      }

      const map = new Map();
      return {
        getItem: function (key) {
          const value = map.get(String(key))
          return typeof value !== 'undefined' ? value : null
        },
        setItem: function (key, value) {
          map.set(String(key), String(value))
        },
        removeItem: function (key) {
          map.delete(String(key))
        },
      }
    })()
  })]
})
```


ただ、localStorageが使える場合はシークレットウィンドウでもウィンドウを閉じるまでは書き込んだ内容が保持されるのに対し、このダミーはページ移動(リロード含む)で消えてしまうので全く同じとはならないので注意です。

要件上厳しいのであれば一番最初に明示的にユーザーに利用不可と伝えてエラーとしてしまうのも検討しましょう。

ここまで書いておいてなんですが、pluginsにcreatePersistedState()を渡さないようにするでも良いですね。

# 最後に

iframeでなければシークレットウィンドウでも問題が出なかったので気付くのが割と遅くなってしまいました、反省。
macOS (Mojave) でSubversionがビルドできないときの解決方法

# 概要
Homebrewを使用してSubversionをソースからビルドする際、SConstructのバグと思われる原因で失敗しました。今回は、SConstructのソースコードを一部変更することで解決しました。

# 環境
macOS 10.14.6 Mojave

# 現象
HomebrewでSubversionをビルドしてインストールしようとしたところ、「Sconstruct: `-rpath`の指定方法が違います」といった内容のエラーが出て、ビルドに失敗しました。

# 原因
エラーメッセージを調べたところ、GCCにランタイムライブラリのパス(`rpath`)を渡す際、GNU GCCでは`-L-rpath=`とするのに対し、mac版のGCCは`-L-rpath,`とする必要があることが分かりました。
Subversionのビルドに使用されているビルドシステム(SConstruct)のソースディレクトリ内で`-L-rpath=`をgrepしたところ、`-L-rpath=`という文字列が見つかりました。その行付近でプラットフォームによる条件分岐がされていなかったことから、SConstructがGCCに渡すパラメータの条件分岐漏れが原因でないかと考えられます。

# 解決方法
SConstructの実行環境がmac(`platform`が`darwin`)であれば、`-L-rpath,`という形式で`rpath`を渡すようにすればよいです。
SConstructはHomebrewでインストールされていましたので、今回の場合は、
`/usr/local/Cellar/scons/4.3.0/libexec/lib/python3.10/site-packages/SCons/Tool/gdc.py`の
`env['RPATHPREFIX'] = '-L-rpath='` を
`env['RPATHPREFIX'] = '-L-rpath,' if env['PLATFORM'] == 'darwin' else '-L-rpath='`
とすることで、無事にビルドができました。

# 補足
macOS Mojaveはすでに古いバージョンのため、Homebrewでビルド済みのバイナリが用意されなくなったFormulaeが増えています。バイナリがない際は、今回のように各自でビルドする必要があります。
log4jとは

## 勉強前イメージ

apacheくらいしかしらない・・・
脆弱性で話題

## 調査

### log4jとは

正式にはApache log4j といい、オープンソースのJavaプログラム用のロギングAPIになります。
プログラムが正しく動作してるかを確認するために、
規模が大きくなればデバッグツールやロギングツールを使ってプログラムの流れを確認します。
Apacheのサブプロジェクトで、Apache Loggingプロジェクトで開発が進められています。
元々はApache関係なかったですが合流することになったようです。
Java以外の言語でもサポートされているものはあり、PerlやRuby、pythonなどがあります。

### 脆弱性について

JavaにはJNDI(Java Naming and Directory Interface)という
アプリケーションがDNSや [LDAP](https://qiita.com/miyuki_samitani/items/6c5ad9b213f89979da17) を利用するためのライブラリがあります。
Log4jの機能としてJNDI Lookupがあり、それが悪用されています。
対象としてはインターネットに接続されているものすべてが対象になります。

脆弱性についてはJNDI Lookupの機能で、
ログとして記録された文字列を加工して出力する役割があり、
それを悪用すれば対象サービスに対して文字列を送るだけでプログラムをダウンロード・実行させることができます。
ログを読ませるだけなので認証等もありません。

サーバだけじゃなく、ネットワーク機器もLog4jのライブラリを使ってる可能性があるので確認が必要です。

## 勉強後イメージ


## 参考

- [オープンソースのログ管理／Apache log4jとは](https://www.ossnews.jp/oss_info/Apache_log4j)
- [【図解】Log4jの脆弱性 CVE-2021-44228 (Log4shell or LogJam) について](https://milestone-of-se.nesuke.com/sv-advanced/sv-security/cve-2021-44228-log4shell-logjam/)
個人開発SaaSをappsumoに出品してみた

# Intro
私はフリーランスエンジニアをしながら個人開発SaaSに取り組んでいる。
せっかくなので、海外ユーザにもサービスを使ってもらいたいと英語圏にFacebook広告をちまちまと打ったりしていた。
仕事のつながりで、そういう人（Bootstrapper,Indie Makerとか言うらしい）のために[appsumo](https://appsumo.com) というマーケットプレイスがあるのを聞いたので出品をしてみた記録である。

# 出品する個人開発サービス
**Servwatch**というサーバー監視SaaSを今回appsumoに出品することにする。

https://servwatch.net

海外ユーザー向けの対応として、以下を行った。

- LPの英語化
- APIドキュメントの英語化
- コンソールの英語化

この辺は、かなりしんどいのだが別の機会に書きたいと思う。

# Step1:アカウント作成
以下フォームより、アカウントを作成する。

https://appsumo.com/accounts/signup/?next=/partners/products/

今回は、Googleアカウント連携にて作成した。

# Step2:Partner PortalからProductを作成する
"List new procuts"から出品対象プロダクトを作成する
![スクリーンショット 2021-12-14 19.30.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212874/bf76fa27-f2fd-e652-46ed-b38a152d4f1d.png)

# Step3:商品説明等の入力
以下項目を埋めていく

- [x] Product basics:ランディングページのURLなど
![スクリーンショット 2021-12-14 19.32.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212874/5bc1fbb9-646f-9a78-f218-3564db9c999a.png)

- [x] Images:ヘッダー画像と商品説明を数枚程度アップロード
（ここで、適当なヘッダー画像をアップロードしたためにStep6にて再アップロードする羽目になる。ロゴ配置に[規定](https://sell.appsumo.com/l/image-guidelines)があるのでよく読んでおきましょう）
![スクリーンショット 2021-12-14 19.35.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212874/21ac1bd1-c3a2-8cd7-fe7d-8d531288b7c8.png)

- [x] Description:製品の説明コピーなど
（[コピーの書き方](https://sell.appsumo.com/l/copy-guidelines)を読んでおく）
![スクリーンショット 2021-12-14 19.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212874/eb5f7d59-1d9c-30db-0e91-034b790be37f.png)

- [x] Pricing:価格体系
今回は1年間使い切りで価格設定をした。
設定に際しては[プライシングについて](https://sell.appsumo.com/g/getting-paid)が参考になるので、読んでおく。
最後に"Review"ボタンを押して、appsumoのLaunch Ops Teamの承認プロセスに回す。

# Step4:Redeem Codeのアップロード
シェルスクリプトなどでとりあず100個のランダムコード生成して、csvファイルの拡張子をつけたファイルをアップロードした

:::note info
Redeem codeとは：appsumo経由で購入したサブスクリプションを有効化するための引き換えトークン
:::
![スクリーンショット 2021-12-14 19.38.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212874/0acc7f72-9d8a-c3d5-7d7f-96dc55fa91c9.png)

# Step5:Reviewを待つ。(12/2〜）
appsumoより「We’re reviewing your listing」の件名でメールが来る。
要約すると

> - 公開まで5~7営業日待て
> - 承認されない場合は、どうすればクリアできるかの詳細をメールするから心配するな
> - リラックス！

この後、appsumo担当者がServwatchのコンソールにログインしたりしてサービスの実態があるか・最低限の品質が担保されているかの確認をしていると想定される。

# Step6:修正事項についてのメール（12/10）
appsumoより「Action needed to complete your listing」の件名でメールが来た。
要約すると

> - ヘッダー画像についての規約を読んで、再アップロードせよ

メール中で案内されたテンプレートファイルを[canva.com](https://canva.com/)で加工してヘッダー画像を規約通り作成・アップロードしその旨メール返信した。

# Step7:自動返信メールあり（12/11）
公開まで5~7営業日待てと連絡あり。

# Step8:公開日決定メールあり（12/11）
先の自動返信メールより間をおかずメールが来た。
要約すると

> - 公開日は12/13 at 12pm CST（日本時間14日AM3時）を予定している
> - 自己紹介をpinned postとして作成しろ

# Step9:公開（12/14 AM3:00）
朝起きて、MarketPlaceを確認すると公開（="Buy Now”のボタンが押せる状態）されていた。
URLは以下のような形式で公開される。
https://appsumo.com/products/servwatch/

# Conclusion
こんな感じで12日ほどのリードタイムでappsumoへ個人開発SaaSを出品することができた！
appsumoのメール対応は迅速で、とても好感が持てた。
Self ListingプランなのでTwitterやらFacebookでセルフプロモートするのが大変だけど、グローバルにサービスを公開できる場を提供してもらっているのはありがたい。
個人開発して、販路開拓で困っているエンジニアの方・特に海外ユーザーに使ってほしいという方は出品する価値のあるサービスではないでしょうか。

（ちょっとだけ宣伝）
インフラエンジニアが作ったサーバー監視SaaS [Servwatch]をぜひ使ってみてください！

https://servwatch.net

[appsumo経由](https://appsumo.com/products/servwatch/)だと年間$99で利用できます。
JAVA SE取得を目指す：Day1

　テックキャンプからSESの会社に就職、さらに現場が決まったのはいいけど、現実って厳しい。
　いま働いているところは、正直開発とかプログラミングとは無縁の環境で、この現場で学べるところは一切ない状態。

　ぬくぬくと現場の環境に居ても、そのうち切られるかもしれないし、何より会社へ回せる金を増やしてお賃金も増やしたいので、自分を追い込むためにもJAVA SEを目指す日記をつけていきます。

今日やったこと
-------------
・参考サイトの確定
https://techgamelife.net/2021/02/14/java-silver-se-11/
ここの内容に沿って、進めていこうと思います、
明日の現場終了後に本屋へダッシュして購入しよう。

明日やること
---------
・買った本と1時間は向き合う

さて、どれくらいで行けるようになるかなー。
インフラ調査 ~データ分析編~

## はじめに
弊社でデータ分析の話もあり、AWSとかインフラまわりをちょこちょこと調べていて備忘録も兼ねてまとめてみた。

## AWS

https://aws.amazon.com/jp/redshift/

>データウェアハウスは、トランザクションシステムと基幹業務アプリケーションから取得したリレーショナルデータを分析するために最適化されたデータベースです。データ構造とスキーマの事前定義は、SQL クエリが高速になるように最適化されます。業務レポート作成や分析などには、通常、SQL クエリの結果が使用されるためです。データにはクリーニング、エンリッチメント、変換が実施され、信頼できる "単一の情報源" となるようにします。

https://aws.amazon.com/jp/big-data/datalakes-and-analytics/what-is-a-data-lake/

>データレイクでは、基幹業務アプリケーションからのリレーショナルデータに加えて、モバイルアプリケーション、IoT デバイス、ソーシャルメディアからの非リレーショナルデータも保存されます。データの構造やスキーマは、データをキャプチャした時点では定義されません。つまり、データの保存時には、慎重に設計する必要がなく、この先答えが必要になりそうな質問を把握しておく必要もありません。SQL クエリ、ビッグデータ分析、全文検索、リアルタイム分析、機械学習など、さまざまなタイプのデータ分析を使用し、インサイトを発見できます。


課題としては、
・データが溢れすぎて目的を失いがち。
・データのメンテナンスや整理整頓など定期的に管理が必要。
こんな声を見た。
たしかに目的を明確化しておかないと、ただただデータを持っているだけの沼になってしまいそう…。

あと、なんというか構築するのとかセキュリティ面とかいろいろ大変そう。
と、思ったらこんなのもあった。

https://aws.amazon.com/jp/lake-formation/?whats-new-cards.sort-by=item.additionalFields.postDateTime&whats-new-cards.sort-order=desc

金融サービス向けとかもある。なにがどう見えるのだろうか。

https://aws.amazon.com/jp/blogs/news/amazon-finspace-simplifies-data-management-and-analytics-for-financial-services/


>Amazon FinSpace は、履歴データでの作業も簡単にします。信用リスクを計算するモデルを構築したとしましょう。このモデルは利率とインフレ率に依存し、これらは頻繁に更新されます。顧客に関連付けられたリスクレベルは、インフレ率と利率が異なっていた数か月前のものであるため、現在は変わってしまっています。データアナリストがデータを現在の状態、および過去の状態で検証するモデリングは、bitemporal modeling と呼ばれます。Amazon FinSpace では、時間をさかのぼり、モデルが複数の次元に沿ってどのように進化しているかを比較することが容易になります。

おー！なるほど！なんだかおもしろそう！


他参考にしたもの。

https://qiita.com/manabian/items/e689daf5cc006eced737

https://qiita.com/hayao_k/items/906ac1fba9e239e08ae8

## 5G

だけど、大容量データとか重たい処理するときにちょっと気になるのがネットワークの問題…。

そういえば、先週くらいに「5Gで遠隔医療操作ができるようになる」というニュースを見た。
通信が早くリアルタイム性がある5Gだからこそ成せるのだとか。

https://www.itmedia.co.jp/news/articles/2107/27/news054.html

>「既にあるWi-Fiネットワークでもいいのでは」という見方も出てくるだろうが、大規模な工場などの本格的なデジタル化を実現する上では、やはり5Gが持つ高い性能、とりわけ低遅延や多数同時接続といった特徴が重要だという声が多いようだ。それに加えて5Gはもともと広域で利用が想定されており、複数の基地局を設置して広いエリアをカバーするのが得意なことから、大規模な工場や倉庫、さらには港湾など屋外の広いエリアで利用する上ではWi-Fiより優位性がある。

費用が高額とかまだまだネックになるところが多そうで、どこでもだれでもすぐに使えるわけではなさそう。ただ、ネットワークの問題が解消されたら、出来ること見れることがより増えそう。まずは、スマホで体験してみたい…。

## さいごに
本当はデータ分析に括らず、自動化ツールとかセキュリティとか気になるものをいろいろ調べていたが、時間の都合上そのまとめはまたの機会に…。

弊社では積極的に新しい技術を取り入れて、もっといいものを作っていこうと頑張っています。
物流、ロジテック、サービス連携、データプラットフォーム等に興味のある方、カジュアル面談もできますので、ぜひお話しできたらと思います。

https://www.wantedly.com/companies/company_3244501/projects
kintoneの日英中対応どこまでできる？～使ってみて気づいた落とし穴～

#kintoneの他言語対応って？
kintoneでは、標準機能として、アプリのフィールドなど、言語ごとの名称を設定することができます。
[言語ごとの名称を設定する](https://jp.cybozu.help/k/ja/id/040557.html)

設定言語はユーザーの「アカウント情報」から設定することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584477/9bd3c590-f7cc-3125-afeb-3d32be000c35.png)

最近は海外にも積極的に進出しているkintone。
便利だと思っていましたが、使ってみると意外と落とし穴がありました。

既に下記のようにQiitaで記事になっているものもありますが、
・@will-yama様 [バイリンガルなkintone使いの人たちへ](https://qiita.com/will-yama/items/662de9d8cb8f42343ef1)
記載以外の点についてもう少し書いてみようと思います。

#意外な落とし穴：設定編
## ①: すべてのフィールドには対応していない
特に、以下のフィールドが対応していないのが困るなと思いました
・「グループ」フィールド
　→　グループフィールドの中のフィールドは問題なく対応しています
・「数値」フィールドの**単位**
　→　グループフィールドは非表示にしてしまえばよいですが、こちらはそうはいかないので困りました…
・「ラベル」フィールド
　→　ラベルが対応していれば無理やりフィールド配置してみたいなこともできるのですが…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584477/2e0f6b67-c6b1-7fc8-1f95-95e6f48fa4b8.png)

## ②: アプリ名は対応しているが、スペース名は対応していない
・アプリの言語ごとの表示なので当たり前ではありますが、アプリ名を変えたのに、スペース名は日本語なのは
　結構表示としては目立ってしまいます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584477/85fed92f-6906-f68b-62e8-c8e3cf6262b3.png)

#意外な落とし穴：カスタマイズ編
## ①：レコード情報の値もログインユーザーの言語に束縛される
・各イベントのeventオブジェクトの中身など、値がログインユーザーの言語によって変わってしまうため、
　値によって処理を変更する場合、**日・英・中のすべての設定値で条件を判断する必要があり**、管理が大変です。
　HTMLでのドロップダウンの"value"などのように統一した値があったらいいなと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584477/f512ba7e-5e03-1c4a-74aa-89652e17b203.png)


#できるといいな：設定編
上記の落とし穴の他に、今後のアップデートでこれができると良いなと思いました。

## ①：言語による表示・非表示の選択
・例えば文字列(1行)フィールドなど、ユーザーの入力フィールドについて
　日・英・中で分けたいということがあるかもしれません。
　**英語の時だけ特定のフィールドを表示する**なんてことができればもっと自由度が上がるなと思いました。

## ②：言語ごとの一覧設定
・上記に近いですが、各言語用の一覧を設定できたらいいなと思いました。
　現状、文字列(1行)フィールドを一覧で対応するとすると、DOM操作しかない状況です。

## ③：各言語のプレビュー画面
・日本語で入力して、英語での表示を見たいと思った時、現状はユーザーの設定を変えない限りは
　見ることができないため、簡易的なプレビューが見れると非常に嬉しいと思っています。

#まとめ
今回は標準機能として、kintoneの他言語対応についてtipsを書いてみました。
海外のユーザーもどんどん増えていくと思うので、
今後のアップデートに期待したいと思います。
エンジニアのメンタルヘルスを維持する方法

始めにお伝えしますが、この記事には技術に関する内容は記載されていません。
某IT企業でエンジニアとして働く筆者が、エンジニアがメンタルを強く持って生き抜いていくためのライフハックを書き連ねています。

### エンジニアはそれなりに大変

- 基本エラーとの戦い
- 動いて当たり前だから、やっとこさ実装できても誰からも(特に他部署の人間)　感謝されない
- 納期のプレッシャー
- 上記のように大変な割に意外とそんな稼げない　（未経験からフリーランスで年収一千万を謳ってるプロスクにはくれぐれも騙されないようにしましょー）

などなど。

### エンジニアになる人の特徴
筆者の周りのエンジニアはこんな感じです。

- 絹豆腐メンタルを持った人（豆腐じゃなくて絹豆腐ね。ここ重要）
- HSP気質

元々繊細な気質を持つ彼らは、例えプログラミングが好きで非常に優秀であっても、エラーとの戦いで疲れ果てさらにメンタルを病むという悪循環に陥ってる気がします。

### メンタルを健康に保つには
メンタルを病んで精神科に行ってからでは遅いので、日頃からメンタルヘルス維持に向けて活動していくのがベストなのではないかと考えています。以下に書き出してみたので元気のない方は実践してみましょう〜

#### 運動編
- サイクリング

（筆者のように、）運動神経が悪い民でも楽しめます。チャリ持ってない方は赤チャリなど利用すれば30分165円で観光できます。ただ予期しない動作をするチャリもたまにあるので運ゲーではあります。新しめのチャリを選べば安全なはずです。

https://docomo-cycle.jp/

- 会社の近くの小学校や中学校の体育館を借りてバドミントンやバレーボールをする

利用登録さえすれば、ほぼ無料で利用できるケースが多いです。職場の人と行ってみてはいかがでしょうか。

- ボルダリング

これは上級者向けです。アンメルツヨコヨコもしくはバンテリンを持参しましょう。筆者と一緒に行ったエンジニアはボルダリングの翌日タイピングが出来なかったそうです。

- 山登り

これもやや上級者向けですが、高尾山くらいなら体力のない方でも楽しめるはずです。麓から登ったらちょっとキツかったですが笑

- ウォーキング

筆者はリモートの時は業務前と業務終わりにお散歩を20分くらいしています。

- キャンプ

陽キャ寄りのエンジニアももちろんいるでしょう。ということでキャンプも紹介します。
この前バイト先のデザイナーと営業の方とキャンプ場に行ってきました。（社員さん仲良すぎんか）
ザ・非日常感という感じがして良きでした。
陰キャの私でも楽しめたので、一緒に行く人達の仲さえ良ければ楽しめるかと。


運動したら質の良い睡眠が取れます。質の良い睡眠は効率の良いコーディングにつながります。エンジニアのの方は特に運動不足になりがちなので、積極的にエクササイズしていきましょう！


#### メンタルケア編
- 好きなこと100個書き出す

これ楽しいですよ〜。自分の大切な軸とか見えてくるので就活にも役立つと思いました。

- 自然を見にいく。
この前、友人のエンジニアと一緒に埼玉にあるムーミンバレーパークに行ってきました。湖が広くて綺麗でした。普段ブルーライトを浴びまくっている我々の目に優しい景色が広がっていました。
![IMG_6208.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677833/468ce334-8fe7-21bd-22ec-5653bc3ea871.jpeg)
![IMG_6212.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677833/2517fd98-559b-f5fd-bc9c-ee1751ce389f.jpeg)



### 最後に
エンジニアにはみんな優しくしあげて
技術本を読むのが苦手だった私が楽しくなった方法

話題の技術本を手にとって
面白そうだと思ったまま
読みきらない事はないだろうか。

私は多々あった。
ざっと読んで理解はしていないがなんとなく使えるようになったのでいいかと思ったり、わからないから読むのをやめてしまったり。

そんな私が本の隅から隅まで、まるでファンタジー小説のように楽しく読める方法に気付いた。

それは、著者、訳者と実際に会話することだ。
すると今まで難解だった技術本が途端に彼らの伝記の一部になる。
登場人物に命が吹き込まれ、彼らが世界をより良くしようとした物がその本だと思える。

実際に会うのが難しい場合はwikiなどで生い立ちを辿るのもいい。
自分の中で彼らのキャラクターを確立してしまうのだ。

ぜひ試してみてほしい。
Twitter API キーとトークンがやっと取得できたよ

pythonで感情分析するのにtwitter apiを利用しようとして審査も通ったはずなのに、謎にAPIキーとトークンが取得できない日々が約半年以上ありました。

が、ついに昨日(12月13日)に<twitterdeveloper@twitter.com>から以下のメールが届きました。

```
Are you ready to kickstart your build using the Twitter API?
Now is the perfect time to check back in with your developer account to start a project, monitor your usage, find technical support, and see what's new.
```

開いたら以下の通りAPIキーとトークンがもらえました（今更感）
![スクリーンショット 2021-12-14 19.16.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677833/71565bf5-b737-fe7f-8b67-178f82b44e24.png)
Jacksonでenumをシリアライズ／デシリアライズするときの注意点

#2.11以降では、enum（列挙型）の変換に`@JsonCreator`は必ずしも必要ではなくなった

https://github.com/FasterXML/jackson-module-kotlin/issues/336

下記のようにenumに独自の値を持たせているのであれば、そのフィールドに`@JsonValue`をつければシリアライズ、デシアライズともに取り計らってくれるようになったようです。

```java:TestEnum.java
public enum TestEnum {
    TEST_A("testA"),TEST_B("testB");

    TestEnum(String label) {
        this.label = label;
    }

    @JsonValue
    @Getter
    private String label;

    // このメソッドに@JsonCreatorは不要
    public static TestEnum of(String label) {
        for (var instance : values()) {
            if (instance.label.contentEquals(label))
                return instance;
        }
        throw new IllegalArgumentException();
    }

}
```
下記のような列挙子のみの場合は、`@JsonCreator(mode = Mode.DELEGATING)`をつけるように、と説明があります。

```java:TestEnum2.java
public enum TestEnum2 {
    TEST_A,TEST_B;

    @JsonCreator(mode = Mode.DELEGATING)
    public static TestEnum2 of(String name) {
        for (var instance : values()) {
            if (instance.name().contentEquals(name))
                return instance;
        }
        throw new IllegalArgumentException();
    }

}
```

enumに対して`@JsonCreator`をmode=Mode.DEFAULTで使用すると"Input mismatch reading enum"というエラーメッセージを受け取ってしまいます。内部の型推測のルールが変更されたことが原因のようです。`@JsonCreator`の引数に`@JsonProperty`を使用する場合は、明示的にmode=Mode.PROPERTIESを指定する必要があるようです。

また、不変オブジェクトのデシアライズに関しては、下記も参考になると思います。
https://www.baeldung.com/jackson-deserialize-immutable-objects
Goで使用しない戻り値、引数は無視しよう

### 戻り値を無視する
この方法は結構使いますね。
関数から受け取った戻り値を使用せず放置すると、コンパイルエラーになってしまいます。

```go
func main() {
	s, ok := plus(1, 2)
	println(s)
}
```
ビルドしてみると

```go
./main.go:4:5: ok declared but not used
```

このような時に、使用しない戻り値を無視すると、問題なくビルドが通るようになります。

```go
func main() {
	s, _ := plus(1, 2)
	println(s) // => 3
}
```

## パラメータを無視する
戻り値を「\_」を使って無視するのと同様に、関数の引数も「\_」を使って無視することができます。

```go
func doSome(_ int, _ int) int {
	return 100
}
doSome(1, 2) // => 100
```

では、一見無駄なように見えるこの機能は、どのようなケースで使われるのでしょうか。

実は、Goには「インターフェース」という機能があり、特定のインターフェースに属する型は、そのインターフェースに定義されているすべての関数群（メソッド）を実装しないといけません。
しかし、定義上では引数を受け取ったものの、実装上は一部の変数が不要な場合があります。
不要な引数を受け取ったからといって、コンパイルエラーになるとか、実害が発生わけではありません。
ここでは、**あえて不要な引数に変数を割り当てない**ことで、**実装上必要としないことを明示できる**効果があります。

```go
// インターフェース型I
type I interface {
	// 引数が二つ必要な定義
	plus(int, int) int
}

// 型Tの定義
type T struct {

}

func (t *T) plus(a int, _ int) int {
	// 実装では引数を一つしか使用しないので、２番めは無視
	return a
}
```
MacBookでReactNativeの開発環境を整える

# 環境
MacBook air m1チップ
macOS Monterey 12.0.1

# nodejsのインストールの前に
nvsをインストール。
nodeのバージョンを管理してくれるやつ。
下記からmac用のインストールコマンドをコピペ。

https://github.com/jasongin/nvs

# Macの場合PATHを通す必要がある
Macの場合、nvsやnodejsをインストールしたあと、ターミナルを削除してまた起動したあと、
nvs --versionのようにnvsコマンドが使えなくなる。
これはPATHが通ってないために起こる。
そこで、自分のアカウントの直下に.zshrcというファイルがあるので、そこに下記を追加しておく。

```
export NVS_HOME="$HOME/.nvs"
[ -s "$NVS_HOME/nvs.sh" ] && . "$NVS_HOME/nvs.sh"
nvs use 16.13.0
```

# Jestを導入する

```
npm install --save-dev jest
```

pacage.jsonのscriptに下記を追加する。

```
"test": "jest"
```

テストしたいときは、

```
npm test
```
でテストが走る。

参考にした記事。

https://qiita.com/wafuwafu13/items/9fb6bf09c1a181517352#jest%E3%81%AE%E5%B0%8E%E5%85%A5

# Enzymeを導入する。
EnzymeはReactコンポーネントをレンダリングするためのライブラリ。

# 実際にテストしてみる

## 関数のテスト
sum.jsファイルを作成する。

```js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
```

sum.test.jsファイルを作成する。

```js
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
```

```
npm test
```
を実行すると、テスト結果が表示される。

![スクリーンショット 2021-12-12 21.41.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2284107/ae0c63c5-da08-90b5-12fb-0ea58a6cfc6b.png)

## スナップショットテスト
UIのテスト。予期しないUIの変更をテストすることができる。

https://jestjs.io/ja/docs/tutorial-react-native

算術関数や書式変換関数のテストなどはイメージつくけど、スナップショットのテストの重要さはいまだによくわかっていない。
素のLaravel に TailwindCSS v3系をインストールするメモ

TailwindCSS の新しいバージョン（v3系）がリリースされました。早速、素の Laravel に Tailwind の v3 系をターミナル経由でインストールした際のコマンドをメモ。

> **注意**：今回は、素の Laravel をインストールして Tailwind をインストールする手順を紹介しています。例えば、認証パッケージで有名な「Breeze」や「Jetstream」をインストールした際は、自動的に Tailwind のスカフォールドが入っていますので、わざわざTailwindをインストールするためのコマンドを打つ必要がありません。

## 1, 素の Laravel をインストールして、プロジェクトルートに移動

```terminal
// Laravelをインストール
laravel new project_name

// プロジェクトルートに移動
cd project_name
```

## 2, Tailwind を npm 経由でインストール

```terminal
// 最初に Tailwind のインストール
npm install -D tailwindcss postcss autoprefixer

// 次に Laravel の開発に必要なパッケージのインストール
npm install
```

## 3, 設定ファイルの「tailwind.config.js」を作成

```terminal
npx tailwindcss init
```

## 4, LaravelMix の構成を編集

プロジェクトルート直下の `webpack.mix.js` を編集して、PostCSS のプライグインとして Tailwind を取り入れます。

```diff:./webpack.mix.js
mix.js("resources/js/app.js", "public/js")
  .postCss("resources/css/app.css", "public/css", [
+   require("tailwindcss"),
  ]);
```

## 5, テンプレートパスを書き込む

プロジェクトルート直下の `tailwind.config.js` を編集して、Tailwind のコンパイル先を指定します。

```diff:./tailwind.config.js
module.exports = {
+ content: [
+   "./resources/**/*.blade.php",
+   "./resources/**/*.js",
+   "./resources/**/*.vue",
+ ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

## 6, Tailwind のディレクティブを CSS に追加する

`./resources/css/app.css` を開いて、Tailwind の各々のディレクティブを追加して、Tailwind の機能を取り入れます。

```css:./resources/css/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

## 7, ビルドプロセスを開始するためのコマンドを打つ

```
npm run watch
```

## 8, bladeファイルを保存し、結果をブラウザ上で確認する

⑤で設定したテンプレートパス内の対象のファイルを保存すると、自動的にコンパイルが開始されます。

例として、`/resources/view/welcome.blade.php` を以下のようにまるごと変更して保存しましょう。そして、保存した直後にコンパイルが正常に実行されているか確認してみましょう。

```html:./resources/view/welcome/blade.php
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
  <h1 class="text-3xl font-bold underline">
    Tailwind を始めよう
  </h1>
</body>
</html>
```

こんな感じで Tailwind v3 系のインストールが完了しました。v3 系になって、さらにパワーアップした Tailwind を enjoy していただければ幸いです(=ﾟωﾟ)ﾉ
逆求人のメリットとデメリット

こんにちは。井関です。


大学3年生（2021年）になってから就職活動に積極的に参加し始めました。
<b>その際に、とても役立ったのが、逆求人のイベント参加です。</b>


この記事は、
<b>
・逆求人ってなに？　
・逆求人に参加してみたいけど不安
</b>

などと思っている就活生に役に立てればいいなと思っています。
### ※エンジニアの逆求人について話していきます。


#逆求人とは
<b>
「逆求人」とは、学生や求職者が逆求人サイトなどに「自分の強み」や「経験」といったプロフィールを掲載し、それを見た企業側が学生や求職者に直接アプローチを行う、という新たな採用活動のスタイルを指す。</b>
<b>
特に新卒採用において、これまで一般的だった、企業が出した求人募集に対して学生が応募するという形式とは反対に、学生側が提示する情報に対して企業がアプローチし、求人条件にマッチする人材を探すことができる手法として活用されている。企業と学生、双方の利害が一致した新しい就活スタイルといえるだろう。特に、規模が大きくはないが積極採用に取り組んでいる企業側や、企業の知名度や規模にはこだわらず、自身が活躍できる場を就職先として探している学生側にメリットがある手段だ。
</b>


だそうです。引用元はこちら

https://www.hrpro.co.jp/series_detail.php?t_no=2266



#メリット

####企業について詳しく知れる。
 僕が参加したものが初めに企業プレゼンがあるので、「この会社こんな技術を使っているのか！」や「プロダクトがとても興味深い」と感じることができました！詳しく聴きたかったら、個人面談で聞けるのでより理解力が高まります!

####特別の選考などの紹介をしてもらえる
面談の評価や会社との相性が良かった場合、まれに特別選考を紹介してもらえることがあります。僕も何度かありました。

####プレゼンの練習ができる
僕が参加した逆求人は５分ほどの自己紹介プレゼンの時間があったため、内容や伝え方を学生のうちから経験できます。

####面接の練習ができる
プレゼンについての質問などがあることが多いので、面接の練習になりました！
どう答えたらいいかなどの経験ができます。

####学歴があまり関係ない？？
僕が通っている大学は、そこまで偏差値が高くないかつ文系大学生ですが、しっかりと面談ができています。
これは、普通に嬉しかったです笑



#デメリット

####拘束時間が長い。
数多くの企業が参加し、場合によっては８社連続で面談を行うので、1日を使ったりします。その日は予定を入れ内容がいいと思います。（単純に疲れるので、、、）

####心が折れやすい。

プレゼンの質問などでうまく答えられなくて、しっかりと伝えられないといったことがあったため、僕は何度も心が折れました笑

####プレゼンの準備が必要なケースが多い
僕が参加した逆求人は５分ほどの自己紹介プレゼンの時間があるため、そのためのプレゼンを作成する必要があります。



#まとめ

まだひよっこ就活生ですが、企業を調べていく上で逆求人はとても手っ取り早いと思いました。




#参加経験のある逆求人一覧

参加した逆求人のエージェントを紹介します。2つだけですが、複数回の参加経験があります。

###athletics

https://athletix.run/


###サポーターズ

https://talent.supporterz.jp/events/　


詳しくは登録して聞いてみるといいと思います！！
しかし、逆求人に参加するためには、スキルチェックやある程度の開発物があると思うので、頑張ってください。



#ちょっとした紹介

調子乗って投稿しましたが、
開発物が無かったり、チーム開発の経験がしたい方はどうでしょうか？？？
簡単なFlutterアプリを開発できたらいいなと思っています。

https://youtrust.jp/posts/1e623d75d95e96713965056699fa6f91?utm_source=twitter&utm_medium=social&utm_campaign=share_post





お疲れ様でした。
【SwiftUI】List項目のタップに余白を含める

#概要
SwiftUIのList項目にGestureを追加した際に余白を含める方法です。
NavigationLinkであればList項目全体が反応しますが、
単純にテキストなどを並べた際は該当テキストの位置しか反応しません。
こう言った場合に項目全体を含める方法です。

#コード
```swift
List {
    HStack {
        Text("")
        Spacer()
    }
    .contentShape(Rectangle())
    .onTapGesture {

    }
}
```

#解説
まずList項目をHStackで囲いSpacerも加えます。これでList項目はテキストだけでなく列全体になります。
しかしSwiftUIはしっかりとコンテンツと余白を区別して除外してくれるのでまだタップできません。

次にcontentShapeにRectangleを指定します。
これでList項目の長方形がコンテンツと認識される為、余白の位置までタップが有効になります。

#対応が必要な理由
例えば、通常はNavigationLinkが表示されており、
EditModeがtrueの際にはテキストが表示されタップするとSheetが開く
と言ったViewを作った際にEditModeのON/OFFでタップ可能な範囲が変わると不便です。

また、文字数が少ない場合はタップしにくくなりますし、
空文字を許容するとタップ不可になります。

#ブログ紹介
ブログには実用例も含めて詳しく解説しています。
興味があればご覧ください。

https://thwork.net/2021/12/14/swiftui_list_ontap_spacer/
タイトル付きCSVを一行づつJSONで取り出す。

# ちょ、予告なしに列順変えてくるんじゃねぇよ！

なんか突然ある日項目が増えて列の参照が狂うみたいな、そんな困ったCSVを送ってくるクライアントをお持ちのあなた！　そうあなた！
これを使えばもう安心です（おおよそな

ただしタイトル行があるのが前提。
無かったら・・・（目を反らす

ていうか今どきCSVとかつｋ（

## コード

```python
import csv

class CSVobj:

    def __init__(self, filename):
        self.filename = filename

    def read(self, enc="utf-8"):
        with open(self.filename, "r", encoding=enc) as fr:
            reader = csv.reader(fr)
            for idx, row in enumerate(reader):
                if len(row) > 0:

                    # 最初の行はタイトル
                    if idx == 0:
                        titles = row

                    # {タイトル：データ}でdictにしてyield
                    else:
                        buf = {}
                        for tidx, title in enumerate(titles):
                            buf[title] = row[tidx]

                        yield buf

    def __enter__(self):
        return self

    def __exit__(self, enctype, excvalue, traceback):
        pass

if __name__ == "__main__":
    with CSVobj("imadoki.csv") as cobj:
        for row in cobj.read():
            print(">>" , row)
```

## 使い方

JSONというかdict型で一行づつ取り出します。

mainのところを見てもらえばそのままです。
これで数万行あるわけのわからないCSVを毎回毎回送ってこられた上に列順が変わっても対処可能！

さあ、負けるな！
TavernでStageごとの設定を使い分ける方法

## Tavernとは
YamlでAPIの自動テストがかけるテストフレームワークです
https://tavern.readthedocs.io/en/latest/index.html

他の方が丁寧な説明記事を投稿されていますので、Tavern自体の説明はそちらをご覧になって頂くのがよろしいかと

+ [Tavernを使ってみよう！](https://qiita.com/Fujita234/items/9b618a0f7978c0728862)
+ [YAMLとpytestで動くAPIテストフレームワーク「Tavern」でWebAPIの自動テストが簡単に書けた](https://qiita.com/yuji_saito/items/f9d9f9e26d0a46905f4b)
+ [API Test ライブラリ Tavern のご紹介](https://user-first.ikyu.co.jp/entry/2019/05/07/110000)

## 共有したいこと
Stageごとにエンドポイントを切り替える方法がなかなか見つからなかったので、その方法をシェアします

[公式ドキュメントにヒント](https://tavern.readthedocs.io/en/latest/basics.html#multiple-global-configuration-files)が載っていたので、そのまま使いました
testファイルのディレクトリ構成は下のようになっています

```
tests/
├── __init__.py
├── test_dev.sh
├── test_api.tavern.yaml
└── values
    ├── __init__.py
    ├── dev_values.yaml
    └── common.yaml
```
test_api.tavern.yamlがtest対象のファイルで、
dev_values.yamlに開発環境に依存するデータを
common.yamlに共通に使うデータを設定しています

テスト実行時は下のように--tavern-global-cfgに両方のファイルを指定しています
コマンドが長いので、test_dev.shに下のコマンドを書き込んで、テストの実行時は、test_dev.shを実行しています

```test_dev.sh
#!/bin/bash
tavern-ci --tavern-global-cfg tests/values/dev_values.yaml tests/values/common.yaml
```

今後開発が進むと、stg_values.yamlとかが増え、そのファイルを指定したtest_stg.shが増えていく予定です

なお、test_api.tavern.yaml内に**!include**で共通ファイルを指定して、ステージごとのファイルを--tavern-global-cfgに設定した場合は、エラーになってしまいました
[Using the run() function](https://tavern.readthedocs.io/en/latest/basics.html#using-the-run-function)も試しましたが、tavernのテストファイルのアドレス解決が素直に出来なかったので、諦めました

## 終わりに
目的は達成できたのですが、もっとスマートな実現方法もありそうな気がしています
良い方法がありましたら、教えて頂けると嬉しく思います
FTRの対応のためにログのセキュリティを強化した話

今回はAWS FTRを受けた時の内容の中から
ログ保存の対応についてを記載しようかなーと思います。

そもそもFTRとは？については公式ページ読んでね
https://aws.amazon.com/jp/partners/foundational-technical-review/

#私の立ち位置
システムのインフラ運用に携わりつつ、
内部統制についての監査を担当している人
（ITわからないなりにAWSを勉強中）

#そも、ログって大切だよね？
みんなわかっていると思うけれどログの保存についてはなんとなーくで実施している人も多いのでは・・・
大切・・・なのはわかるけど・・・
とりあえず保存しておけばいいでしょ感ないですかね。
実際私もそう思っていた時期がありました（ありました）

#で、ログの保存で必要なことは？
①必要なログを出力させること
　超大前提。
　今回はAWSのCloudTrailのログを対象としていたのですでに出力されていたけれど、
　ほかにも必要な場合はまず出力させるところから！

②ログにアクセスさせる人を限定すること
　今回の対応範囲。
　改ざん・削除される可能性を考えて、
　システムの運用からは離した人を設定しておくのがベスト( *´艸｀)

③ログの保護
　今回の対応範囲。
　ログを改ざんされたときとかのためにバージョニングとか管理してねって話。

④ログの整合性
　今回の対応範囲。
　ログファイルが変更されなかったかの判断のために検証機能をonにしておけよって話。
　検証でだめ(乂'ω')って出たらアラートが発火するとかすればさらに良い٩( 'ω' )و


#結局、どうやったの？

###①ログ保存のための専用アカウントを作成する
提供しているシステムを動かしているアカウントとは別の完全に切り離したログ用アカウントを作成。
そこに、ログ管理者用の専用IAMを作成（今回の場合は２人分）した。
ログを閲覧したい場合もあるじゃん！という要望にお応えして、S3へのRead onlyロールを作成。（最小権限の原則）
**※ここで設定したログ管理者はシステムを動かしているアカウントの管理者とは全く別にしているのがポイント！**
　**権限を分散することで職務分掌を図っているのです**
![FTR_log_s3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/859223/b5f0c284-8ae6-b067-d289-fbc23f69875f.png)

　

###②ログ管理者がログ専用S3バケットを作成する
1.（まあほかにできる人いないんですけど）ログ管理者がログ保存用のバケットを作成。
2. 名前はお好きに。
　（と言いつつ変な命名にすると後ろから刺されるので、格納されているログが名前から判別できるようにしておきましょう）
3. バケットのバージョニングを有効に。（意図しない変更があっても戻せるように）
4. バケットポリシーを設定。
　①各アカウントからのPut許可（ないと死ぬ）
　②Readonlyのロールからのアクセス許可（ないと管理者しか見れない）
　③その他必要なアクセス許可
5. ライフサイクルルールを有効に。（設定期間が短すぎると監査とかで死ねるので注意）

###③ログ出力先の設定を変更する
各アカウントにログインして変更可能なIAMまたはロールでCloudTrailの出力先を作成したバケットに変更する。
変更の際に「ログファイルの検証」の有効化も忘れずに！


###④ログ出力の確認
適当なRead操作をしてログが設定したS3バケットに出力されていることを確認する。

完成！
おわり！
解散！

#まとめ的な感想的な

設定自体はやると決めたら案外すぐできてしまうわりにFTRに対応しているぜ( ･´ｰ･｀)ﾄﾞﾔ ができるので、普通におすすめです。
ログは後から操作内容が追える唯一の手段といっても過言ではありません。超大切です。
さらっとできるこのようなセキュリティ強化もしてみてはいかがでしょうか？
AWS Lambda->SNS>(chatbot)->Slack連携ができずに悩む

CloudWatchでサブスクリプションフィルタをLambda連携で作って、Lambdaで適当なsns.publishコードを書いて、Chatbotと連携済みのSNSトピックを指定してもSlackに通知されない。エラーは出てない。切り分けるとメールは送れる。Lambdaでコードの書き方に何か見落としがあるのか? アクセス権限が不足なのか? 意味が分からず色々試したり調べたりしているとクラスメソッドさんの天の声が.. (記事元はAWSのリファレンスではありますが)

https://dev.classmethod.jp/articles/tsnote-support-chatbot-launch-001/
Amazon SNS から AWS Chatbot を経由した通知がうまくいかない原因と対処方法

> Q: AWS Chatbot を使用して任意の通知を受け取ることができますか? いいえ、AWS Chatbot はドキュメントに記載されているサービスからの通知のみをサポートしています。サポート対象外のソースからのイベントはチャットルームに配信されません。

多分これだ..
今回でいうとイベントソースはLambdaとCloudWatchのサブスクリプションフィルタのどっちかになるんだろうが..
どっちも非サポート。
その前提で、別のやり方を試してみよう。