OmniAICreator/Qiita-1.07M huggingface dataset:
ワイヤーフレームつくるぜ。【プログラミング日記~ポートフォリオ②~】#3

#今回の作業
###①いろんなポートフォリオのサイトを見まくる:eyes:
→かっこいいデザインのサイトが多くびっくり‼
自分もそんなのを作りたいが初めてなので欲張らない。。。

###②実際にワークフレームを作ってみる
今回はpower pointerを使って作成してみました
超絶シンプルにしたので20分もせず完成‼
#完成品
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043307/37b85223-26bd-cbc8-7bcf-49331427fbb4.jpeg)
![スライド2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043307/6297f9d0-9e2f-ca13-414b-ca7ba2e341ac.jpeg)
![スライド3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043307/7f8ad5f2-3db2-db1c-01ae-4868201d3805.jpeg)
![スライド4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043307/de44de0d-2688-0591-cabc-5b93895aa824.jpeg)
![スライド5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043307/bc9f0a62-fbc6-8bd6-09e7-e8a8015511a4.jpeg)

欲張らずシンプルとは言ったがもう少し凝ったデザインにするべきだったかな笑？
まぁ、画像入れたり色が入ったらもう少しましになるはずなのでこれで良しとする:thumbsup:

#終わりに
今回はwebサイトの設計図ワイヤーフレームを作りました！
実際に作ってみると具体的なイメージがわいてきて俄然やる気になりました！
それでは～
【Javascript】オブジェクトの要素を配列で扱いたい場合

###以下のオブジェクトのキーとバリューをそれぞれ1つの配列で扱いたい場合
```javascript:sample.js
  const point = {
    x: 100,
    y: 200,
  };
```

###期待する結果
``Key : x Value: 100``
``Key : y Value: 200``



##実行コード

```javascript:sample.js
  const keys = Object.keys(point);
  keys.foreach((key) => {
    console.log("Key: ${key}, Value: ${point[key]}");
  });

```


1.``Object.keys``メソッドにオブジェクト``point``を渡すことによって 配列``[x,y]``が生成される
2.配列にはfor文が使えるので``foreach``を使うとそれぞれの配列からにキーとバリューを扱うことができる
AWS EC2インスタンス ストレージ増量後　反映方法

EC2でストレージを増量した後に反映させる方法
---
【環境】
Amazon Linux 2
(RedHat 系)

まず一応ストレージ増量(AWS側)を簡易的に書くよ！


`対象のEC2インスタンス選択`->下に出てくる設定的なところの`ストレージ`を選択
->アタッチされてる`ボリューム`を選択->`アクション`をクリック
->`ボリュームの変更`->`サイズ`を変更する ->`変更`

EC2値段計算サイト
https://aws-rough.cc/ec2/

```
lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  15G  0 disk 
└─xvda1 202:1    0  15G  0 part /
  ↑ここに出てくるpart必要

sudo growpart /dev/xvda 1
sudo xfs_growfs /dev/xvda1
df -h //確認コマンド


Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        474M     0  474M   0% /dev
tmpfs           492M     0  492M   0% /dev/shm
tmpfs           492M  424K  492M   1% /run
tmpfs           492M     0  492M   0% /sys/fs/cgroup
/dev/xvda1       15G  7.4G  7.7G  49% /←ここのSizeが増量時に設定したものになってればOK
tmpfs            99M     0   99M   0% /run/user/1000
```
これで反映されると思います。


参考記事
https://qiita.com/sand_bash/items/50c1721cc10f48fef804
Wordpress で「お探しのページが見つかりません。」と出た時の対処方法

Arch Linux で運用しているで次のような現象が起きました。
![ageha_jan30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/68fca32a-8fc9-6dc2-a8ea-e4cdc9b30a66.png)

Nginx のログは次のようになっていました。

```text:/var/log/nginx/error.log
2021/01/30 17:48:10 [error] 6189#6189: *103 FastCGI sent in stderr: "ment-query.php on line 484
PHP message: PHP Warning:  get_comment(): Argument #1 ($comment) must be passed by reference, value given in /var/www/wordpress/wp-includes/class-wp-comment-query.php on line 484
```

PHP のバージョンは次の通りでした。

```text
$ php --version
PHP 8.0.1 (cli) (built: Jan  8 2021 16:19:26) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.1, Copyright (c) Zend Technologies
```

対策は、WordPress のバージョンを 5.6 に上げることです。
それで、解決しました。
ローカル環境のDocker、nginxプロキシでSSLサーバ証明書を使う（mkcertでオレオレ証明書）

ローカル環境でSSLを使ったテストをどうすれば良いか悩み、あれこれやって何とか形になったので、記事に残しておきます。

## インストール
```
brew install mkcert
```

```
brew install nss # if yo use Firefox
```

## ブラウザの証明書に登録する

```
mkcert -install
```

## サーバ証明書と秘密鍵を発行する
※リバースプロキシで複数のサブドメインを使うため、ワイルドカードを使う

```
mkcert -cert-file localhost.crt -key-file localhost.key "*.dev.localhost" dev.localhost localhost 127.0.0.1
```

## /etc/hosts にドメインを追加する

```
　・
　・
　・
127.0.0.1 dev.localhost
```


## nginxのプロキシサーバーを作る（docker）
※./certsフォルダに作った証明書と秘密鍵を入れておく
※dockerのnetworkを作っておく

```
version: '3'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: 'nginx-proxy'
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./certs:/etc/nginx/certs

networks:
  default:
    external:
      name: proxy-network
```

## web1サーバーを作る

### web1 HTMLファイル
※publicフォルダに入れておく

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>web1</title>
  </head>
  <body>
      <h1>
        web1
      </h1>
  </body>
</html>
```
### web1 dockerサーバーを作る

```
version: '3'
services:
  web1:
    container_name: 'web1'
    image: nginx
    volumes:
      - ./public:/usr/share/nginx/html
    environment:
      - VIRTUAL_HOST=web1.dev.localhost
      - VIRTUAL_PORT=80

networks:
  default:
    external:
      name: proxy-network
```

## web２サーバーを作る

### web２ HTMLファイル
※publicフォルダに入れておく

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>web2</title>
  </head>
  <body>
      <h1>
        web2
      </h1>
  </body>
</html>

```
### web2 dockerサーバーを作る

```
version: '3'
services:
  web2:
    container_name: 'web2'
    image: nginx
    volumes:
      - ./public:/usr/share/nginx/html
    environment:
      - VIRTUAL_HOST=web2.dev.localhost
      - VIRTUAL_PORT=80

networks:
  default:
    external:
      name: proxy-network
```

## その他のdockerサーバーをプロキシ配下に入れる
VIRTUAL_HOST、VIRTUAL_PORT、networksを追加すれば入れることができる
rbenv 基本操作

たまに使うので、rbenvを使ったrubyのバージョン管理基本操作をメモしときます。

## 実行環境
OS: macOS Catalina バージョン10.15.7

##  rbenvとは
rubyのバージョンを管理してくれるツールです。
グローバル・各プロジェクトごとで使うrubyのバージョンを管理してくれます。

## まずインストール
```
# Homebrewをアップデート
brew update 

# インストール(ruby-buildはrbenv用プラグインです)
brew install rbenv ruby-build

# 初期化
echo 'eval "$(rbenv init -)"' >> ~/.zshrc # bashなら.bash_profile
source ~/.zshrc

# インストールできるrubyを確認(--list-allで全て確認できる)
rbenv install --list(--list-all)

# listを最新版にするにはruby-buildを更新
brew upgrade ruby-build

# 欲しいバージョンをインストール
rbenv install 3.0.0

# インストールできたか確認
rbenv versions

```

## バージョン切り替え方
```
# グローバルに設定
rbenv global 3.0.0
ruby --version # 切り替わったか確認

# ローカルに設定
cd hoge # 管理したいプロジェクトのルートに移動
rbenv local 3.0.0
ruby --version # 切り替わったか確認
```
プロジェクトのルートディレクトリに`.ruby-version`というファイルができるはずです。
以後、そのプロジェクトに移動すると自動でrubyのバージョンが`.ruby-version`に記載されているバージョンに切り替わります。

## 参考記事
[rbenvの使い方と仕組みについて - Qiita](https://qiita.com/Kodak_tmo/items/73147ed4f0eec54d6e94)
[rbenvでrubyのバージョンを管理する - Qiita](https://qiita.com/hujuu/items/3d600f2b2384c145ad12)
UnityでTextファイルを出力する方法


Textファイルに変数を出力する方法

```
using UnityEngine;
using System.IO;

public class Output : MonoBehaviour
{
    int[] num ={0,1,2,3,4};

    // Start is called before the first frame update
    void Start()
    {
        StreamWriter sw = new StreamWriter("./Assets/TextData.txt",false); // TextData.txtというファイルを新規で用意

        foreach (int i in num)
        {
            sw.WriteLine(i);// ファイルに書き出したあと改行
        }

        sw.WriteLine("おわり");

        sw.Flush();
        sw.Close();
        
    }
}
```
UnityのIL2CPPとDataContractJsonSerializerを用いたシリアライズの組み合わせの罠

## この記事で述べていること
・Unityのビルド時にIL2CPPを使い、かつDataContractJsonSerializerのWriteObjectでインスタンスのJsonデータをStreamに書き込もうとすると、ビルド環境でランタイムエラー(`System.NullReferenceException`)が発生する。  
・そのまま回避する方法はtry-catchでエラーを握りつぶすしかない(要検証)ので、これを使うのならMonoでのビルドを使うこと。  

## Unityのバージョン
Unity 2018.4.5f1

## はじめに
ある日私はUnityで、ある構造体のインスタンスをシリアライズしてファイルに書き出し保存しようと考えました。  
最初は`JsonSerializer`を使おうと考えたのですが、名前空間`System.Text.Json`が入っていないらしいためやめ、代わりとして`System.Runtime.Serialization.Json`の`DataContractJsonSerializer`を以下のように用いようとしたのですが、そこで問題が発生しました。  

  ```c#:やったことのコード例
public class Hoge : MonoBehaviour{
    //Dataの構造体の定義
    [DataContract]
    public struct Data{
        [DataMenber]
        public int hoge;
    }
    //Dataのインスタンス
    private Data data;

    //データのインスタンスのシリアライズを行う
    private void serializeData(){
        using(var memoryStream = new MemoryStream()){
            var dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Data));
            //memoryStreamにDataのインスタンスをシリアライズしたJsonデータを書き込む
            //ここで問題が発生しました
            dataContractJsonSerializer.WriteObject(memoryStream, data);
            //カーソル位置を最初に戻す
            memoryStream.Position = 0;

            //memoryStreamの内容を読み込んで、全ての文字列をファイルに書き出す
            using(var streamReader = new StreamReader(memoryStream, Encoding.UTF8)){
                var filePath = "任意のファイルのパス";
                File.WriteAllText(filePath, streamReader.ReadToEnd(), Encoding.UTF8);
            }
        }
    }
}
  ``` 

## 起こった問題
Unityエディタ環境では上のコードはエラーも無く動いたのですが、ビルド後のexeで見てみるとうまく動かないことが判明しました。  
ビルド後の環境のため`Debug.Log()`は使えないので、テキストをログ代わりにしてプリントデバッグしたところ、WriteObjectでインスタンスをシリアライズして書き込む段階で止まっていることがわかりました。  
そのためtry-catchで囲んでエラーを出力してみると、`NullReferenceException`が出ていることが判明しましたが、 当然ながらmemoryStream、dataContractJsonSerializerどちらもnullチェックをしてもfalseであった(構造体は非null許容型ですし)ため、他のビルドに関わる部分を調べてみることにしました。

## 原因
**ビルド時のPlayerSettingsで、ConfigurationのScripting BackendをIL2CPPにしている**ことが原因でした。  

## 解決法
原因から分かると思いますが、Monoに切り替えたところ正常に動きました。  
IL→C++への変換でうまくいかないところがあるのでしょうか。はっきり言ってよく分かりません。  

## この記事を見た人へのお願い
上記のようにとりあえずの解決法は見つかりましたが、原因については全く分かっていない状態です。  
異なる解決法(IL2CPPのままでOKなものなど)、もしくは原因の具体的な詳細など分かる方がおられるなら、コメント・Twitter等で教えていただけるとありがたいです。  

## 参考文献
・Microsoft Docs  JsonSerializer
　https://docs.microsoft.com/ja-jp/dotnet/api/system.text.json.jsonserializer?view=net-5.0

・Microsoft Docs  DataContractJsonSerializer
　https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.serialization.json.datacontractjsonserializer?view=net-5.0
React/TypeScript: 他のコンポーネントの型注釈（型の定義）を取得する方法備忘録


`ComponentProps`を使用する。

# コード
```react
const HogeTypes = ComponentProps<typeof Hoge>
```

応用: `chakra-ui`の提供するHeadingコンポーネントの型注釈を取得し継承？しつつ独自の`text`を定義している。

```react
import { ComponentProps } from "react"
import { Heading } from "@chakra-ui/react";

type Props = ComponentProps<typeof Heading> & {
  text: string;
};
```
echoのWrapHandlerを使ってnet/httpで作られた関数をそのまま使う

## 使い方
- `echo.WrapHandler(http.HandlerFunc(Redirect))`のような感じで使う

## ソース

```golang
package main

import (
	"net/http"

	"github.com/labstack/echo"
)

func main() {
	e := echo.New()

	e.GET("/", echo.WrapHandler(http.HandlerFunc(Redirect)))
	e.Logger.Fatal(e.Start(":1323"))
}

func Redirect(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, "https://vamdemicsystem.black", http.StatusFound)
}
```
第15回 DHCP機能

##DHCP技術
IPアドレスをPCに割り当てる際、専用のサーバーを使用せずに**ルーターやL3スイッチ**でも同等の機能を使用することができる。大規模ネットワークではあまり使用されないが、**支店やオフィスの会議室**など**小さな規模のネットワーク帯**ではよく使用される。


##L3スイッチで各部署ごとに異なるアドレスを割り当て
構築ネットワーク
![L3スイッチ DHCP.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559536/207835ba-cb67-7424-1615-3fbc10598979.jpeg)



###投入コンフィグ(1個のルータでDHCPプール設定)
```ruby:Router設定
enable secret TEST
!
username TEST secret TEST
!
ip dhcp excluded-address 192.168.1.1 192.168.1.10
※営業部のPCには1～10以外のIPアドレスを払い出す
ip dhcp excluded-address 10.1.4.1 10.1.4.40
※経理部のPCには1～40以外のIPアドレスを払い出す
!
ip dhcp pool TEST01
network 192.168.1.0 255.255.255.0
default-router 192.168.1.1
dns-server 8.8.8.8
!
ip dhcp pool TEST02
network 10.1.4.2 255.255.252.0
default-router 10.1.4.3
dns-server 8.8.8.8
!
interface GigabitEthernet0/1
 no switchport
 ip address 10.1.4.5 255.255.252.0
 no shutdown
!
interface GigabitEthernet0/2
 no switchport
 ip address 192.168.1.1 255.255.255.0
 no shutdown
!
```

##複数ルーターでのDHCPリレー

###DHCPのリレー機能
ルータやDHCPサーバーで**DHCP機能**を設定した場合、**L2スイッチ**ならブロードキャストを通過させ、その先に接続されているPCに設定した範囲のIPアドレスを割り振ることはできる。
しかし、異なったネットワーク帯をつなぐルーター同士では**ブロードキャストを通過せることは出来ない。**そういう時に使用する機能が**DHCPリレーエージェント機能**

構築ネットワーク
![リレーエージェント.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559536/7f7185d4-6539-9388-d098-8638abfa47f6.jpeg)

###投入コンフィグ(DHCPリレーエージェント機能)
```ruby:DHCPサーバー設定
!
ip dhcp excluded-address 192.168.1.1 192.168.1.10
ip dhcp excluded-address 172.16.1.1 172.16.1.10
ip dhcp excluded-address 162.15.14.1 162.15.14.10
!
ip dhcp pool Accounting
 network 192.168.1.0 255.255.255.0
 default-router 192.168.1.1
 dns-server 8.8.8.8
!
ip dhcp pool Sales1
 network 172.16.1.0 255.255.255.0
 default-router 172.16.1.1
 dns-server 8.8.8.8
!
ip dhcp pool Sales2
 network 162.15.0.0 255.255.240.0
 default-router 162.15.14.1
 dns-server 8.8.8.8
!
interface GigabitEthernet0/0
 ip address 10.1.1.1 255.255.255.252
no shutdown
!
interface GigabitEthernet0/1
 ip address 192.168.1.1 255.255.255.0
no shutdown
!
ip route 172.16.1.0 255.255.255.0 10.1.1.2 
ip route 162.15.0.0 255.255.240.0 10.1.1.2 
!
```

```ruby:DHCPリレー設定
!
interface GigabitEthernet0/0
 ip address 10.1.1.2 255.255.255.252
no shutdown
!
interface GigabitEthernet0/1
 ip address 172.16.1.1 255.255.255.0
 ip helper-address 10.1.1.1
no shutdown
!
interface GigabitEthernet0/2
 ip address 162.15.14.2 255.255.240.0
 ip helper-address 10.1.1.1
no shutdown
!
ip route 192.168.1.0 255.255.255.0 10.1.1.1 
!
```


#参考
[ネットワークエンジニアとして TCP/IP - DHCP]
(https://www.infraexpert.com/study/tcpip13.html)
【Rails】ModelのPrimary keyをULIDにする【ActiveRecord】

RailsのModelはPrimary keyに何も指定をしないとidカラムがPrimary keyとなります。(サロゲートキー)
idカラムの値はインクリメントされていくのですが、本記事はidカラムの値をインクリメントされる値から、ULIDに変更する方法です。

## 環境
Ruby: 2.7.2
Rails: 6.1.1
DB: PostgreSQL 13.1
ライブラリ: [ULID](https://github.com/rafaelsales/ulid)

## 手順
1. カラムの型を変更(Migration)
2. Modelに追記
3. テスト

### Migration
```rb
class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    # idカラムをstringにする
    create_table :users, id: :string do |t|
      t.string :name, null: false

      t.timestamps
    end
  end
end

```

### Model

```rb:models/user.rb

class User < ApplicationRecord
  include Identifiable
end
```

```rb:models/concerns/identifiable.rb
module Identifiable
  extend ActiveSupport::Concern

  included do
    # memo: primary_keyのデフォルトの値は`id`
    attribute primary_key, :string, default: -> { ULID.generate }
  end
end


```



### 実行結果

```shell
irb(main):001:0> User.new
=> #<User id: "01EX96S3Q185V0FSMFCCJBKN6P", name: nil, created_at: nil, updated_at: nil>

```

```shell
irb(main):006:0> User.create(name: 'name')
  TRANSACTION (0.2ms)  BEGIN
  User Create (0.5ms)  INSERT INTO "users" ("id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["id", "01EX95SNJ85JX57H8F43JEAGPG"], ["name", "name"], ["created_at", "2021-01-30 08:15:51.880899"], ["updated_at", "2021-01-30 08:15:51.880899"]]
  TRANSACTION (3.6ms)  COMMIT
=> #<User id: "01EX95SNJ85JX57H8F43JEAGPG", name: "name", created_at: "2021-01-30 08:15:51.880899000 +0000", updated_at: "2021-01-30 08:15:51.880899000 +0000">
```

### テスト
```rb:spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  it_behaves_like 'Identifiable'
end
```

```rb:spec/support/models/identifiable.rb

RSpec.shared_examples 'Identifiable' do
  describe '#primary_key' do
    context 'primary_keyカラムが`id`の場合' do
      subject { described_class.new.id }

      it { is_expected.to match(/[0-9A-Z]{26}/) }
    end
  end
end

```

## 参考
https://github.com/rails/rails/blob/5f3ff60084ab5d5921ca3499814e4697f8350ee7/activerecord/lib/active_record/attribute_methods/primary_key.rb#L89
https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html
https://github.com/shin1rok/ulid-rails-sample
KotlinでJUnit5を使ってデータドリブンテストを整備する

今回は自身で開発・運営している「Hello Books」で利用している技術について紹介したいと思います。  
  
#### [Hello Books](https://hellobooks.org)について
https://hellobooks.org

* エンジニア向け技術書レビューサービス
* 購入前の判断にエンジニアのレビューを役立てることができる
* アカウントを作成することで自身もレビューを投稿したり、技術書をブックマークしたりできる
* 詳細な条件による技術書の検索が可能
![スクリーンショット 2021-05-27 10.20.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521599/52eb5728-a133-3759-011d-95571d1f006d.png)

## `@ParameterizedTest`・`@MethodSource`でデータドリブン化

業務ではPHPを使っているのですが、DDD（ドメイン駆動設計）でValueObjectの単体テストを記述する際にはPHPDocを利用したデータドリブンにテストを記述しています。Kotlinでも同じ様にデータドリブンテストを実現する方法がないかなと探していたところ、JUnit5の`@ParameterizedTest`アノテーションを使うことで簡単に記述できることが分かりました。  

JUnitでは`@Test`を記述することでテストケースを作成することができますが、データドリブンなテストケースを作成する場合には`@ParameterizedTest`を記述します。

```kotlin
class BookTitleTest {
    @ParameterizedTest
    @MethodSource("dataProvider")
    fun testValidation(value: String, isValid: Boolean) {
        when (isValid) {
            true -> assertDoesNotThrow { BookTitle(value) }
            false -> assertThrows<ConstraintViolationException> { BookTitle(value) }
        }
    }
    companion object {
        @JvmStatic
        fun dataProvider() = listOf(
                // 正常系
                Arguments.of("test title", true),
                Arguments.of("a", true),
                Arguments.of("a".repeat(50), true),
                // 異常系
                Arguments.of("", false),
                Arguments.of("a".repeat(51), false)
        )
    }
}
```

上記の様にすることで、`dataProvider`メソッドをソースとするデータドリブンテストとして`testValidation`を記述することができます。  
`dataProvider`メソッドでは`testValidation`テストで受け取る2つの引数として渡す値を設定します。今回は1つ目の引数`Value`をString、2つ目の引数`isValid`をBooleanとして設定しているので、設定する値は`Arguments.of("test title", true)`の様になります。 任意の数のデータを任意の型としてソースから受け取ることができるのはとても便利ですね。

テストを実行して、各パラメータのテスト結果を確認してみます。IntelliJでは各ケースで渡された値とそのテスト結果を一目で確認することができます。  
![スクリーンショット 2021-01-30 17.27.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521599/c3479285-b356-6dd4-cdab-048b5e87b3dd.png)


## Kotlinで記述する際の注意点

とてもシンプルに利用できる`@ParameterizedTest`ですが、Kotlinで利用する際には2つ注意点があります。  
  
### ①ソースメソッドを`companion object`として記述する
`@MethodSource`として設定されるメソッドは`static`として記述される必要があります。Kotlinでは`static`が存在しないため、代替方法として`companion object`としてソースになるメソッドを記述する必要があります。  
  
### ②ソースメソッドに`@JvmStatic`を記述する  
Kotlinで`companion object`として記述されたメソッドはJavaとして扱われる際に`Companion`クラスを経由しないとアクセスすることができません。今回の例で言うと、ソースメソッドにアクセスするには`BookTitleTest.Companion.dataProvider()`とする必要があるわけです。このままではJavaが「`BookTitleTest.dataProvider()`どこにもないよ、、」と困ってしまうので、`@JvmStatic`アノテーションを付記してあげる必要があります。こうすることで、`Companion`を経由しなくてもメソッドを呼び出せる様になります。  
  
## まとめ
どうしてもデータドリブンテストを書きたくて、TestNGなどの外部フレームワークを利用しなくちゃダメかなと思い調べていたところ、JUnit5であれば非常に簡単に実現できて感動しました。皆様もぜひお試しください！
AWS SAM CLIでLambdaの環境変数を切り替える

###目的
ローカル、ステージング、本番環境でLambdaの環境変数を切り替えたい。
### ローカルで環境変数を設定してみる

ヘルプに大体書いてある。

~~~
$sam local invoke --help
~~~

-n オプションで環境変数ファイルを指定するとある。


vars.json

~~~
{
  "Parameters": {
    "ApiHost1": "http://hogehoge.com"
  }
}
~~~

template.yaml

~~~
Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        ApiHost1:
~~~

app.js

~~~
exports.lambdaHandler = async (event, context) => {
  try {
    console.log("process.env.ApiHost1", process.env.ApiHost1);
...
~~~

コンソールで実行

~~~
$sam local invoke HelloWorldFunction -n vars.json
~~~

成功。
コンソールに出力された。

### デプロイ先環境ごとに切り替え
ヘルプを見てみる。

~~~
$sam deploy --help
~~~

--config-env オプションで環境名を指定。デフォルト環境は'default'
samconfig.tomlに環境ごとにパラメータを指定。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

parameter_overridesというのがある。

以下のデプロイでできたsamconfig.tomlにparameter_overridesを追記してみる。
https://qiita.com/pino2/items/fd994534e94e6b4928f3

sam local invoke コマンドに効かせたい時は、これをヘッダにする。
[default.local_invoke.parameters]

samconfig.toml

~~~
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
...
[default.local_invoke.parameters]
parameter_overrides = "ApiHost1=http://hoge.com ApiHost2=http://fuga.com"
~~~

テンプレートはパラメータを参照するように修正

template.yaml

~~~
Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        ApiHost1: !Ref ApiHost1
        ApiHost2: !Ref ApiHost2
~~~

app.js

~~~
exports.lambdaHandler = async (event, context) => {
  try {
    console.log("process.env.ApiHost1", process.env.ApiHost1);
    console.log("process.env.ApiHost2", process.env.ApiHost2);
...
~~~

実行

~~~
$sam local invoke HelloWorldFunction
~~~


コンソールに表示された。

~~~
INFO	process.env.ApiHost1 http://hoge.com
INFO	process.env.ApiHost2 http://fuga.com
~~~

####本番とステージング用設定追記

samconfig.toml

~~~
[staging.deploy.parameters]
...
parameter_overrides = "ApiHost1=http://staging.hoge.com ApiHost2=http://staging.fuga.com"
[production.deploy.parameters]
...
parameter_overrides = "ApiHost1=http://prod.hoge.com ApiHost2=http://prod.fuga.com"
~~~

template.yamlにParametersを書かないとエラーになった。

~~~
Parameters:
  ApiHost1:
    Type: String
  ApiHost2:
    Type: String

Globals:
  Function:
    Timeout: 3
    Environment:
      Variables:
        ApiHost1: !Ref ApiHost1
        ApiHost2: !Ref ApiHost2
~~~

環境は本番を指定してデプロイ実施してみる。

~~~
$sam deploy --config-env production
~~~

template.yamlにOutputを書いていると
コンソールにエンドポイントURLが出力されるので、そのパスにアクセス。
Lamdbaのconsole.logはCloudWatchLogsに出力されるので確認。
ちゃんと設定した値が表示された。
AthenaでCAST関数を利用する機会が増えた

昨年からAWSを触る機会が増えて、今年に入ってから初めてAthenaを使いました。

Athenaの環境でPrestoに触れてからというもの、標準SQLでクエリーを記述している頃に比べるとエラーの発生頻度が増えた気がする。。

もしかしたら、私と同じように困っている人がいるのでは？と思い、今回投稿してみることにしました。

例えば、至ってシンプルな下記テーブル情報が存在しているとします。

```
データベース：test
テーブル：test_table
カラム：test_code,test_name,test_date
```

Athenaを含めた、他のRDB上で下記クエリーを実行すると、全カラムを全件出力して正常終了します。

```
SELECT * FROM test.test_table;
```

しかし、Athenaで<B>test_table.test_code</B>内に存在する<B>111</B>という情報を取り出そうとすると・・・

```
SELECT *
FROM   test.test_table
WHERE  test_table.test_code = 111;
```

「SYNTAX_ERROR: line～」から始まるエラーが返ってきました。でも、レコードに<B>111</B>は存在しています。。。

Google先生を頼りに調べてみたところ、どうやらAthenaでは<B>CAST</B>関数を用いて、型変換しなければ上手くいかないケースが多い事を知りました。

[error-1]:https://docs.aws.amazon.com/ja_jp/athena/latest/ug/data-types.html
データの型については、[コチラ][error-1]のページを参考にしました。

※ついでにCASTしてからSUBSTRで指定した文字列を返すケースも書いてあるので助かりました。

そういうわけで、CAST関数を用いて型変換を行ってみたら・・・すんなりと実行結果が戻ってきました。

```
SELECT *
FROM   test.test_table
WHERE  cast(test_table.test_code as integer) = 111
```

もし、私と同じような目に合った方がいたら参考にしてみてください。
Github ActionsでdevelopブランチにPullRequestが作成されたタイミングでテストを実行してみる。

GithubのActionsタブから新しいActionを追加する

```sh
# .github/workflows/実行するアクションの名前.ymlでファイルを作る
$ touch .github/workflows/run-tests-on-pr-towards-develop.yml
```

```yml:.github/workflows/run-tests-on-pr-towards-develop.yml

name: Run Tests on PR towards develop branch

# Controls when the action will run.
on:
  # Triggers the workflow on pull request events but only for the develop branch
  pull_request:
    branches: [ develop ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Install npm packages
        run: npm install
      - name: Run Tests
        run: npm run test
```
Windowsでpingのレスポンスを時刻と共にCSV出力するバッチ

# はじめに
　な～んかネットワークが遅いなぁと感じる時ってありますよね。アクセスできない訳ではないけど、時間帯によって遅い、宛先によって遅い、経路の途中で遅延している気がする、といったようなスッキリしないケース。

　pingかけ続けて調査する手もありますが、遅延している時の時刻と紐づけができずイラっとします。

　会社環境はWindowsというケースも多いと思いますので、
　　・Windows環境で、
　　・複数の宛先IPに対して、
　　・指定した間隔でpingのレスポンス（Min、Max、Ave）を計測し、
　　・時刻と共にCSV形式で出力する。
というバッチ（.bat）を作りました。

# バッチ

以下の変数を適当に変更して下さい。

　・INTERVAL : ping実行間隔 [秒] : 60の倍数をお勧めします。
　・MAXCOUNT : 繰り返し回数 [回]
　・PINGCOUNT : ping実行時のパケット送信回数 [回]（ping -n オプション）
　・PINGCOUNT : pingのタイムアウト [ms]（ping -w オプション）
　・IPLIST : ping送信先IPアドレス一覧（スペース区切り）

また、pingのパケット送信回数と宛先IP数を掛けた値がping実行間隔より小さくなるよう調整して下さい。

　PINGCOUNT * IPLISTのIP数 < INTERVAL

ping送信1回あたりのレスポンスが最大1秒（PINGTIMEOUT=1000）と想定して1つの宛先IPあたり、PINGCOUNT回数分の時間を要すると仮定しています。

これがINTERVALの値を超えるとpingの実行間隔がINTERVALで指定した間隔より長くなります。

```pingResToCsv.bat
@echo off

rem ###############################################################
rem 以下のパラメータを設定
rem ###############################################################
rem ※　INTERVALは60の倍数(1分単位)を設定。
rem ※　おおよその実行時間[秒] : INTERVAL * MAXCOUNT
rem ※　「PINGCOUNT * IPLISTのIP数 < INTERVAL」となるよう調整。
rem ※　（ping送信1回あたりのレスポンスが1秒と想定。）
rem ###############################################################
rem INTERVAL : ping実行間隔 [秒]
set INTERVAL=60

rem MAXCOUNT : 繰り返し回数 [回]
set MAXCOUNT=5

rem PINGCOUNT : ping実行時のパケット送信回数 [回]
set PINGCOUNT=3

rem PINGCOUNT : pingのタイムアウト [ms]
set PINGTIMEOUT=1000

rem IPLIST : ping送信先IPアドレス一覧（スペース区切り）
rem 設定例 : IPLIST=192.0.2.1 198.51.100.1 203.0.113.1
set IPLIST=192.168.1.1 192.168.2.2 192.168.3.3
rem ###############################################################

rem 文字コード変更
chcp 437 > NUL

setlocal enabledelayedexpansion

echo date/time,IP,Min,Max,Ave

for /l %%g in (1, 1, %MAXCOUNT% ) do (

	for %%h in (%IPLIST%) do (

		for /f "usebackq tokens=3,6,9 delims= " %%i in (`ping -n %PINGCOUNT% -w %PINGTIMEOUT% %%h ^| find "Minimum"`) do (
			set MINRES=%%i
			set MAXRES=%%j
			set AVERES=%%k
			echo !date! !time:~,8!,%%h,!MINRES:ms,=,!!MAXRES:ms,=,!!AVERES:ms=!
		)

	)

	timeout /nobreak %INTERVAL% > nul
)

```

# 出力結果

そのまま実行すると標準出力に以下のように
　時刻, 宛先IP, Min[ms], Max[ms], Ave[ms]
のCSV形式で出力されますので、

```
C:\>pingResToCsv.bat
date/time,IP,Min,Max,Ave
2021/01/30 16:08:46,192.0.2.1,2,5,3
2021/01/30 16:08:51,198.51.100.1,2,11,5
2021/01/30 16:08:57,203.0.113.1,22,24,22
2021/01/30 16:09:47,192.0.2.1,2,3,2
2021/01/30 16:09:52,198.51.100.1,2,4,3
2021/01/30 16:09:59,203.0.113.1,24,28,26
  :
```
「pingResToCsv.bat > output.csv」のようにcsvファイルにリダイレクトし、それをExcelでグラフ化するといった使い方ができます。

# 参考

######[・【Windowsバッチ】コマンド実行結果を環境変数に代入する方法 - @RagWork](https://qiita.com/RagWork/items/d8c404cba9d204a719c3)
######[・　Windowsのバッチファイル中で日付をファイル名に使用する - ＠IT](https://www.atmarkit.co.jp/ait/articles/0405/01/news002.html) 
######[・　文字列を置換する方法 - バヤシタ](https://bayashita.com/p/entry/show/294)
vue.jsでIntersection Observerを使って画像遅延を行う

#背景
作成したサイトがSearchConsoleで『改善が必要』との指摘をされた。
画像表示の遅さが原因のひとつであるためIntersection Observerを使って画像遅延読みこみすることにより改善をはかる


#実施したこと
##html
- 対象の画像にrefを設定する
- srcにダミー画像を設定
- data-srcに表示したい画像を設定する
　content_info.eyecatch_img_urlには表示したい画像のパスを入れる

```html
<img class = "index_img" ref="index_imgs" src = "/img/no_img2.png" v-bind:data-src = "content_info.eyecatch_img_url">

```

##javascript
- 対象画像が表示範囲に入った場合の処理を"if (entry.isIntersecting)"ないに記述...(a)
- 下記はsrcにdata-srcを代入する...(b)
- 監視を解除する...(c)
- 監視を設定する...(d)

```js
    var content_table = new Vue({
        el: "#index_content_list",
           ...中略....
        },
        mounted: function(){
            if ("IntersectionObserver" in window) {
                var observer1 = new IntersectionObserver(entries => {
                    entries.forEach(function(entry) {
                        if (entry.isIntersecting) { ...(a)
                            let lazyImage = entry.target;
                            lazyImage.src = lazyImage.dataset.src; ...(b)
                            observer1.unobserve(lazyImage); ...(c)
                        }
                    })
                })
                const observe_elements = this.$refs.index_imgs;  
                observe_elements.forEach(function(lazyImage) {
                    observer1.observe(lazyImage); ...(d)
                });
            }
        },
    })
```

#残件
Intersection Observerがサポートされていないブラウザへの対処
polyfillを使った処理を追加する

```js
if ("IntersectionObserver" in window) {
    ...上記
}else{
    ...polyfillを追加
}
```

#オプションの解説
[IntersectionObserver APIのオプション解説](https://sd2.dozono.net/2020/04/16/intersection-observer.html)


#参考

[vue.jsで無限スクロール(infinite scrolling)](https://reffect.co.jp/vue/vue-js-infinite-scroll-intersection-observer-api)
[SEOを考慮したGoogle推奨の画像の遅延読み込み処理まとめ](https://webtan-tsushin.com/seo-lazy-image-load)
Vimの頻出操作

私自身も初心者ですが、よく使うvimのコマンドを使う順に並べました。

## 1. 通常モードに変更

vimで最初に躓く点はモード変更です。
通常モードへの変更はCtrl + C か Escapeか好きな方を使えば良いのですが遠くのEscapeを左手小指で押しにいくとホームポジションが崩れるのでCtrl + Cを勧めます。

|key|操作|
|:---:|:---:|
|Ctrl + C or Escape|通常モードに変更|

## 2. カーソル移動

通常モードで使えるカーソル移動です。
右手の小指を伸ばして方向キーを操作している人は今すぐ辞めましょう。
挿入モード中はカーソル移動が使えないので細かくCtrl+Cを押す必要があります。

行末の文字の右側へはカーソル移動できない点で躓く初心者もいます。行末に追記できないから方向キーに固執する初心者もいます。挿入モードで方向キーを操作すると行末へ移動できてしまうのが厄介です。行末へ文字を追加したり改行して入力するvim的に正しい方法が用意されているので次項で紹介します。

|key|操作|
|:---:|:---:|
|h|カーソルを左に移動|
|j|カーソルを上に移動|
|k|カーソルを下に移動|
|l|カーソルを右に移動|

## 3. 挿入モードに変更

挿入モードへの変更を覚えてようやくVimの速度と優位性が感じられるようになります。
aやoが身について漸く方向キーから手が離れるようになりました。

|key|操作|
|:---:|:---:|
|i|カーソル位置で挿入モードに変更|
|a|カーソルの右で挿入モードに変更
|Shift + a|行末で挿入モードに変更|
|o|下に改行し、新しい行で挿入モード|
|Shift + o|上に改行し、新しい行で挿入モード|

## 終わりに

この記事は使いこなせるコマンドが増えるにつれて下に追記していく予定です。
効率よく何千行もコードを生産できる人間になりたいものです。
不要Rubyのアンインストール方法

##①Rubyの管理ツールを調べる

```ターミナル.
$which ruby # rubyがインストールされている場所を調べる
#/Users/ユーザー名/.rbenv/shims/ruby

```
↑で、**rbenvかrvmかでこの後のコマンドが変わってくる！**

##rbenvとは
複数のRubyのバージョンを管理し、プロジェクトごとにRubyのバージョンを指定して使うことを可能としてくれるツールです。 

##rvmとは
上記同様。違う点としては、rvm単体でRubyのインストールが可能である点。
rbenvの場合は、別途パッケージ管理ツールが必要。(Homebrew等)


##インストール済みのRubyを確認

```ターミナル.
$rvm list
```

##不要なRubyのバージョンをアンインストール

```ターミナル.
$rvm remove 2.6.3
#上記はversion 2.6.3を削除したい場合

```
【初心者向け】Googleカレンダーからスプレッドシートへ出力・合計し、時間の可視化をする(GAS、関数)

#1.はじめに
Googleカレンダーで予定の管理をしている方は多いのではないでしょうか。

Google Apps Script(GAS)と関数の合わせ技により、
Googleカレンダーから、タイトルが同じまたは同じワードが入っているものの合計時間を簡単に出すことができます！

自分が何に時間をかけていたか、カレンダーとにらめっこすることなく数分で出すことができます！
本当に便利で準備も簡単なので、ぜひやってみてください！

それでは説明していきます！

#2.使うもの
・Googleカレンダー
・Googleスプレッドシート

#3.Googleカレンダーからスプレッドシートへの出力
##3-1.GoogleカレンダーのIDを確認
今回は仮に、「テスト」というカレンダーから出力していきます。
![スクリーンショット 2021-01-30 16.02.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/b3ef07cf-22b0-91a6-74d8-c4354d6d33dd.png)

この「テスト」の設定と共有から、カレンダーの設定の画面に移動して、
カレンダーの統合のところにある、カレンダーIDをメモします。
aabbcc@abcd.com
みたいな形式のはずです。

##3-2.Googleスプレッドシートを用意
新しくスプレッドシートを作り、
Googleスプレッドシートからスクリプトエディタを開きます。
![スクリーンショット 2021-01-30 16.10.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/f79ff002-655b-31b0-6962-3406a626f62f.png)

##3-3.スクリプト
スクリプトエディタに書く内容は以下。

```javascript:GAS
function callender() {
//1　スプレッドシートを読み込む
  const sp = SpreadsheetApp.getActiveSpreadsheet();
  const sh1 = sp.getSheetByName('シート1');

//2　カレンダーをIDで読み込む
  const cal=CalendarApp.getCalendarById('ここに先ほどのカレンダーIDを入れる！'); 

//3　カレンダーのイベントの期間を指定
  const startTime = new Date('2021/01/01 00:00:00');
  const endTime = new Date('2021/02/01 00:00:00');
  const event = cal.getEvents(startTime,endTime); 

//4　イベントをスプレッドシートへ書き出す
  for(var i=1;i<event.length+1; i++){
    sh1.getRange('a'+i).setValue(event[i-1].getTitle());//イベントタイトル
    sh1.getRange('b'+i).setValue(event[i-1].getStartTime());//イベント開始時刻　　
    sh1.getRange('c'+i).setValue(event[i-1].getEndTime());//イベント終了時刻
    sh1.getRange('d'+i).setValue("=round((rc[-1]-rc[-2])*24)");//所要時間　　
  }
}
```
`シート1`のところは、出力したいシート名に合わせて。
`ここに先ほどのカレンダーIDを入れる！`には、メモしてあるカレンダーIDを入れてください。
出力したい期間も今回は2021年の1月にしていますが、適宜変えてください！

こんな感じになっているはず。
![スクリーンショット 2021-01-30 16.18.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/0bef5efe-61fe-c52b-db2e-d4401cd76768.png)


##3-4.実行
実行を押します。
承認が必要です。と出ると思いますが、順に進んでアクセスを許可してください。

![スクリーンショット 2021-01-30 16.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/90373950-29f2-804c-9de8-b8956c9bdc36.png)
シート1にスケジュールがずらっと出力されました。

#4.合計時間の算出
ここから、タイトルごとの時間を出していきます。
##4-1.シート2を作成
まず、新しいシートを追加してください。
##4-2.タイトルと関数を入れる
今回は、「授業」「課題」「バイト」「遊び」の時間をそれぞれ合計したいので、
シート2のA列にタイトルを入力します。

そしてB1に入れる関数がこちら。
`=SUMIF('シート1'!$A$1:$D,A1,'シート1'!$D$1:$D)`

シート1を参照して、シート2のA1の値と一致した行のD列の値を足し算します。
数式で表示するとこうなります。
![スクリーンショット 2021-01-30 17.16.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/ac94e709-953f-37a9-df7f-8d8eefa949c1.png)

シート2に、合計時間が表示されました。
![スクリーンショット 2021-01-30 17.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390448/3db067f7-7275-12cd-d900-cc9defe3e173.png)



#5.まとめ
以上より、あっという間に時間の可視化ができたと思います。
部分一致で検索したり、時間表示を分表示にしたりと、
使い方はいろいろあると思いますので、ぜひやってみてください！！
pythonでpandas_profilingを使ってデータの雰囲気を掴む

google colabでもjupyter labでもこんな感じで
dfを引数に入れるだけでデータの雰囲気を出してくれます
>!pip install pandas_profiling

まずはpandas_profilingをインストール

>import pandas_profiling as pdp
>pdp.ProfileReport(train_df)

これで分析結果がhtmlで出力されるのでかなり便利です
Azure初学者として不安だったこと

はじめに：
　Azureを学習するにあたり、初めて自分の責任の範囲で
　クラウドを取り扱うのに、不安に思ったことに対する記録。

前提：
　無料枠は使ってしまっている状況。

学習を始めたUdemyの講座：
　作りながら覚えるMicrosoft Azure入門講座（IaaS編）
　　https://www.udemy.com/course/microsoft-azure-iaas-part/

不安に思ったこと：
１．課金はどの程度かかるの？
２．セキュリティ・アカウント管理は素人な自分は大丈夫？
　　「１にも関わってくることだが、不正アクセスされて
　　　リソース乱立されたらものすごい課金されるのは困るし、怖い。」

とりあえず不安を払拭しないと
行動出来ないので簡単に調べてみる事とした。

##１．課金はどの程度かかるの？
＜従量課金の注意点＞
参考：https://www.atmarkit.co.jp/ait/articles/1405/09/news093.html

仮想マシンは、シャットダウン（GUI & コマンド）で停止させても従量課金は継続される。　
Azure側のUIで停止を押して、ステータスが「停止済み (割り当て解除済み)」にすることで課金が停止する。

考え方として占有してしまっているリソースには継続して従量課金が発生するようだ。
※パブリックIPアドレス、ストレージの領域など
※マシンスペック、ストレージ性能（IOPS）なども課金額に関わってくる。

どの程度かかるの？＝使ってみないとわからん。

初月の課金額：1,915円でした。(とりあえず自分の財布から出せる金額で安心）
※使い方次第で無尽蔵に従量課金が発生するため、コスト管理はこまめに気にするほうが良い！
　設定した限度額を超えるとメールでアラート通知することも出来る！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664515/eb23f2cd-cf60-5705-4f91-c7edc86ff330.png)

##２．セキュリティ・アカウント管理、素人な自分は大丈夫？
＜アカウント管理・セキュリティ面の担保について＞
こちらは比較的容易に解決した。
多要素認証での認証方式があり、Azureポータルにアカウントでログインを行おうとすると
スマホにインストールしたアプリ「Microsoft Authenticator」に通知が飛び、それに対して承認することでアクセス可能となる方式であったため、セキュリティ面で一定の安心感が得られた。

参考：https://qiita.com/morih90/items/c2755c83430493a9dbd1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664515/7b3540e9-75b4-db60-7f7f-d75faf5b9aeb.png)




=2要素認証でのAzureポータルへのアクセスの流れ図解=
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664515/da0eebdb-ebd7-effb-ac49-dece3fc1be68.png)

=追伸=
Udemy講座についての感想
現在学習継続中、順序立ててハンズオンを踏まえた講座であるため理解が進みやすいです。
次回は、AZ-104 の範囲の中から理解したことを自分なりに記事としてアップする予定。
EXIN Agile Scrum Foundation 合格体験記

#はじめに
新卒1年目のスクラム初心者が、EXIN Agile Scrum Foundation 試験を合格するまでにやったことを共有します。
同試験を受験しようと考えている方の参考になれば幸いです。

#EXIN Agile Scrum Foundation とは
アジャイル手法とスクラム実践に関する知識を保有していることを証明できる資格です。
詳細は[公式ページ](https://embed.exin.totalservices.io/certifications/exin-agile-scrum-foundation-exam?language=ja)に書かれています。

認定資格の範囲には以下の項目が含まれます。

- アジャイル思考法
- スクラム実践
- スクラムの計画と見積
- スクラムプロジェクトのモニタリング
- 上位スクラム　コンセプト

#試験の概要
- 予約方法
    - [Prometric](http://www.prometric-jp.com/)でオンライン予約
- 受験場所
    - [全国のテストセンター](https://www1.prometric-jp.com/reserve/center_map.asp)
- 受験料（2021年1月現在）
    - 27,540円（税抜き）
    - クレジットカード決済のみ
- 試験時間
    - 60分
    - 早く解き終わった場合は途中で退出可能
- 問題数
    - 40問（全て3〜4択の選択問題）
- 合格ライン
    - 65%（26問/40問）

#学習方法
私の場合は以下の教材で勉強しました。

- [アジャイルソフトウェア開発宣言](https://agilemanifesto.org/iso/ja/manifesto.html)
    - アジャイル開発手法における価値観がわかります
- [スクラムガイド2020](https://www.scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf)
    - スクラムの公式ドキュメントであり、これを読めばスクラムの概要がわかります 
- [SCRUM BOOTCAMP THE BOOK](https://www.amazon.co.jp/SCRUM-BOOT-CAMP-BOOK%E3%80%90%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88%E3%80%91-%E3%82%B9%E3%82%AF%E3%83%A9%E3%83%A0%E3%83%81%E3%83%BC%E3%83%A0%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%82%A2%E3%82%B8%E3%83%A3%E3%82%A4%E3%83%AB%E9%96%8B%E7%99%BA/dp/4798163686)
    - 初心者でもスクラムを理解できるように、漫画などで非常にわかりやすく説明してある本です
- 準備ガイド
    - [公式ページ](https://embed.exin.totalservices.io/certifications/exin-agile-scrum-foundation-exam?language=ja)からダウンロードできます
    - スクラムガイドやSCRUM BOOTCAMP だけでは押さえられない語句を洗い出すことができます
- 模擬試験
    - [公式ページ](https://embed.exin.totalservices.io/certifications/exin-agile-scrum-foundation-exam?language=ja)からダウンロードできます
    - 試験の雰囲気を掴めるので、一度は受けておくことをオススメします

#押さえるべきポイント
勉強する上で押さえておくと良いポイントをまとめておきます。

###アジャイルとスクラムの価値観

- アジャイルソフトウェア開発宣言における4つの価値観
- スクラムの価値基準

これさえ理解できていれば、複雑な状況を問う問題にも対応できます。
また、明らかな誤選択肢がわかるようになるので、消去法でも解けるようになります。

###見積もり
それぞれの手順と利点について押さえておくと良いです。

- プランニングポーカー
- 類推見積もり
- 三角測量
- フィボナッチ工数見積もり
- ポイント制と理想時間制

###情報ラジエータ
それぞれ何を示すものなのか、どういう時に使うのかについて押さえておくと良いです。

- バーンダウンチャート 
- バーンアップチャート
- カンバンボード
- ニコニコカレンダー

###大規模なスクラム開発
様々な状況での、開発の進め方や各ロールの立ち回り方について押さえておくと良いです。

- 複数チームによるスクラム開発
    - スクラムマスターの人数
    - スクラムオブスクラム
- グローバルなスクラムチーム

#その他
同じ意味の言葉の言い換えがたまに出てきます。惑わされないように注意してください。

- デイリースタンドアップミーティング（= デイリースクラム・朝会）
- アジャイルマニフェスト（= アジャイルソフトウェア開発宣言）
- スプリント計画会議（= スプリントプランニング）
ディレクトリ配下で特定の文字列を検索して、文字列が含まれているファイル名を出力する

### ディレクトリ配下で特定の文字列を検索して、文字列が含まれているファイル名を出力する
```
 grep -rnw '.' -le 'foo'
```

### ディレクトリ配下で特定の文字列が含まれたファイルの、ファイル名と指定の文字列が含まれた行の一覧表示する
```
 grep -rnw '.' -le 'foo' | while read v; do grep -rnw $v -e 'hoge'; done
```
yarn serveした時に「Cannot find module 'vue-template-compiler'」の対処法

vueプロジェクトを新規作成して`yarn serve`しようとした時、エラーになったので対処法のメモ書きです。

# エラー内容
```bash
 ERROR  Error: Cannot find module 'vue-template-compiler'
Require stack:
- ~/node_modules/@vue/cli-plugin-typescript/lib/tslint.js
- ~/node_modules/@vue/cli-plugin-typescript/generator/index.js
- ~/package.json
Error: Cannot find module 'vue-template-compiler'
Require stack:
- ~/node_modules/@vue/cli-plugin-typescript/lib/tslint.js
- ~/node_modules/@vue/cli-plugin-typescript/generator/index.js
- ~/package.json
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:965:15)
    at Function.Module._load (internal/modules/cjs/loader.js:841:27)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (~/node_modules/@vue/cli-plugin-typescript/lib/tslint.js:7:21)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
```

# 対処方法

cliで使うパッケージが不足していたみたいなので、追加で`@vue/compiler-sfc`を入れます。
FYI: [Cannot find module 'vue-template-compiler/package.json' | GitHub](https://github.com/vuejs/vue-cli/issues/2109)

yarn

```bash
yarn global add @vue/compiler-sfc
```

npm

```bash
npm i -g @vue/compiler-sfc
```
いつもGitHubの初期化の仕方が分からなくなるのでメモ

##はじめに
開発ど初心者です。そして初投稿です。
私、いまだにGitHubが苦手で...

やりたいことは、
**もともとローカルで色々編集していたフォルダを、GitHubのレポジトリにあげたい** 
だけなのです。

でも、


1. GitHubでレポジトリ(例えば"test"という名前にする)を作る

2. ローカルでREADMEやらを作成←ここまではいい

3. ローカルで色々編集したものをtestにプッシュする←ここでなぜかいつもつまる


ということで、メモ代わりにやり方を残しておきます。
何度も言いますが、初心者目線なので、めちゃくちゃに当たり前のことを書いている気がしますがお許しを。

##GitHubのテンプレート、どれをコピーすればいいの？
![スクリーンショット 2021-01-30 15.50.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664067/2d9310eb-3091-db81-8963-5abfab574ed4.png)

こんな感じで、GitHubのレポジトリを作るとなんか3つ選択肢が出るんですが、いつも、

「create a new repositoryって言われても、もうリポジトリ作ったし、２番目の方かな？」

とか思って2つ目をコピペしたら、めちゃくちゃエラーが出てきてすみませんってなるんですよね〜〜

結論から言うと、私のシチュエーションの場合は1個目が一番近いんですが、今回は自分の経験を踏まえて、ちょっとこのテンプレと違うコマンドをメモしていきます。

##流れ
###git init
`git init`
これをしないと初期化は始まりません。

出力は
`Initialized empty Git repository in /data/name/test/.git/`
そして、masterブランチが勝手に作られます。

###git commit -m "first commit"  はなんかエラーが出る？
`git commit -m "first commit" `
これをしてコミットすると、なんかエラーっぽいのが出てきます。

```
ブランチ master

最初のコミット

追跡されていないファイル:
        .gitignore
        README.md
        hoge/
        piyo.py

nothing added to commit but untracked files present
```

これが出る理由はどうやら、「ステージしていなかったから」らしく、
本当は、
`git add piyo.py`
みたいなことをしないといけないらしいです。

でも、もともとローカルでファイルを大量に作っていた場合、作ったファイル全部git addするの？？って感じなので...

VSCodeにおんぶだっこするのが一番楽だということがわかりました！

###VSCodeでコミットする
![スクリーンショット 2021-01-30 16.30.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664067/91e6588f-6ba1-696b-62e2-1b890fbdd118.png)

VSCode左上のここですここ。

Messageのところにコミットメッセージを入れて、二重線ひいたチェックのところを押せば、めちゃくちゃ簡単にコミットできます。

しかも、ステージされてないと、「ステージしますか？」的なメッセージが出て、それに「OK」しちゃえば、勝手にステージとコミットをしてくれちゃいます。便利！

これで何もエラーが出なくなりました。

###git remote add origin git@github.com:name/test.git  
`git remote add origin git@github.com:name/test.git`
これで初めて、自分が作ったレポジトリと連携できます。

###VSCodeでプッシュする
最後に、プッシュ！
これはコマンドでも全然いいですが、私はVSCode派なので、そっちでやります！

![スクリーンショット 2021-01-30 16.38.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664067/f7268964-7ea2-b997-8752-a831f3fe10ae.png)
またしても左上の、「・・・」を押すと、プルダウンで「Push」の文字列があります。

これを押せば、勝手にmasterブランチをプッシュしてくれるのです！わあい！

こうすることで、無事testレポジトリに、もともとあったファイルたちを入れることができました。

##おわりに
多分1から全てをあげるのであれば、GitHubのテンプレ通りにコピペすれば済むのでしょうが、
もともとあったフォルダを一気にあげようとしたから苦戦してたのかなと思います。

初心者、がんばります！
【初学者】なぜエンジニアにアウトプット激推し説が盛んなのか、業界背景を元にまとめてみた。

##はじめに
&emsp;看護師として働く中で、とある出来事からエンジニアを志すようになった私。
&emsp;プログラミングを学習していると、至るところに出てくるアウトプット激推し説。
&emsp;これまでの学習スタイルとしては、特にアウトプットはせずに自身でまとめたり、勉強会に出席した際には、
&emsp;受動的にレポートとして提出することぐらいでしかしてこなかった私...
&emsp;なぜこれほどまで、エンジニアではアウトプットが必要なのか、考えてみた。



##まずIT（information technology）業界とは
★information technology = 情報技術
&emsp;情報技術とは、コンピューターやネットワークなどの情報を処理する技術のこと。
&emsp;その情報技術を利用して、様々なサービスを提供する業界である。

現状IT業界は、ほかの業界と比べ発展途上にある。
まだまだ新しい技術が出ており、それを利点として積極的に取り入れる企業も多く、
日々の技術革新やトレンドの流れが速い業界である。
このため、新しい技術を身につけている人に需要がある一方、
昔ながらの技術や成熟した技術にも、需要があり続けている現状である。



##IT業界で働くエンジニアに求められること
<dl>
  <dt>◉ネットやカリキュラムで記述されていること</dt> 
  <dd>業界変化に素早く対応でき、実力を示し続けられるように、知識や技術を習得し続けることが求められる。</dd>
  <dd>.....なぜ？</dd>
  <br>
  <dt>◉情報収集した結果の持論</dt>
  <dd>IT企業 = 顧客が抱える課題に対し、情報技術を利用してサービスを提供する。</dd>
  <dd>システムエンジニア = 顧客が抱える課題を把握し、解決するためのシステムを設計する</dd>
  <br>
  <dd>エンジニアには、顧客の抱える課題を正確にヒアリングし、提供するサービスを</dd>
  <dd>的確に具現化できる能力が必要である。</dd>
  <dd>そのヒアリングや具現化するスキルも、知識や技術が豊富にあるとより正確性が向上するので、</dd>
  <dd>あるに越したことはない。</dd>
</dl>

##アウトプットがもたらす３つの効果
__１、インプットを定着させ、知識や技術力が向上する__
&emsp;⚫︎インプットした内容を再度学習することと同じである。
&emsp;⚫︎情報のキャッチアップが早くなる。
&emsp;⚫︎他者に分かりやすく説明することを意識して、頭の中でインプットした情報を再構成するため、
&emsp;インプットした内容の理解が進む。
&emsp;⚫︎曖昧な部分があった場合、表現も分かりづらいものとなるため、改めて検討する必要がある。


__２、コミュニケーションツールになる__
&emsp;⚫︎他社（全世界）のエンジニアとも交流することができる。
&emsp;⚫︎アウトプットした内容に対して評価や意見をもらうことができ、
&emsp;適切なアドバイスがもらえれば、自らのスキルを向上させることもできる。

__３、自身のPRになる__
&emsp;⚫︎ポートフォリオやソースコードを公開することで、自分の力量を見てもらうことができる。
&emsp;⚫︎そこでのコメントのやりとりなどから、コミュニケーション能力についても伝えることができる。
&emsp;⚫︎転職活動においては、下記の点において、アピールすることができる。
&emsp;「しっかりと学びを言語化し、次に生かせる人なのか」
&emsp;「プログラミングに対して意欲的に取り組んでいるのか」 


##結論
　　ラーニングピラミッドを用いて振り返る。
<img width="748" alt="スクリーンショット 2021-01-30 12 49 35" src="https://user-images.githubusercontent.com/70472160/106346361-ec8d6400-62f9-11eb-80f9-6bd51f2eb2f6.png">

　　...一目瞭然。


&emsp;&emsp;*__『エンジニアとって、問題解決としてのIT関連の知識と技術は、武器となる。
&emsp;&emsp;インプットとアウトプットを繰り返すことで、それを効率よく習得し、定着を図ることができる。
&emsp;&emsp;そのため、エンジニアにとってのアウトプットは、非常に有効な手段である』__*


##最後に
&emsp;目的が明確に分かることで、学習の意図も見つけやすく、業務内容のイメージもつきやすい。
&emsp;しかし、過去の私といえばプログラミングを学ばなければ、今までの学習スタイルを突き進めていただろう。
&emsp;井の中の蛙大海を知らず。
&emsp;新しいことを知って、自身に取り入れていくのは楽しいことなので、
&emsp;引き続きインプットとアウトプットを繰り返して、社会に還元していきたいと思う。
Ubuntu 16.04でもIntel oneAPIをAPTでインストールしたい

#概要

本投稿は，APTを用いてUbuntu 16.04にoneAPIを導入する手順をまとめたものです．ただし，[Operating System Requirements](https://software.intel.com/content/www/us/en/develop/articles/intel-oneapi-base-toolkit-system-requirements.html)にある通り，oneAPIはUbuntu 16.04を公式サポートしていないので自己責任でお願いします．

UbuntuのLong Term Support(LTS)版はすでに20.04まで出ています．したがって，「えっ，今更16.04？？」とか，「そんなの，OSアップデートすればいいだけじゃん……」と思う方もいるかと思います．(ごもっともです．)
しかし，組織のポリシーや，ワークステーション(WS)購入時の保証，他ソフトウェアの問題でアップデート不可，といった問題で，未だ16.04を使い続けている方も多いはずです．実際私も，今年度導入した某HPC向けSIer製造のWSにUbuntu 16.04がインストールされており，いろいろな兼ね合いもあって致し方なく16.04のまま運用している次第です．Ubuntu 16.04だけどoneAPI試したい，そんな方々のお役に立てれば幸いです．

#インストール方法

実は，Intelが公開している[Installing Intel® oneAPI Toolkits via APT](https://software.intel.com/content/www/us/en/develop/articles/installing-intel-oneapi-toolkits-via-apt.html)の通り実行すれば，APTリポジトリの追加までは滞りなく実行できます．日本語によるインストール解説はすでに，[implicit_noneさんの記事](https://qiita.com/implicit_none/items/35bc4be8f2022903747a)がありますが，本投稿でも解説します．
なお，インストールの前にoneAPI β版のアンインストールを実施する手順がありますが，本投稿をご覧の方には不必要な作業かと思いますので省略致します．また，GPGPUを行う場合は[ドライバのインストール](https://dgpu-docs.intel.com/installation-guides/index.html)が必要となりますが，Ubuntu 16.04でのインストールを確認していないので，こちらも本投稿では省略致します．

## 1. Intelリポジトリの公開鍵を入手する

```sh
cd /tmp
wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
```
[公式ページ](https://software.intel.com/content/www/us/en/develop/articles/installing-intel-oneapi-toolkits-via-apt.html)に従い，/tmpで作業を実施しています．

## 2. APTのキーリングに公開鍵を追加する

```sh
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
```
[公式ページ](https://software.intel.com/content/www/us/en/develop/articles/installing-intel-oneapi-toolkits-via-apt.html)に従い，入手した公開鍵は最後に消去しています．

## 3. APTがIntelリポジトリを認識するよう設定する

```sh
echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
```
ここまでは[公式ページ](https://software.intel.com/content/www/us/en/develop/articles/installing-intel-oneapi-toolkits-via-apt.html)と同様の手順です．

## 4. CMakeリポジトリの公開鍵を入手をする

```sh
wget -qO - https://apt.kitware.com/keys/kitware-archive-latest.asc | sudo apt-key add -
```
Ubuntu 16.04でoneAPIをインストールできない理由として，oneAPIのインストールに必要な最新のCMakeがUbuntu 16.04でサポートされていない点があります．そこで，Cmakeの最新バージョンをインストールできるように，KitwareのCMakeリポジトリを追加します．

## 5. APTがCMakeリポジトリを認識するよう設定する

```sh
sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial main'
```

## 6. APTのパッケージリストを更新する

```sh
sudo apt update
```

## 7. CMakeおよびoneAPIをインストールする

```sh
sudo apt install cmake
sudo apt install intel-basekit
```

ifortが必要な場合はIntel oneAPI HPCkitも必要です．

```sh
sudo apt install intel-hpckit
```

## 8. oneAPIのパスを設定する

```sh
source /opt/intel/oneapi/setvars.sh
```
APTによるインストールのみではoneAPIにパスが通らないので，上記コマンドの実行が必須です．bashrcにでも追記しておくと良いと思います．
以上でインストールは終了です．お疲れさまでした！

# 最後に

APTを用いてUbuntu 16.04にoneAPIを導入する手順をまとめました．私の環境では，上記の方法でiccとifortは動作確認できました．icc及びifort以外のツールに関しては動作確認未実施です．
冒頭にて述べましたが，oneAPIはUbuntu 16.04を公式サポートしていないので自己責任でお願いします．

APTによる管理ができるようになったため，shellによる方法と比べアップグレード作業が楽になりそうですね．
【docker-compose&&run時に「listen tcp 0.0.0.0:xxx: bind: address already in us」エラー対処】

こんちわ〜
UdemyでDokcer講座を購入して、エラーに詰まったので、それの対処法を備忘録として残します！

##「listen tcp 0.0.0.0:xxx: bind: address already in us」 解決手順
- **【エラーログ：原因】**
 -   <code>Error response from daemon: Ports are not available: listen tcp **0.0.0.0:8888**: bind: address already in use.
ERRO[0000] error waiting for container: context canceled </code>
 - どうやら「0.0.0.0:8888ポートが使われてるから、利用できません」と叫んでます

- **【対処法】**
    - ⑴ ポート8888が使われている**プロセス**を確認　
       - **プロセス**とは実行中のプログラムのこと
    - ⑵ **sudo lsof -i -P | grep "LISTEN"**　を叩き確認する
        - **sudo** = スーパーユーザーの権限でコマンド実行する時のコマンド
       
        - **lsof** = プロセスが開いているファイルを表示するコマンド
        - **-i**   =  **ネットワークソケットファイル**を指定する
           - **ネットワークソケットファイル** = ネットワークの接続口のこと
        - **-P** = ポート名をサービスネームに指定しない
        - **grep** = 指定した文字列（のある行）を検索するときに使うコマンド
        - **"LISTEN"** = 通信機能を持つソフトウェアが、外部からのアクセスに備えて待機すること<br>(grepの検索される文字列)
          - 【参考記事:lsofについて】<br>https://qiita.com/hypermkt/items/905139168b0bc5c28ef2
          - 【参考記事:sudoについて】<br>https://wa3.i-3-i.info/word11269.html
          
- **【表示内容】**
  - <code>httpd     40812        ****   4u  IPv6 0x5d70488c5d7bfe07      0t0  TCP *:8888 (LISTEN)</code>
- **【プロセスを終了するコマンド】**
  - kill プロセス番号(PID)
  - <code>kill 40812</code>

よし！！これでdocker run できる！！！
terraformでIAMのRoleとPolicyを同時に作成・削除する

#はじめに
IAMの管理をterraformで行う際、RoleとPolicyを同時に作成、削除を行ったら思い通りの挙動ができずハマったのでその対処。
以下はLambda用に新たにロールを作成し(ロール名：test-lambda-role)、AmazonS3FullAccessをアタッチする例となります。

#同時に作成・削除ができないパターン

```hcl:

resource "aws_iam_role" "test_lambda" {
  assume_role_policy = <<POLICY
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      }
    }
  ],
  "Version": "2012-10-17"
}
POLICY

  max_session_duration = "3600"
  name                 = "test-lambda-role"
  path                 = "/"
}

resource "aws_iam_role_policy_attachment" "test_lambda_AmazonS3FullAccess" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
  role       = "test-lambda-role"
}
```

上記の書式で`terraform apply`を実行すると以下のエラーが表示されます。

##作成時のエラー
```
Error: Error attaching policy arn:aws:iam::aws:policy/AmazonS3FullAccess to IAM Role test-lambda-role: NoSuchEntity: The role with name test-lambda-role cannot be found.
status code: 404
```

##削除時のエラー
```
Error: error deleting IAM Role (test-lambda-role): DeleteConflict: Cannot delete entity, must detach all policies first.
status code: 409
```

どうやらRoleとPolicyがうまく関連付いておらず、順序性が無いためにエラーとなっているように見えます。

#同時に作成・削除ができるパターン
```hcl:


resource "aws_iam_role" "test_lambda" {
  assume_role_policy = <<POLICY
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      }
    }
  ],
  "Version": "2012-10-17"
}
POLICY

  max_session_duration = "3600"
  name                 = "test-lambda-role"
  path                 = "/"
}

resource "aws_iam_role_policy_attachment" "test_lambda_AmazonS3FullAccess" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
  role       = aws_iam_role.test_lambda.name
}
```

先のパターンとの違いは、`aws_iam_role_policy_attachment`での`role`の指定を固定文字列ではなく、
`("type").("local").("name")`といったterraform内での参照方法とした記法としたことです。

・NGな書き方 : role       = "test-lambda-role"
・OKな書き方 : role       = aws_iam_role.test_lambda.name

#何が起きていたか
roleの指定方法を変えた場合に何が起きていたのか、tfstateを覗くと違いがありました。
OKだったパターンでは、`aws_iam_role_policy_attachment`の中に`dependencies`という項目が追記されていたのでした。

```json
{
  "type": "aws_iam_role_policy_attachment",
  "instances": [
    {
      "dependencies": [
        "aws_iam_role.test_lambda.name"
      ]
    }
  ]
},
```
#おわりに

リソースの指定は固定文字列ではなく、terraformの言葉で書くようにすると幸せになれると思いますので、　事情がなければこちらを使おう。