OmniAICreator/Qiita-1.07M huggingface dataset:
ECS-CLIを使用してFargateタスクのクラスターを作成する

# 前提条件
・AWSアカウントをセットアップしてある
・タスク実行IAMロールを作成してある (ECSコンテナエージェントはユーザに変わってAWS APIを呼び出すのでIAMポリシーとロールが必要
・Amazon ECS CLIのインストールしてある
・AWS CLIをインストールして設定してある


# クラスター設定
ECS CLIで使用するクラスター名、リージョン、リソース作成プレフィックスを定義してクラスター設定を作成します。

```
ecs-cli configure --cluster クラスター名 --default-launch-type FARGATE --config-name チュートリアル  --region  ap-northeast-1
```

# クラスターとセキュリティを作成
### 1. ecs-cli up コマンドでECSクラスタを作成
このコマンドの出力に作成されたVPCとサブネットIDは後で使うのでメモっておく。

```--vpc VPC_ID```で既存のVPCを指定することができます。
その場合、そのVPC内の既存サブネットのリストを```--subnets```で指定する必要があります。


```
ecs-cli up --capability-iam --cluster-config クラスター名
```

### 2. AWS CLIを使用してVPCのデフォルトのセキュリティグループIDを取得する
前の出力のVPC IDを使用する
このコマンドの出力には次のステップで使用するセキュリティグループIDが含まれる

```
aws ec2 describe-security-groups --filters Name=vpc-id,Values=VPC_ID --region ap-northeast-1
```

### 3. AWS CLIを使ってセキュリティグループルールを追加し、ポート80でインバウンドアクセスを許可する

```
aws ec2 authorize-security-group-ingress --group-id SECURITY_GROUP_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 --region ap-northeast-1
```
# 構成ファイルを作る
デプロイ用の`docker-compose.yml`を作成
ここでは例としてWEBアプリケーションを作成する`docker-compose.yml`を作成します。
ECS CLIではバージョン1, 2, 3がサポートされています。ここでは３を使用する。

```docker-compose.yml
version: '3'
services:
    web:
        image: イメージ
        ports:
            - '80:80'
        logging:
          driver: awslogs
          options:
            awslogs-group: test
            awslogs-region: ap-northeast-1
            awslogs-stream-prefix: web
```

### ECS固有のパラメータを`ecs-params.yml`を作成する
さっきメモったサブネット、セキュリティグループIDを書き込む

```ecs-params.yml
version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet ID 1"
        - "subnet ID 2"
      security_groups:
        - "security group ID"
      assign_public_ip: ENABLED
```

# クラスターに設定ファイルをデプロイする
`ecs-cli compose service up` でクラスターにデプロイする。デフォルトは現在ディレクトリで
`docker-compose.yml`と`ecs-params.yml`を検索します。`--file`オプションを使用して別構成ファイルを指定することや、`--ecs-params`オプションで別のECS Paramsファイルを指定することもできます。

```
ecs-cli compose --project-name タスク名 service up --create-log-groups ロググループ名 --cluster-config クラスター名
```
golangの名前付き戻り値に関して

名前付き変数は[tour of go](https://tour.golang.org/basics/7)で紹介されているように、関数の最初で変数宣言したのと同様になる。

この時、nameの変数のスコープは`fugafuga()`に対して関数渡しで渡した関数から参照できるため、下記コードは問題なく動く。


```go
package main

import "fmt"

func fugafuga(f func() error, logger func(err error)) error {
	f()
	return nil
}

func hogehoge() (name string, err error) {
	fugafuga(func() error {
		name = "test"
		return nil
	},
		func(err error) {
		})
	return
}

func main() {
	n, _ := hogehoge()
	fmt.Println(n)
}
```

出力結果

```console
$ go run main.go
test
```
QualityForward APIを使ってテストスイートを作成する際の注意点

[QualityForward](http://bit.ly/2U1vjOA)はクラウドベースのテスト管理サービスです。APIを公開しており、テスト管理に関するデータのCRUD操作ができるようになっています。テストケースであったり、それらをまとめたテストスイートなどを作成できます。

今回、APIを使うためのSDKを開発していてハマったポイントについて、メモしておきます。

## テストスイートが表示されない

テストスイートは次のAPI操作で作成できます。

```
POST /api/v2/test_suites.json
```

そして、作成はできるのですが、なぜか一覧には出ません。Node.js SDKで書くと、次のようなコードです。

```js
const count = testSuites.length;
const testSuite: TestSuite = client.TestSuite();
testSuite.name = 'APIから作成したテストスイート';
testSuite.project_id = 748;
testSuite.label_category1 = '機能カテゴリ';
testSuite.use_category1 = true;
testSuite.label_content1 = '環境';
testSuite.use_content1 = true;
testSuite.coverage_panel_column = null;
await testSuite.save();
```

## Web上での操作を真似る

そこでWeb上の管理画面での操作を見てみたところ、テストスイートを作成すると同時にテストスイートバージョンが作成されていることが分かりました。テストスイートバージョンはテストスイートをバージョン管理し、同じテストケースを繰り返しテストするための仕組みです。

つまりテストスイートを作成しただけでは十分ではなく、同時にテストスイートバージョンも作成する必要がありました。コードとしては、下記を追加します。

```js
// テストスイートバージョンの作成
const tsv = testSuite.version();
tsv.name = 'バージョン1';
// テストスイートにセット
testSuite.setVersion(tsv);
// そしてテストスイートを保存
await testSuite.save();
```

こうしてあげることで、テストスイート一覧にAPIから作成したテストスイートが表示されるようになりました。

<img width="1548" alt="Screenshot_ 2020-01-22 16.09.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/1de99f51-b508-0602-ef58-60f17e6b2d92.png">

## まとめ

APIドキュメントには、まだアンドキュメントな部分が多く、手探りが必要な部分が多々あります。SDKは現在、Node.js / Python / Ruby / Google Apps Script版を開発しています。SDKから利用することで、なるべくアンドキュメントなところをエラーで返すようにしていきますので、ぜひご利用ください。

[QualityForward](http://bit.ly/2U1vjOA)
ファイルのPOST時にPHPで拡張子の偽装チェックを行う

こんにちは！

クライアントサイドよりPOSTされたファイルに対して、ディレクトリトラバーサル対策等の他にも着目すべき点はありますが、今回は「クライアントサイドのフォームでPOSTしたファイルのバリデーション」のうち、「ファイルの拡張子の偽装チェック」を行うことに着目します！

## そもそも拡張子の偽装とは何なのか

```shell

mv sample.png sample.jpg
```

上記例のように内部バイナリのコンバート処理を行うことなく、拡張子を変更することであります。

しかし、Hexとして上記ファイルを検閲すると、

<img width="900" alt="hex.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560692/b18a9612-a77b-546a-61d7-c8cbcd3f3452.png">


pngファイルと確認することの出来るマジックナンバーがバイナリヘッダーに存在しますね！

- [各種ファイルのマジックナンバーリスト](https://en.wikipedia.org/wiki/List_of_file_signatures)

では、このファイル名上の拡張子がjpgで内部的にpngであるファイルをクライアントサイドのフォームよりPOSTすると、PHPではどういった挙動になるのか確認してみましょう！

## サーバーサイドで拡張子が偽装されたファイルのバリデーションをする

まず前提条件として、下記のフィールドよりPOSTされたファイルを扱うとします。

```html
<input type="file" name="up-file">
```

その際に、`sample.png を sample.jpg にリネームしたファイル`をPOSTします。
すると、

```php
echo $_FILES["up-file"]["type"];
```

では、MIME-Typeとして

```
image/jpeg
```

を確認できるはずです。

これは、[MIME-Type一覧ページ](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types)で確認できる通り、`jpgファイル`として認識されていることがわかる証拠です。

しかし、

```php

$f_info = finfo_open(FILEINFO_MIME_TYPE); 
$mime = finfo_file(
    $f_info,
    $_FILES["up-file"]['tmp_name'] 
);
finfo_close($f_info); //念の為メモリ空間に開いたストリームの破棄を行う

echo $mime;
```

として、POSTされたファイルのバイナリからMIME-Typeを取得すると、


```

image/png
```

のように本来は`pngファイル`であることを確認できます。

## まとめ

```php
$_FILES["識別子"]["type"]
```

で確認出来るMIME-Typeは、ファイル名上の拡張子から推測されるものであることがわかりました！

なので、

- ファイル名上の拡張子から推測できるMIME-Type
- ファイルのバイナリヘッダーから推測出来るMIME-Type

の合致チェックを行って、よりセキュアにファイルのバリデーションを行おこうと思いました！

以下は、サンプルコードです！

```php

$file = $_FILES["識別子"];

$file_mime_type = $file["type"];

$f_info = finfo_open(FILEINFO_MIME_TYPE);
$binary_mime_type = finfo_file(
    $f_info,
    $file['tmp_name']
);
finfo_close($f_info);

$is_same_mime_type = $file_mime_type === $binary_mime_type;
```


ありがとうございました！！！

## 追記

### POSTされたファイルのHexをPHP上で確認する方法。

```php

$binary = file_get_contents(
    $_FILES["識別子"]["tmp_name"]
);

echo bin2hex($binary);
```

bin2hex(正確にはBinary to Hex)でBinaryデータをHexにコンバートします！
上記コード中の

```php

$_FILES["識別子"]["tmp_name"]
```

には、POSTされたファイルの一時的(temporary)なパスが文字列(String型)として格納されているだけなので、バイナリデータとして扱うために`file_get_contents`をしています。

あとは、

1. 16進数にコンバートされたバイナリヘッダーより拡張子の推測を行い、（[参照](https://en.wikipedia.org/wiki/List_of_file_signatures)）
2. 拡張子からMIME-Typeの推測を行う（[参照](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types)）

事ができます！
AWS CLIを使ってDockerイメージをECRにpushする

# Docker イメージを Amazon ECR リポジトリにプッシュするには
イメージのプッシュするになるAmazon ECRレジストリにDockerクライアント認証をする。
認証トークンは使用するレジストリごとに取得する必要があり、有効期限は12時間です。

## レジストリ認証
以下のコマンドを実行して出力されたコマンドをコピペしてそのまま実行

```bash
$ aws ecr get-login --no-include-email --region ap-northeast-1
```

## aws cli v2

ECRにログインするために必要な上記のコマンドがv2で使用できなくなっていました。
下記のようにすることでecrにログインできます。


```bash
$ aws ecr get-login-password | docker login --username AWS --password-stdin リポジトリURL
```

# タグ付け

使用する ECR レジストリ、リポジトリ、オプションのイメージタグ名の組み合わせによってイメージをタグ付けします。レジストリ形式は aws_account_id.dkr.ecr.region.amazonaws.com です。
リポジトリ名は、イメージ用に作成したリポジトリ名と一致する必要があります。

```bash
$ docker tag イメージID　aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app
```

# ECRにpushする

__docker push__ コマンドを使用してイメージをプッシュします。


```bash
$ docker push aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app
```

これでおしまい
【初心者向け】Fizzbuzzをメソッドを使用して解く

#Fizzbuzzの問題を応用し、数字の横にFizzbuzzを表示させてみた。

条件
・1~100までを表示させる
・3は数字の3とその横にfizzと表示
・5は数字の5とその横にbuzzと表示
・15は数字の15とその横にfizzbuzzと表示
・それぞれfizz,buzz,fizzbuzzのメソッドを使用すること


#擬似コードを書く

```ruby

def fizzbuzz
  #15と表示
  #15の横にfizzbuzzと表示させる
end

def fizz
  #3と表示
  #3の横にfizzと表示させる
end

def buzz
  #5と表示
  #5の横にbuzzと表示させる
end


1~100までの繰り返し文
 もし15で割って0なら
    fizzbuzzメソッドへ
  もし3で割って0なら
    fizzメソッドへ
  もし5で割って0なら
    buzzメソッドへ
  その他は
    数字をそのまま表示
  end
end
```


#コーディングしていく

```ruby

def fizzbuzz(num)
  print num
  num = "fizzbuzz"
  puts ":#{num}"
end

def fizz(num)
  print num
  num = "fizz"
  puts ":#{num}"
end

def buzz(num)
  print num
  num = "buzz"
  puts ":#{num}"
end

(1..100).each do |num|
  if num % 15 == 0
    fizzbuzz(num)
  elsif num % 3 == 0
    fizz(num)
  elsif num % 5 == 0
    buzz(num)
  else
    puts num
  end
end
```

#ポイント

・引数のnumを除いてやってみると、Nameerrorが出ます。
・printは改行せずに表示してくれます。
・putsは改行します。

#動作確認

```
1
2
3:fizz
4
5:buzz
6:fizz
7
8
9:fizz
10:buzz
11
12:fizz
13
14
15:fizzbuzz
16
17
18:fizz
19
20:buzz
21:fizz
22
23
24:fizz
25:buzz
26
27:fizz
28
29
30:fizzbuzz
31
32
33:fizz
34
35:buzz
36:fizz
37
38
39:fizz
40:buzz
41
42:fizz
43
44
45:fizzbuzz
46
47
48:fizz
49
50:buzz
51:fizz
52
53
54:fizz
55:buzz
56
57:fizz
58
59
60:fizzbuzz
61
62
63:fizz
64
65:buzz
66:fizz
67
68
69:fizz
70:buzz
71
72:fizz
73
74
75:fizzbuzz
76
77
78:fizz
79
80:buzz
81:fizz
82
83
84:fizz
85:buzz
86
87:fizz
88
89
90:fizzbuzz
91
92
93:fizz
94
95:buzz
96:fizz
97
98
99:fizz
100:buzz
```
こんなお遊びがお役に立てると幸いです。

--------------------------------

ここからさらにFizzbuzzを遊んでいきます。
fizzbuzzの際は、さらにアホゥと叫ぶコードを追加していきます。

っといっても引数を追加して表示させるだけなのですが、

#fizzbuzzの際に、さらにアホゥと数字の横に並べて表示させる
```ruby

def fizzbuzz(num, aho)
  print num
  num = "fizzbuzz"
  aho = "アホゥ!"
  print ":#{num}"
  puts "#{aho}"
end

def fizz(num)
  print num
  num = "fizz"
  puts ":#{num}"
end

def buzz(num)
  print num
  num = "buzz"
  puts ":#{num}"
end


(1..100).each do |num, aho|
  if num % 15 == 0
    fizzbuzz(num, aho)
  elsif num % 3 == 0
    fizz(num)
  elsif num % 5 == 0
    buzz(num)
  else
    puts num
  end
end
```

#動作確認（アホゥver）
```
1
2
3:fizz
4
5:buzz
6:fizz
7
8
9:fizz
10:buzz
11
12:fizz
13
14
15:fizzbuzzアホゥ!
16
17
18:fizz
19
20:buzz
21:fizz
22
23
24:fizz
25:buzz
26
27:fizz
28
29
30:fizzbuzzアホゥ!
31
32
33:fizz
34
35:buzz
36:fizz
37
38
39:fizz
40:buzz
41
42:fizz
43
44
45:fizzbuzzアホゥ!
46
47
48:fizz
49
50:buzz
51:fizz
52
53
54:fizz
55:buzz
56
57:fizz
58
59
60:fizzbuzzアホゥ!
61
62
63:fizz
64
65:buzz
66:fizz
67
68
69:fizz
70:buzz
71
72:fizz
73
74
75:fizzbuzzアホゥ!
76
77
78:fizz
79
80:buzz
81:fizz
82
83
84:fizz
85:buzz
86
87:fizz
88
89
90:fizzbuzzアホゥ!
91
92
93:fizz
94
95:buzz
96:fizz
97
98
99:fizz
100:buzz
```

ここは違うよ。または、ここはこうしたほうがセンスいいんじゃない？
等々ございましたらご指摘いただけますと幸いです。

最後までみていただき、ありがとうございました。
Coral Dev Board で Camera を使ってみる

# やること

Coral Dev Board に公式カメラをつけて、顔認識させる。

## 用意

* Coral Dev Board (設定済み)
* カメラモジュール (ケーブル突き)

![camera](https://user-images.githubusercontent.com/315029/72531238-664fdc80-38b4-11ea-9d0e-d9f7fcd1b8ee.jpeg)

* 同梱の説明書について
カメラの仕様が知りたいわけでなく、どうやってDevBoardと繋げるかを知りたいのでありますが、For more information, see g.co/coralと書いてあるのみの紙切れでありました。

URLから想像できるように、これはトップページに行くわけでして、直接カメラの接続方法を知りたいわけです。

* https://coral.ai/docs/dev-board/camera/

上記のURLで無事行けました。ちなみにトップページから意地でも行きたい方は、ProductからSensorsを選び、CameraのView Productを選ぶ。そして、Get Started Guideで無事たどりつけます。

## カメラの接続

見た感じ表には接続部分は見あたらないなと思ったら、背面に接続するようになっていました。

* 黒いラッチを上げます。そうするとCamera Connectorという文字が見えるようになります。
* ケーブルを白いコネクタに差し込みます。あまり奥まで入りません。その後ラッチを閉じて接続完了です。
* ケーブルの向きは青い部分が見えるようにです。
* もしカメラにケーブルが接続していなければ、同じように接続します。私の場合は始めから接続されていました。

![IMG_9258_2](https://user-images.githubusercontent.com/315029/72741565-40477680-3beb-11ea-96f6-9ebeb9f6769a.jpg)

## デバイスの確認

```
$ v4l2-ctl --list-formats-ext --device /dev/video1
Failed to open /dev/video1: No such file or directory
```

公式の通りやってみたのですが、そんなデバイスは無いと怒られました。

```
$ v4l2-ctl --list-devices
i.MX6S_CSI (platform:30a90000.csi1_bridge):
	/dev/video0
```

このように確認すべきだった模様です。video1はないけど、video0はあるのねと。

```
$ v4l2-ctl --list-formats-ext --device /dev/video0
ioctl: VIDIOC_ENUM_FMT
	Index       : 0
	Type        : Video Capture
	Pixel Format: 'YUYV'
	Name        : YUYV 4:2:2
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 720x480
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1920x1080
			Interval: Discrete 0.067s (15.000 fps)
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 2592x1944
			Interval: Discrete 0.067s (15.000 fps)
		Size: Discrete 0x0
```
ちゃんとデバイスデバイスの確認ができた模様です。

## デモの実行

### ファイルのダウンロード

まずはファイルを置くディレクトリの指定します
```
$ export DEMO_FILES="$HOME/demo_files"
```

続いてそこにダウンロードします

```
$ wget -P ${DEMO_FILES}/ https://github.com/google-coral/edgetpu/raw/master/test_data/mobilenet_v2_1.0_224_quant_edgetpu.tflite
$ wget -P ${DEMO_FILES}/ https://github.com/google-coral/edgetpu/raw/master/test_data/imagenet_labels.txt
$ wget -P ${DEMO_FILES}/ https://github.com/google-coral/edgetpu/raw/master/test_data/mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite
```

### 顔認識のデモ、ストリーミングサーバ版

まずは一つデモを実行してみます。顔認識のデモ、ストリーミングサーバ版です。

```
$ edgetpu_detect_server \
--model ${DEMO_FILES}/mobilenet_ssd_v2_face_quant_postprocess_edgetpu.tflite

(edgetpu_detect_server:3632): Gtk-WARNING **: Locale not supported by C library.
	Using the fallback 'C' locale.
INFO:edgetpuvision.streaming.server:Listening on ports tcp: 4665, web: 4664, annexb: 4666
...
...
```

ウェブブラウザでボードの `IP:4664` にアクセスしてください。例えばボードが `192.168.0.100` だったら、 `http://192.168.0.100:4664` となります。

![Snip20200122_4](https://user-images.githubusercontent.com/315029/72848572-3bb4b800-3ce8-11ea-9d0e-3e704b0d8ff1.png)

Objectsが1。顔が認識されました。
enable-sharedオプションで入れろと怒られた時にやったこと

#開発環境

Linux


# 参考

[こちらがとても参考になりました！](https://amzn.to/2XNA4eK)


#あらすじ

vimのプラグインであるYouCompleteMeをいれようとしたときに「シェアードアブジェクトがねーぞ、pythonをenable-sharedオプションでインストールしろ」と丁寧なerror文で教えてくれた。


#やり方

```terminal:ターミナルで実行
env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install バージョン
```

こちらを参照
https://github.com/pyenv/pyenv/wiki#how-to-build-cpython-with---enable-shared
Laravel カスタムバリデーション導入

## はじめに
- Laravel ではバリデーションの機能がある。
- デフォルトでもそれなりに使えるけど、カスタムした方が使える時もままある
- ちょっと分からなくて調べたりしたのでそれを記述する

## 前提
### laravel のバージョン
```
$ php artisan -V
Laravel Framework 5.8.16
```

## 使いたい
#### ①カスタムバリデーションの作成をする
- Validation のクラスを作成する。
- 今回は、場所は`app/Services/` に作成する
- メソッド名は `validate`の後ろにオリジナルの名前。この名前がバリデーションの名前になる。
- 下記のメソッドは、半角英数字とスペースを許可するってこと。多分。

```app/Services/CustomValidator.php
<?php

namespace App\Services;

class CustomValidator extends \Illuminate\Validation\Validator
{
    public function validateAlphanumeric($attribute, $value)
    {
        return (preg_match("/^[a-z0-9 ]+$/i", $value));
    }
}
```
#### ② 呼び出せるようにサービスプロパイダーで有効にする
- クラスを作ったままだと呼べないので、サービスプロパイダーという場所に登録する
- `AppServiceProvider` に登録を行う

```app/ServiceProviders/AppServiceProvider.php
<?php

namespace App\ServiceProviders;

use Illuminate\Support\ServiceProvider;

use App\Services\CustomValidator;   // 自分で作成したValidationのクラス

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // 自分で作成した Validation のクラス
        \Validator::resolver(function($translator, $data, $rules, $messages) {
            return new CustomValidator($translator, $data, $rules, $messages);
        });
    }
```

####③フォーム単位？で使いたい。
- コントローラで呼び出せるように `Request` クラスを生成して、割り当てていく。
- 下記のコマンドで `app/Http/Requests/TestRequest.php` を作成する

```
$ php artisan make:request TestRequest
```

- 作成した `Request` クラスを編集して、バリデーションをはめてく
- バリデーションの名前は `validate` を抜いた名前でスネークケースにした名前にする

```app/Http/Requests/TestRequest.php
<?php

namespace App\Http\Requests;

use App\Http\Requests\Request;

class TestRequest extends Request
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            // 自分で作成したカスタムバリデーションの名前を name = "test_box" の
            // テキストボックスに当てはめる
            'test_box' => 'alpha_numeric',   
        ];
    }
}
```

- コントローラ側でも使えるようにしていく

```app/Http/Controllers/TestControllers.php
<?php

namespace App\Http\Controllers\Test;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

use App\Http\Request\TestRequest;   // 作成した Request のクラスを使う

class TestController extends Controller
{
    // 作成した Request クラスを使う
    public function showForm(TestRequest $request)
    {
        〜処理を色々省略
        return view('test.form');
    }
}
```

####④バリデーションエラーの時のメッセージを多言語化
- バリデーションのエラーメッセージを多言語化できる。
- `/resources/lang/ja` の下に `validation.php` を作成で日本語ファイル
- `/resources/lang/en` の下に `validation.php` を作成で英語ファイル

```/resources/lang/ja/validation.php
<?php

return [

    'alpha_numeric'  => '半角英数字または半角スペース以外は登録できません',
];
```
Angular Material の type="datetime-local" で秒も入力する

このように `step="1"` を追加すれば秒も入力できます。

```
<input matInput type="datetime-local" placeholder="配信日時" step="1">
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18273/7e2ddddb-ae7d-8a8b-d4f1-c39eeadb5573.png)
PowerApps コーディング標準

Microsoftによるコーディング標準です。
出典：
https://aka.ms/powerappscanvasguidelines
https://aka.ms/powerappscanvasguidelinesjp （日本語版）

## 画面名
- 単語間にスペース
- 省略形は使わない
- 末尾に"Screen"

```C#
// Good
Home Screen
Thrive Help Screen

// Bad
Home
LoaderScreen
EmpProfDetails
Thrive Help
```

## コントロール名
- キャメルケース
- 先頭にコントールの略語3文字

```C#
// Good
lblUserName

// Bad
zipcode
Next
```

| コントロール名              |コントロール名（英）                    | 略語                                  | 
|:--------------------------|:----------------------|:------------------------------------:|
| ボタン                 | button   | btn    |
| カメラコントロール                 | camera control   | cam    |
| キャンバス                 | canvas   | can    |
| カード                 | card   | crd    |
| コレクション                 | collection   | col    |
| コンボボックス                 | combo box   | cmb    |
| 日付の選択                 | dates   | dte    |
| ドロップダウン                 | drop down   | drp    |
| フォーム                 | form   | frm    |
| ギャラリー                 | gallery   | gal    |
| グループ                 | group   | grp    |
| ヘッダー ページの図形                 | header page shape   | hdr    |
| HTMLテキスト                 | html text   | htm    |
| アイコン                 | icon   | ico    |
| 画像                 | image   | img    |
| ラベル                 | label   | lbl    |
| ページ セクションの図形                  | page section shape   | sec    |
| 図形（丸、三角 etc.）                 | shapes   | shp    |
| テーブルデータ                 | table data   | tbl    |
| テキスト入力                 | text input   | txt    |
| タイマー                 | timer   | tim    |

コントロール名はアプリ内で一意にしなければいけません。複数の画面で同一のコントロール名を設定したい場合は、コントロール名の末尾に画面の略称を付与します。

```C#
// ホーム画面（Home Screen）上のテキスト入力
txtUserNameHS

// ログイン画面（Login Screen）上のテキスト入力
txtUserNameLS
```

## データソース
**カスタムコネクタ**

- コネクタ名：パスカルケース
- 操作名：パスカルケース

```C#
// コネクタ名
VegetableFarm

// 操作名
VegetableFarm.Harvest("トマト");
```

標準コネクタのようにコネクタ名をパスカルケースではなくスペース区切りでも設定できますが、Power Apps上ではコネクタ名のスペースはすべて削除されます。  

```C#
// コネクタ名
Vegetable Farm

// Power Apps上の表示
VegetableFarm
```

**Excelワークシートデータ**

- データテーブル名：パスカルケース


## 変数名

コンテキスト変数とグローバル変数に同じ変数名を設定することが可能なため、変数名にプレフィックスを付与します。

**グローバル変数**

- キャメルケース
- 先頭にgbl

```C#
// Good
gblFocusedBorderColor
```

**コンテキスト変数**

- キャメルケース
- 先頭にloc

```C#
// Good
locSuccessMessage
```

（おまけ）
なお、コンテキスト変数とグローバル変数に同じ変数名を設定した場合、通常参照ではコンテキスト変数が優先され、演算子を使用する場合はグローバル変数が参照可能です。

```C#
// グローバル変数
Set(vegetable, "きゅうり" );
// コンテキスト変数
UpdateContext({ vegetable: "にんじん" });

vegetable        // にんじん
@ [vegetable]    // きゅうり
```

## コレクション名

- キャメルケース
- 先頭にcol

```C#
// Good
colMenuItems
```
MacOS Catalina zsh HomeBrew リンク・パスを通す

# Terminalで諸々動かなくなった(bash->zsh)
- MySQL
 - mysql.server start 実行できない
- PHP
 - php -v 実行できない
 
※ cat ~/.bash_profile >> ~/.zshrc でもよかったかも😅

## MySQL
### mysql.server start 実行できない

```terminal
# MySQL確認
yasuken@mba13 % ls /usr/local/Cellar/mysql*

# 実行結果
/usr/local/Cellar/mysql:
8.0.19

/usr/local/Cellar/mysql@5.7:
5.7.22	5.7.24

# デフォルト8.0 unlink
yasuken@mba13 ~ % brew unlink mysql

# 実行結果
Unlinking /usr/local/Cellar/mysql/8.0.19... 92 symlinks removed

# 5.7 link
yasuken@mba13 ~ % brew link mysql@5.7

# 実行結果
Warning: mysql@5.7 is keg-only and must be linked with --force

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/mysql@5.7/bin:$PATH"' >> ~/.zshrc

# パスを通す
yasuken@mba13 ~ % echo 'export PATH="/usr/local/opt/mysql@5.7/bin:$PATH"' >> ~/.zshrc

# パス読み込み
yasuken@mba13 ~ % source ~/.zshrc

# MySQL起動
yasuken@mba13 ~ % sudo mysql.server start

# 実行結果
Starting MySQL
.... SUCCESS!
```
## PHP
### php -v 実行できない
```terminal
# PHP確認
yasuken@mba13 ~ % ls /usr/local/Cellar/php*

# 実行結果
/usr/local/Cellar/php:
7.3.11

/usr/local/Cellar/php@7.1:
7.1.16_1	7.1.18		7.1.19		7.1.25

/usr/local/Cellar/php@7.2:
7.2.19_1

# PHP7.2 link
yasuken@mba13 ~ % brew link php@7.2

# 実行結果
Warning: php@7.2 is keg-only and must be linked with --force

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/php@7.2/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/usr/local/opt/php@7.2/sbin:$PATH"' >> ~/.zshrc

# パスを通す
yasuken@mba13 ~ %   echo 'export PATH="/usr/local/opt/php@7.2/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/usr/local/opt/php@7.2/sbin:$PATH"' >> ~/.zshrc

# パス読み込み
yasuken@mba13 ~ % source ~/.zshrc

# PHPバージョン確認
yasuken@mba13 ~ % php -v

# 実行結果
PHP 7.2.19 (cli) (built: Jun 17 2019 09:03:55) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.19, Copyright (c) 1999-2018, by Zend Technologies

```
kubectl の補完機能

# はじめに
Kubernetesを触り始めて、kubectlって毎回入力するの手間だなと思い、エイリアス設定したら補完機能使えなくなったので対応方法をまとめました。
自分の中で整理したくて投稿しました。

# 補完機能の設定方法
そもそも、kubectl 入力後TABキーを押してもサブコマンドが候補として出てこないので、下記コマンドで設定する必要があります。

```bash
$ source <(kubectl completion bash)
```

ただ、これだけだと再起動すると毎回実行しないといけないので、~/.bashrcに記述しておきます。

```bash
$ vi ~/.bashrc
(以下をファイル末尾等に記入)
source <(kubectl completion bash)
```
保存して終了後下記を実行し、記述ミスないか確認しときます。
(記入ミスあって再起動したらシェルが上手く立ち上がらなくなるので、、)

```bash
$ . ~/.bashrc
```

# エイリアス設定
補完機能だけでも大分楽にはなるんですけど、kubeから始まるコマンド(kubeadmやkubesec等)が色々いじっているといっぱいできるので、エイリアス設定しようと思いました。

```bash
$ alias k='kubectl'
```

でTABキーを押すと、どうなるのかというとkでTABキーを押すとファイル名が補完候補として出てきてしまいます。
これだと前と一緒なので、下記コマンドを実行してエイリアスも補完機能を有効にします。

```bash
$ complete -o default -F __start_kubectl k
```

これで、エイリアスでも補完機能が有効になります。
ただ、端末落とすとまた無効になってしまうので、~/.bashrcに記述しておきます。

```bash
$ vi ~/.bashrc
(以下をファイル末尾等に記入)
alias k='kubectl'
complete -o default -F __start_kubectl k
```

下記コマンドの実行も忘れずに。

```bash
$ . ~/.bashrc
```

###補足 completeコマンドとは

Bashでコマンドラインで補完を行う方法を指定するコマンドです。
詳細は下記URLをご確認ください。
[completeコマンドの使用方法](https://www.atmarkit.co.jp/ait/articles/1907/12/news015.html)

# さいごに
参考になれば幸いです。
React勉強の備忘録

#React HooksのuseState
React HooksのuseStateがどういう原理で実現されてるのかさっぱりわからなかったので調べた
[https://sbfl.net/blog/2019/02/09/react-hooks-usestate/](https://sbfl.net/blog/2019/02/09/react-hooks-usestate/)
リモートデスクトップ利用者をリモートから強制ログオフ

リモートデスクトップを利用していると、
ちゃんと？ログオフ（サインアウト）しないでウィンドウだけ閉じて放置している人がいる

とても困るのでリモートから強制ログオフさせるコマンドをメモしとく


# ユーザの一覧を表示

まずは誰が使っているのか、使っていたのかを一覧表示

```
C:\> query  session  /SERVER:マシン名
 セッション名      ユーザー名               ID  状態    種類        デバイス
 services                                    0  Disc
 rdp-tcp#4        abcdefgh                   8  Active
 console                                     9  Conn
 rdp-tcp                                 65536  Listen
```

利用中であれば、状態が Active で出てくる
リモートウィンドウを閉じている場合は Disc の状態になる

```
C:\> query  session  /SERVER:マシン名
 セッション名      ユーザー名               ID  状態    種類        デバイス
 services                                    0  Disc
                  abcdefgh                   8  Disc
 console                                     9  Conn
 rdp-tcp                                 65536  Listen
```

# 強制ログオフ

ユーザの一覧で表示された ID を利用して強制ログオフ

```
C:\> logoff  8  /SERVER:マシン名  /VM
abcdefgh (セッション 8) をコンピューター  からログオフしようとしています。
続行しますか (n=いいえ)?y
```
（一覧のID ８ を強制ログオフさせる例）


リモートデスクトップを利用していた人は突然サインアウトの画面になってしまいます
未保存のドキュメントなどを保存する間もなく強制ログオフとなるので本当に利用している場合はとても困ると思います

ユーザ名が一覧で分かるので声掛けしてログオフしてもらうのが良いですが、
できない場合はこのコマンドで強制ログオフを実行できます

# 強制再起動

ログオフではなくリモートから再起動してしまいたい場合は以下の呪文で

```
C:\> shutdown  /r  /f  /m  マシン名
```

/r で再起動
/f で強制
/m でマシン名を指定
【Python3】Pythonを始めたときに参考にしたサイト一覧

# Pythonを始めたときに参考にしたサイト一覧

- Pythonに初めて触る方
- とりあえずPythonを動かしてみたい方

上記の状態ではじめに参考にしたサイトの一覧です。

## Pythonをインストールする（Mac版）

[環境構築ガイド - python.jp(公式日本語版サイト)](https://www.python.jp/install/install.html)

上記の画面でmacOSなど、インストール先のOSのボタンをクリックすると、
インストール方法が詳しく書かれています。

## Pythonを試してみる

[2. Python インタプリタを使う - python.jp(公式日本語版サイト)](https://docs.python.org/ja/3/tutorial/interpreter.html)

インストールしたPythonでとりあえず何か動かしてみようと思ったときに参考にさせて頂きました。

# Pythonについて


[Python 3.8.1 ドキュメント(公式日本語版サイト)](https://docs.python.org/ja/3/)を見ると、詳しいPythonの使い方が載っています。
「チュートリアル」や「ライブラリーリファレンス」などを見ると、やりたいことを選んで確認できます。

※バージョンが最新かについてはご自身でご確認ください。

# 参照URL

- [環境構築ガイド - python.jp(公式日本語版サイト)](https://www.python.jp/install/install.html)
- [2. Python インタプリタを使う - python.jp(公式日本語版サイト)](https://docs.python.org/ja/3/tutorial/interpreter.html)
- [Python 3.8.1 ドキュメント(公式日本語版サイト)](https://docs.python.org/ja/3/)
date コマンドで UTC な現在時刻を time zone designator を Z の iso8601 で出力

date コマンドで local time を iso8601 で表示すると

```bash:
date --iso-8601="seconds"
2020-01-22T14:56:44+09:00
```

こんな感じ、time zone は日本だから +09:00

これを UTC で表示すると

```
date --iso-8601="seconds" --utc
2020-01-22T05:57:37+00:00
```
たしかに +00:00 なんだろうけどここは Z と出してほしい

```
echo `date '+%Y-%m-%dT%H:%M:%S' --utc`Z
2020-01-22T05:58:22Z
```

完成！
spring-batchのpartitioning使用時のReader must be open before it can be readの原因と対策

# 現象
以下のようなbean定義をしたところエラーになり実行できなかった。

やろうとした事としては https://docs.spring.io/spring-batch/docs/current/reference/html/scalability.html#partitioning の、spring-batchのPartitioningを試している。partitioningの情報（```fileName```）が```stepExecutionContext```に入るため、beanを```@StepScope```にしている。

```java
	@StepScope
	@Bean
	public ItemReader<String> itemReader(@Value("#{stepExecutionContext['fileName']}") 
	Resource resource) throws IOException {
```

エラー内容は以下のとおり。

```java
org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read.
	at org.springframework.batch.item.file.FlatFileItemReader.readLine(FlatFileItemReader.java:201) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:178) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
```

# 対策
戻り値型の```ItemReader```を```FlatFileItemReader```に変更する。または、この場合は```ItemStreamReader```でも良い。

```java
	@StepScope
	@Bean
	public FlatFileItemReader<String> itemReader(@Value("#{stepExecutionContext['fileName']}") 
	Resource resource) throws IOException {
```

# 理由
<code>@StepScope</code>により戻り値をAOPでプロキシするため。

```@StepScope```の中身を見ると```proxyMode = ScopedProxyMode.TARGET_CLASS```であり、いま、戻り値型が```ItemReader```なのでこのインタフェースに対するプロキシが作られる。そして、このインタフェースには（```ItemStream```の）```open```は無いので、当然```open```は呼ばれない。実際のインスタンスは```FlatFileItemReader```でこれはopenせずにreadは出来ないため、```Reader must be open before it can be read```という例外になる。そういわけで戻り値型を```FlatFileItemReader```か、```ItemStreamReader```に変えれば正常に動作する。

# 参考URL
* https://stackoverflow.com/questions/23847661/spring-batch-org-springframework-batch-item-readernotopenexception-reader-must
* https://stackoverflow.com/questions/21241683/spring-batch-beforestep-does-not-work-with-stepscope/21941127#21941127
社内環境でGitLabからYoctoへgit cloneする話

大雑把に下記のようなテンプレートでbbファイルを作成しました。

```
DESCRIPTION = ""
HOMEPAGE = ""

LICENSE = "CLOSED"

DEPENDS = ""

S = "${WORKDIR}/git/"

SRC_URI = ""

SRCREV = ""

inherit autotools
```

自分の場合だと、SRC_URIの記述方法で突っかかった。
元々社内で建てたサーバーからのgit cloneは成功していました。

サーバーは公開鍵認証方式を利用してますので、protocolはsshです。
何も記入しない(default)とprotocolはgitになります。defaultだと社内の場合、プロキシ設定が必要になると思います。

```
SRC_URI = "git://xxx.yyy.zzz.jp/home/project.git;protocol=ssh"
```

GitLabをyoctoへgit cloneする場合は**git@**が必要っぽいです。
こちらのGitLabのサーバーも公開鍵認証方式を利用しています。

```
SRC_URI = "git://git@gitlaburl.com/project.git;protocol=ssh;branch=master"
```

参考文献
http://gopinaths.gitlab.io/post/yocto_tips/
IBM Cloud IaaSへのVPN接続をLinuxのCLIからやってみた

まだ積み記事がたくさん残っていますが、今週のIBM Cloud Classic InfrastructureのSSL-VPNがアプライアンスのファームウェアアップグレードの影響でクライアント側もアップグレードが必要になったので、公式には記載が無いLinux版コマンドラインでのSSL-VPN接続手順を紹介します。

`注意:`本手順はあくまでも検証結果に元に記載した内容であり機能を保証する物ではありません。IBM Cloudオフィシャルの手順ではないため自己責任で検証してください

## CLIだと何が嬉しいか
IBM Cloudの公式ドキュメントでの手順はX-Windowが導入されている環境を前提として
Linuxでの接続手順が公開されています。
バッチ処理や開発環境など、X-Windowの導入していない（したくない）環境上からも、
本手順を使用すると接続が可能です。

内部で開発を行い、リリースの時だけジョブとしてVPN接続→内部的にSSHしてアプリリリースをしたり、なんていうデザインもできるかもしれません。

## 検証した環境
CentOS 6.9 64 bit -> GLIBCバージョンの修正必要。
CentOS 7.7.1988 64bit
 
## クライアントの入手
まずは Motion Pro Array VPNのクライアントソフトウェアを準備します。
[Array Networksのダウンロードページ](
https://support.arraynetworks.net/prx/001/http/supportportal.arraynetworks.net/downloads/downloads.html)から、最新版の`Linux standalone client 64bit(cmd)`をダウンロード・もしくはURLをコピーしてLinux Box側からwgetしておきます。

## 導入手順
今回の手順はzipファイルを使用します。

```
# unzip vpn_cmdline_linux64_v1.0.54.zip 
```

はい。おしまいです。

## 接続方法

コマンドラインで実行後、パラメータを回答していくことで接続ができます。
バックグラウンドには回らないので、Ctl＋Zなどで後ろに回せば他のコマンドが使えます。

```
# ./vpn_cmdline 
Input server url or ip: vpn.tok02.softlayer.com
Input server port: 443
0   radius
choose login method number: 0
you choose method: radius
Input username: {your SSL-VPN ID}
Input password: {your SSL-VPN PW}
login successfully!
starting vpn......
connect successfully!
vpn is running...

```

別ターミナルを開いてVPN経由でサーバーへSSHすることができました
セッションを切るには、`Ctl+C`でプロンプトに戻ると切断できます。

## コマンドラインで一発接続
いちいち毎回尋ねられるのがいやだ・簡単にしたい時は以下を利用します。
パスワードはコマンドのhistoryにも残ってしまうので、その辺りのセキュリティは適宜考えましょう。

```
# ./vpn_cmdline -h vpn.tok02.softlayer.com -u {your SSL-VPN ID} -p {your SSL-VPN PW}
```

& をつけてバックグラウンドで回しても接続することができました。

## 失敗編

CentOS 6.9で試したところ、GLIBCのバージョンが古くて動きませんでした。
6.Xでどうしても利用したい場合は、GLIBCのバージョンをRPMとは別に自分でビルドする必要があります。

マンホーラーのdotnsfさんが記事としてGLIBCのライブラリバージョンアップについて紹介されているので、CentOS6で利用される方はこちらを参考にしてください

[CentOS 6 の stdlibc++ を更新する：まだプログラマーですが何か？](http://dotnsf.blog.jp/archives/1064353059.html)

```
[root@localhost ~]# ./vpn_cmdline 
Input server url or ip: vpn.tok02.softlayer.com
Input server port: 443
0   radius
choose login method number:0
you choose method: radius
Input username: {your SSL-VPN ID}
Input password: {your SSL-VPN PW}
login successfully!
starting vpn......
./array_vpnc: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./array_vpnc)
create local connect socket failed!
```

### GLIBCのバージョン確認方法
以下コマンドでLinux BoxがサポートしているGLIBCバージョンの一覧を取得することができます。

```
# strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH
```
プロジェクト管理の課題と解決（4）プロジェクト管理ツールの効果と今後

「プロジェクト管理ツールとはなんぞや？どうあるべきか？」と初心に戻って考えながら書いている『プロジェクト管理の課題と解決』も4回目です。
時間に追われて企画やUXまで気を回せない！…という開発者の方もいらっしゃいますが、**良いツールを構築するにはやはり、そのツールの目的や生み出された背景を理解することが大事**だと考えています。

あと、ビジョンですね。関わる人間全員が同じ成功を想定しているか。これにつきます。

##意外な結果を生み出した個別原価管理

これは開発後、導入してからの話になりますが、**必要に迫られて実施してみたことが思わぬ効果をもたらした**という事例があります。

「プロジェクト管理ツールとグループウェアを統合させた[『Time Krei』（タイムクレイ）](https://timekrei.tenda.co.jp)が生まれたのには、大きく2つの背景がありました。」と[前述](https://qiita.com/Kapi-Ten/items/1b0c519a46dec806d026)しましたが、大きな背景の二つ目である「IPO」のための『個別原価管理』がその良例です。

それまで個別原価管理をしていなかった理由は時代的なものというか、一言でいうとずさんだった…という事ではないかと思います（あくまでも想像です。当時はいなかったので）。

当時モバイルサービスが『出せば売れる』時代であったため、[テンダ](https://www.tenda.co.jp/)は規模もさまざまにかなりの数のモバイルサイト／サービスの立ち上げを行いました。運用も含めてすべてにおいて個別に原価を管理するということは、使うことを習慣化できるシステムがない状態では大変難しい事でした。担当者レベルは自分のやっていることを事細かに登録しなければなりませんし、マネージメント層はそれをまとめなければならないからです。

特に運用フェーズにおいては、事業部側ではエンジニア一人一人がどの案件どのぐらい稼働していたかは把握していませんでした。スケジュール管理と連動している個別原価管理がTime Krei で可能になったことで、個人個人が「何を何時から何時まで行ったか」の登録がカンタンに出来るようになり（スケジュールだけを入れておけば良いのですから）、結果、一つ一つの案件の運用工数が顕在化しました。高収益な案件だと思っていたものが、意外と粗利が低いということがわかったのです。

売り上げが多いからと優遇していた案件が実は赤字に近かったということは、特に経営層にはショッキングな出来事でした。しかし、その顕在化した数値を見せて値上交渉に成功したため、結果的に増益に転じるという…どれだけ原価管理が大事かわかりますね。

このように、会計上ではわからなかった業務の無駄が、自動的にグラフなどで分かりやすく可視化でき、そのデータを蓄積できるTime Krei は、[『プロジェクト管理ツール』](https://timekrei.tenda.co.jp)としてマネージメント層やエンジニアの日々のプロジェクト推進に役に立つだけでなく、**経営層にも必要なデータを提供してくれる『経営判断に役立つ』ツール**として、現在では導入企業さまにご利用いただいています。

そして、当然この**『個別原価管理』は、これから上場を目指す工事進行基準が適用となる企業には、無くてはならない機能**なのです。

##自社で必要なツールを開発するということ

すぐれたプロジェクト管理ツール・・・[テンダ](https://www.tenda.co.jp/)の社員が必要としたプロジェクト管理ツールは、当然機能が優れているだけでは事が足りません。

先ほどの表計算ソフトで作成したWBSの話と同じですが、操作しやすく見やすくなければ、使うことが習慣化されず、きちんと更新することもせず、結局は使えないツールになってしまうのです。
担当が変わり「今開発している、このツールの状況を知りたい」と思っても、更新されていなかった資料しか出て来ず、まずはその更新からはじめる…なんて案件も、実際自分が担当したものにはありました。

[テンダ](https://www.tenda.co.jp/)は自社パッケージ製品の開発・販売からゲームコンテンツの企画・制作・運用まで幅広く展開している企業ですが、こういった企業のアドバンテージは**「必要とするものを作りだすことができる」**ことではないでしょうか。しかも、「自分たちが必要とするものは、大抵の企業も必要としている」ことも間違いないのです。

弊社が開発するBtoB製品／サービスは、もちろん社員として働く我々や身近にいるパートナーもターゲットであり、使用することが前提です。そのため使いやすさや品質にはこだわって開発しています。

新規立ち上げはもちろん、機能の追加や改善においては、社内の使用者がバグ発見者であり、UX改善のネタを出してくれる人でもあります。これは「必要とする人が必要とする業務で実際に使う」という最良のテスト環境であり、そうして少しでも良いものをお客さまに使っていただくための、一つの必須業務でもあると考えています。ただ使うのではなく、ひっかかる箇所があれば仮説を立て、改善案を考えることもテンダの一員としての日々の活動と言えましょう。

とはいえ、どんなにPDCAを回しても「誰にでも使いやすいプロジェクト管理ツール」は、なかなか長い道のりだと感じています。[『Time Krei』](https://timekrei.tenda.co.jp)だけでなく、どんな自社製品／サービスであれど（いや、余計にむしろ）使っていると言いたいことは山ほどあったりします。

**企画・開発・営業する全員が同じビジョンを目指して取り組める**か、さらに「愛着」といったスキルを育てて、自社開発ツールをもっと良くしていけるのか、が、開発現場の鍵のように感じています。

[『Time Krei』](https://timekrei.tenda.co.jp)の開発に取り組み、最終的には**『Time Krei』があるお陰で効率的に仕事ができ、ライフワークバランスが最良のものになれば**…と考えています。これは自分自身のためだけでなく、仕事で関わるすべての人、また「非効率」と言われる日本全体に貢献できるプロジェクトではないか…と思います。

もし、[テンダ](https://www.tenda.co.jp/)の[プロジェクト管理ツール『Time Krei』（タイムクレイ）](https://timekrei.tenda.co.jp/)にご興味をお持ちの場合は、是非一度[サービスサイト](https://timekrei.tenda.co.jp/)をご覧ください。
RHEL7.6(CentOS7.6) で発生する謎通信(port 53,1900)の原因を突き止めた

# 概要
RHEL7.6で構築したサーバにて下記２点の謎通信が発生し、NW機器のACLに引っかかってログに記録されていた。

#### ① dst port 1900 GUIログイン時に発生(１回のログインで2通信)

(パケットキャプチャ結果)
15:52:33.525715 IP localhost.localdomain.57153 > 239.255.255.250.1900: UDP, length 132

#### ② dst port 53(DNS) 毎晩深夜0時過ぎに発生(毎夜、30通信程)

(パケットキャプチャ結果)
00:00:01.096996 IP localhost.localdomain.53887 > 192.58.128.30.53: 28304% [1au] NS? . (28)

これらの通信に関して、ログに記録されてしまうためログ監視に影響が出ていた。特にDNSの方は、各ルートサーバに複数回アクセスを試みており、大量のログが出てしまっていた。そこで、原因となっているサービスを突き止めて停止し、通信を抑止することを考えた。

# 結論
#### ① dst port 1900 GUIログイン時に発生(１回のログインで2通信)

ログイン処理の中でGOA(GNOME Online Accounts)というサービスが起動する際に発生している通信だった。[GNOME公式ページ][1]を参考に下記設定ファイルを作成し、`dconf update`を実行してGOAを無効化することで、通信が発生しなくなった。

```
# vi /etc/dconf/db/local.d/00-goa
```

```/etc/dconf/db/local.d/00-goa
[org/gnome/online-accounts]
whitelisted-providers= ['']
```

```
# dconf update
```

#### ② dst port 53(DNS) 毎晩深夜0時過ぎに発生(毎夜、30通信程)

unbound-anchor.timerというunitの動作が原因だった(実際には、unbound-anchor.serviceがキックされて動いている)。`systemctl stop unbound-anchor.timer`を実施したところ、通信が発生しなくなった(再起動後も有効にしたい場合は、disableも併せて実施すること)。

```
# systemctl stop unbound-anchor.timer
# systemctl disable unbound-anchor.timer
```


# 検証方法
#### ① dst port 1900 GUIログイン時に発生(１回のログインで2通信)

GUIログイン時(VNCで検証)のsyslog(/var/log/messages)をtailし、同時に1900ポート宛の通信をtcpdumpして通信発生時付近の処理を確認したところ、怪しげなデーモンの起動ログを見つけたので、実行ファイルの場所を特定。

```
# tail -f /var/log/messages
...(略)...
Jan  7 16:43:01 localhost journal: goa-daemon version 3.28.0 starting
...(略)...
# whereis goa-daemon
/usr/libexec/goa-daemon
```

一旦力業で実行ファイルを消してみると見事に通信が発生しなくなったため、原因は特定できた。実行ファイルを消して対処するのは流石にスマートではないので、GOAサービスの無効化方法を調べたところ、前述したGNOME公式ページの設定を発見した。


#### ② dst port 53(DNS) 毎晩深夜0時過ぎに発生(毎夜、30通信程)

`systemctl list-unit-files --no-pager | grep enable`で有効化されたUnitを表示し、怪しいものを探しては停止してみた。サービス停止後はdateコマンドで時間を進めて、tcpdumpで53ポート宛をキャプチャして通信が出るかを確認した。

serviceを一通り止めてみて効果がないなぁと思っていたところ、ポツンとenableになっていた`unbound-anchor.timer`に目が留まる。調べたところ、DNSSEC絡みの処理をしているようで、大変怪しい。

こちらを停止したところ、0時に時間を進めても通信が発生しなくなった。

# おわりに
Linuxプロジェクトでは色々と自動化して便利にしようとしてくれているのかもしれないが、設定も入れてないのにデフォルトで勝手に通信するような機構を入れるのは出来ればご遠慮いただきたい所存。特にバージョンのメジャーアップデートのタイミングでこそっと入れられると、更改でOSバージョン上げた際に確認がしんどい。そんなんいちいち気にせず読み捨てろ、firewallで止めちまえ、とか色々とご意見はあるのだろうと思うが、なかなかそのような択を取りづらい事情もあったり...。同じようにして困っている人がこのご時世に万が一いたならば、
何かの参考になればうれしい限りだ。


[1]:https://help.gnome.org/admin/system-admin-guide/stable/lockdown-online-accounts.html.en
webデザインをおしゃれにできる便利＆参考サイトまとめ

メモです

###便利＆サービスサイト
font awsome
https://saruwakakun.com/html-css/basic/font-awesome

ロゴデザイン
https://www.canva.com/ja_jp/create/logos/

box-shadow自動生成
https://css-tricks.com/make-a-smooth-shadow-friend/

チートシート
https://www.webcreatorbox.com/tech/css-flexbox-cheat-sheet#flexbox2


###参考サイト
https://www.nxworld.net/


随時更新
各レコードの計算結果の合計を出す

配列を覚えると大変便利なことに気付いた。

１つの注文に対して複数の注文商品が登録されており、１つの注文の合計金額をorders/show画面に出したい。
使用するメソッドは今回.injectを使う。
関係は以下とする。

```:app/assets/models/order.rb
class Order < ApplicationRecord
 has_many :order_products, dependent: :destroy
end
```
```:app/assets/models/order_product.rb
class OrderProduct < ApplicationRecord
 belongs_to :order
end
```

OrderProductモデルにあるのは以下のカラム
・product_name(商品名)
・quantity(数量)
・price(単価)

```:orders_controller.rb
def show
 @order = Order.find(params[:id])
 @total = [] #配列の初期化
 @order.order_products.each do |order_product|
  @total << order_product.quantity * order_product.price
 end
　#＠orderの持つorder_productの数だけ@totalという配列に各レコードの小計を<<でつっこんでいく
end
```
```:orders/show.html.erb
@total.inject{|sum, n| sum + n }
```

これだけ。最初配列をよく知らなくてめちゃくちゃ悩んだ。
送料でもつけたければ　@total.inject{|sum, n| sum + n }　+ 送料　でもいい。
ただしそれをすると注文商品レコードが１つしか該当しない場合は反映されないので条件分岐が必要。

```:orders/show.html.erb
<% if @total.count = 1 %>
 <%= @total + 送料 %>
<% else %>
 <%= @total.inject{|sum, n| sum + n }　+ 送料 %>
<% end %>
```
こんな感じ。

これが例えば注文一覧に埋め込みたい場合は.findで定義付けできないのでhtmlにそのまま書き込む。
【Vue.js】[Vue warn]: Error in v-on handler: "TypeError: handler.apply is not a function

## はじめに
**Vue.js**と**Vuetify**を使用したSPAアプリケーションを作成中に以下エラーが発生しました。

```shell
[Vue warn]: Error in v-on handler: "TypeError: handler.apply is not a function
```

こちらを解決していきます。

## 環境
```yaml
OS: macOS Catalina 10.15.1
Vue: 2.6.10
vuetify: 2.1.0
```
## 何をしたかったのか
`v-for`で`v-list`をリストレンダリングし、それぞれの項目に対して以下２点を動的に変更したいと考えました。

1. `router-link`の**行き先**
2. `v-on`ディレクティブで発火する**イベント**

この**2**がうまくいかず、ブラウザのコンソールにはタイトルにあるエラーが発生していました。

まずは**元のコード**を示します。

## 元のコード
```vue:Sample.vue
<template>
<!-- 略 -->
        <v-list>
          <v-list-item
            v-for="item in items"
            :key="item.id"
            @click="item.action"
          >
            <router-link :to="{ name: item.link }">
              <v-list-item-title>
                {{ item.name }}
              </v-list-item-title>
            </router-link>
          </v-list-item>
        </v-list>
<!-- 略 -->
</template>

<script>
  export default {
    data() {
      return {
        items: [
          {id: 1, name: '1へのリンク', link: 'Component1', action: ''},
          {id: 2, name: '2へのリンク', link: 'Component2', action: ''},
          {id: 3, name: 'action1を実行', link: '', action: 'action1'},
          {id: 4, name: 'action2を実行', link: '', action: 'action2'},
        ]
      }
    },
    methods: {
      action1() {
        anyAction1()
      },
      action2() {
        anyAction2()
      }
    }
  }
</script>
```

これで、`router-link`は動的に変更できていましたが、`methods`に定義した`action1`, `action2`が発火しませんでした。

次に**解決したコード**を示します。

## 解決したコード(変更部周辺のみ抜粋)

```vue:Sample.vue
<template>
<!-- 略 -->
          <v-list-item
            v-for="item in items"
            :key="item.id"
            @click="triggerClick(item.action)"
          >
<!-- 略 -->
</template>

<script>
  export default {
    // 略
    methods: {
      triggerClick(action) {
      if (action === action1) {
        anyAction1()
      } else if (action === action2) {
        anyAction2()
      }
    }
  }
</script>
```
### 改善したポイント
- `v-list-item`内の`@click="triggerClick(item.action)"`と、**動的な引数**を渡した
- `methods`内でその**動的な引数**に応じて発火するイベントの内容を変化させた

## おわりに
最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

## 参考にさせて頂いたサイト（いつもありがとうございます）
- [vue.js - Can't bind v-on:click on Vuetify Vue warn: Error in v-on handler: "TypeError: handler.apply is not a function" - Stack Overflow](https://stackoverflow.com/questions/56849142/cant-bind-v-onclick-on-vuetify-vue-warn-error-in-v-on-handler-typeerror)
- [Subheader @click bug](https://codepen.io/kimsanka/pen/OeZLZy)
Azure Functions(PowerShell)でユーザー割り当てManaged Identityを使用してAzure SQL Databaseにセキュア接続

#はじめに
忘れっぽいので覚え書き
Azure functions(PowerShell)でユーザー割り当てManaged Identityを使ってAzure SQL Databaseに接続する方法が解らなかったので調査して記します

#Managed Identityの登録

- 他に説明されているサイトが多数あるので割愛

#Azure Functionsの関数アプリでManaged Identityを有効にする

関数アプリをクリック
プラットフォーム機能 ⇒ ネットワーク ⇒ ID
システム割り当て済み
　　オンにして保存
ユーザー割り当て済み
　　追加 ⇒ 作成したManaged Identityを登録

#Azure Functionsに関数を作成しコードを記述
```PowerShell:run.ps1
param($Timer)

$ConStr = "Server=tcp:<サーバURI>,<ポート>; Initial Catalog=<データベース>;"
$resourceURI = "https://database.windows.net/"
$tokenAuthURI = $env:MSI_ENDPOINT + "?resource=$resourceURI&api-version=2017-09-01&clientid=<Managed IdentityのクライアントID>"
$tokenResponse = Invoke-RestMethod -Method Get -Headers @{"Secret"="$env:MSI_SECRET"} -Uri $tokenAuthURI
$accessToken = $tokenResponse.access_token
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $ConStr
$SqlConnection.AccessToken = $accessToken
$SqlConnection.Open()
```
Invoke-RestMethodを使ってManaged Identityのトークンを取得し、そのトークンを接続に使用しています。
clientidが無いとシステム割り当て済みのManaged Identityが使用されるので注意が必要（ドはまりしました💦）

※[\$env:MSI_ENDPOINT]と[\$env:MSI_SECRET]はManaged Identityを有効にすると自動的に環境変数にセットされます。
【unityroom】公開したやつ【蓄積メモ】

---

2020/02/14
『FreeRacing』

standardAssetsの素材だけで適当に作ったものです。
車を動かす、コインを集める、そしてゲームとしてのスタートとエンドを兼ね備えたプレイを楽しんでください。

https://unityroom.com/games/freeracing

---

2020/02/13
同じく、爆速でunitychanを入れて、記事のミスを直しつつ、
ゲームとして***スタート画面***と***エンド画面***を入れるとどうなるかという、簡単なアクションゲーム。
https://unityroom.com/games/unitychanaction
<img width="744" alt="titleicon.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191635/2cad0e8c-a9dd-8bbc-6453-73dea362861f.png">


記事
【unity】爆速でunitychanを動かしたい時【unitychan】
https://qiita.com/sanpeita/items/ab3bb9ce5a5b7e6cf6a0

【unity】unitychanでいきなりエラーが出た【unitychan】
https://qiita.com/sanpeita/items/73ffaa1c1d095ed82035

---

2020/01/22
爆速でunitychan入れてうごかしたやつ。ゲーム性はない。
https://unityroom.com/games/otameshiunitychan

---
Vue.jsでGoogleMapsAPIを使用して地図を表示する（多分）最小構成

#はじめに
Vue環境でGoogleMapsAPIを使用する機会があったので、自分用の備忘録を兼ねて。
Vue.jsじゃなくてもある程度流用できると思います。

[公式ドキュメント（APIキーの取得方法も書いてます）](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)

#コード
```vuejs
<template>
  <div>
    <div class="map" ref="googleMap" />
  </div>
</template>

<script>
import GoogleMapsApiLoader from 'google-maps-api-loader';

export default {
  name: 'Map',
  data() {
    return {
      google: null,
      mapConfig: {
        center: {
          lat: 35.68944,
          lng: 139.69167
        },
        zoom: 17
      }
    }
  },
  async mounted() {
    this.google = await GoogleMapsApiLoader({
      apiKey: 'API_KEY'
    });
    this.initializeMap();
  },
  methods: {
    initializeMap() {
      new this.google.maps.Map(this.$refs.googleMap, this.mapConfig);
    }
  }
}
</script>

<style lang="scss" scoped>
.map {
  width: 100vw;
  height: 100vh;
}
</style>
```

Maps JavaScript APIを読み込むのに[google-maps-api-loader](https://www.npmjs.com/package/google-maps-api-loader)を使用しました。

もっと実用的なGoogleMapsAPIとVue.jsの組み合わせ方は[Vue.jsの公式ドキュメントに載っている](https://jp.vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html)のでこちらを参考にすると良いかもしれません。
今日のExcel Error(macOS)



OSを10.15.2に上げて、excel 15.29を立ち上げたら

![excel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51423/12b4cb29-fdd1-98e0-fbb2-a70e42cc8035.png)

1 左の数字が下向き
２　数字が、１−１３、１４−２６と塊ごとに逆順になっている。
３　いちばん先頭に文字aを入れたら、逆向きに、下の方で表示する。

docker14GBにしてmacOS 2GBにしたから出たのかと思った。

docker 2GB, macOS 14GBに戻しても出た。
最新の AWS rDNS 申請手順と確認方法

AWSのEC2やEKS、ECSでメールサーバを構築した場合、送信がうまく出来ないことが多いです。この問題を解決するためには、AWSが推奨するマネージドメール送信サービスのSESを使うか、またはメール送信制限解除申請(rDNS申請)を行う必要があります。ここでは、rDNS申請手順の最新版(2020年1月時点)を紹介します。

## rDNS(reverse DNS)申請とは？
- 一般的なスパムフィルタは、逆引きできないIPアドレスからのメールは受け取らないことが多いです。そのため、メールサーバのIPアドレスは逆引きレコードを設定する必要があります。
- AWSでメールサーバを構築する場合、IPアドレスのDNSはAWS管理下になるのでこの設定はユーザが直接行うことが出来ません。rDNS申請が必要になります。

## 注意点
- rDNS申請はルートアカウントでしかできません。
- ホスト名(FQDN)の正引きのDNS(Aレコード)の登録が必須です。
- Aレコードとして登録したIPとメールサーバのリソース(EC2など)が紐付いていないといけません。

## 準備
- EIPを登録する
- 使いたいホスト名(FQDN)で登録したEIPで引けるようにDNSの正引き(Aレコード)を登録する
- EC2などリソースに上記EIPをAssociateする

## 申請
1. ルートアカウントでAWSコンソールにログイン
2. 申請フォームにアクセス:  https://aws.amazon.com/forms/ec2-email-limit-rdns-request

## 申請内容
1. Use Case Description : 英語で申請理由を書きます。
2. Elastic IP Address : 逆引き可能にしたいEIPを入れます。
3. Reverse DNS Record for EIP : 逆引きの結果として設定したいホスト名(FQDN)を書きます。(例: mail.example.com)
4. ここにEIPをAssociateしたEC2などリソースを書く項目は存在しません。しかし、リソースが実在しないと申請は承認されません。

## 申請が承認された後の確認
承認までは2営業日以上かかります。また、すんなり承認が通らない場合が多く、何回かメールでのやり取りが必要になることもあります。承認のメールが来たら、逆引きレコードを確認しましょう。

#### 逆引き確認コマンド
```
# dig -x [申請したEIP]
```

#### 結果の例:

```
(省略)
;; ANSWER SECTION:
[EIP].in-ddr.arpa. 17700 IN     PTR     [申請内容3のホスト名(FQDN) 例: mail.example.com]
```

これでOKです。

## 参考
ref. PTR レコードを持つ Route 53 で、逆引き DNS 機能を有効にするにはどうすればよいですか ?　https://aws.amazon.com/jp/premiumsupport/knowledge-center/route-53-reverse-dns/
ref. AWSからのメール送信
https://www.slideshare.net/AmazonWebServicesJapan/aws-30934799
ref. AWS 逆引き申請手順(rDNS) https://qiita.com/tomozo6/items/5f3f4f674d3bcd924f17
CSS3のみでハンバーガーメニューを作る方法

CSSだけでハンバーガーメニューを作れたので参考サイトと作り方をメモします。

参考サイト
https://www.nxworld.net/tips/12-css-hamburger-menu-active-effect.html


1. リストを作る<br>

```html
<div id="ham-menu">
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
  </ul>
</div>
```

2.CSSをいじくる

```css

#ham-menu{
  position: fixed;  
  height:100%;
  width: 300px;
  right: -300px;
  top: 0;
  background-color: #222;
  padding: 10px 40px;
  z-index: 999;
  box-sizing: border-box;  /*パディングとボーダーを幅と高さに含める*/
  taransition: transform .3s linear 0s;
  }


#ham-nenu:before{
  position: absolute;
  width:50px;
  height: 50px;
  top: 0;
  right: 100%;
  content: "≡";
  text-align: center;
  font-size: 50px;
  color: #FFF;  
  line-height: 50px; /*縦位置中央化*/
  background-color: #222;
  z-index: 999;
  border-radius: 0 0 0 10px;
  display: block;
}


#menu-background{
  background-color: #222;
  display: block;
  height: 100%;
  opacity: 0;
  position: fixed;
  right: 0;
  top: 0;
  transition: all .3s liner 0s;
  z-index: 999;
}

#ham-menu:hover{
  transform: translate(-300px);
}


#ham-menu:hover + #menu-background{
  opacity: .5s;
  z-index: 999;
}


li{
  list-style: none;
  padding: .5em 0;
}


```
トップレベルで定義したメソッドは全クラスのプライベートメソッドとなる 

# イントロダクション
西遊記より
孫悟空にお釈迦様は言われます「私の掌から飛び出すことができれば、お前を自由にしてやる」。悟空は筋斗雲に飛び乗り世界の端を目指します。ここまで来たら大丈夫だろうとそこの山に「悟空参上！」と書きました。お釈迦様に報告した所、お釈迦様の掌にはその文字が書いてありました。

# 一見奇妙なコード
ある日、肥大化したクラスをリファクタリングしようと処理を分割していた時、不思議な現象に遭遇しました。

``` ruby
class Hoge
  def execute
    @str = "Hello Ruby!"
    hello
  end
end

def hello
  puts @str
end

hoge = Hoge.new
hoge.execute
# => Hello Ruby!
```
このコードは問題なく動作して「Hello Ruby!」を出力します。
しかしなぜ分離独立している筈のhelloメソッド内部でHogeクラスのインスタンス変数strを参照できているのでしょうか？他の言語ならエラーになりそうなのに。

# 何がおきているのか
まずRubyのトップレベルに書いたメソッドがどこに定義されるのかというと~~Kernelモジュールのprivateに定義されます。~~
（追記：コメントにて指摘されていますが、正しくはObjectに定義されます）

``` ruby
p Kernel.private_methods
# => [:hello, ...]
```
helloメソッドが追加されていることが確認できます。
このKernelモジュールはObjectクラスにてインクルードされ、さらにすべてのクラスはObjectを継承します。
この関係を確認してみましょう。

``` ruby
p Hoge.ancestors
# => [Hoge, Object, Kernel, BasicObject]
```
ここまで来ればHogeクラスにもhelloメソッドが定義されていることに気が付くでしょう。

``` ruby
p Hoge.private_methods
# => [:hello, ...]
```

# 結論
**トップレベルで定義したメソッドは全クラスのプライベートメソッドとなる**
helloメソッドはHogeクラスから分離独立しているように見えるが、実際は内部に定義されていたのです。だから普通にインスタンス変数にもアクセスできたのです。
Rubyの基礎階層を理解していないと孫悟空の様にホワッ？となるエピソードでした。
Gerritとは

Git Repositorに対して、Webアプリ上でコードレビューを行うことができます
コミットしたソースを、第三者がレビュー＆検証を行い、承認後にSubmitと同時にソースがマージされる。
ソースは自動マージを行うが、競合が発生した場合は開発者が手動で行う必要がある。
検証時に、compileが実施される。
Playgroundで動作するミニマムなUITableViewControllerのサンプルコード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883/3a81997e-1302-d4e8-9ab6-2eca947e80a6.png)

```swift
import UIKit
import PlaygroundSupport


class MyTableViewController: UITableViewController {
    private lazy var items: [String] = {
        return [
            "The Swift Programming Language",
            "The Swift Programming Language",
            "The Swift Programming Language"
        ]
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // セルの区切り線を消すハック
        self.tableView.tableFooterView = UIView()
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
        cell.detailTextLabel?.text = "ここはsubtitle : 1行目テキスト\n2行目テキスト"
        cell.detailTextLabel?.numberOfLines = 2
        cell.detailTextLabel?.lineBreakMode = .byWordWrapping
               
        let item = items[indexPath.row]
        cell.textLabel?.text = item
        cell.textLabel?.lineBreakMode = .byWordWrapping
        cell.textLabel?.numberOfLines = 0
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.items.count
    }
}

PlaygroundPage.current.liveView = MyTableViewController()
```