OmniAICreator/Qiita-1.07M huggingface dataset:
GAS Web app をClaspからデプロイ

webAppも作らないとなので、Claspでデプロイまでできると嬉しいなっと。

#1.普段のやり方
　Claspでpushしてバージョン決めて、
  スクリプトエディターから「公開」→「ウェブアプリケーションとして...」
　これがひと手間かかっていたのでClaspだけでできないかな、と。

#2.情報すくない＆コマンドがなくなったのが罠
　少なくとも日本語検索では引っかからず。英語のところでやっと。
　redeployというコマンドが鍵っぽかったけど、いつの間にか削除されてた
　このためほとんどの記事が無意味に。

#3.現状の正解っぽいやつ
[Github](https://github.com/google/clasp/issues/63)を探してたらそれらしきものがあった。
その中でｇｄｇｄ議論があって、下の方に、
WebAppのデプロイの版をアップデートするだけじゃん（超意訳）とあったのでそれを真似る

**ステップ１　appsscript.jsonにwebAppのエントリを作る**

```appsscript.json
  "webapp": {
    "access": "ANYONE_ANONYMOUS",
    "executeAs": "USER_DEPLOYING"
  },
```
webapp.executAs は以下から選択
USER_ACCESSING, USER_DEPLOYING, SERVICE_ACCOUNT, UNKNOWN_EXECUTE_AS

webapp.access は以下から選択
UNKNOWN_ACCESS, DOMAIN, ANYONE, ANYONE_ANONYMOUS, MYSELF

**ステップ2 １回スクリプトエディターでWebAppとして公開する**
これで、デプロイの中にWEBAPPのデプロイ版ができる
clasp deploymentsコマンドでデプロイリストを出してみる

> clasp deployments
4 Deployments.
- AKfyXXXDlsI @HEAD 
- AKfyYYYI6fY @2 - test2
- AKfyZZZP04n @2 
- AKfyAAAvqg @1 - web app meta-version

このweb app meta-version のdeployID(AKfyAAAvqg)をコピーしておく

**ステップ3 コードを更新したらclaspコマンド**

>clasp push
clasp deploy -i AKfyAAAvqg 

pushでGASを更新し、deployのiコマンドで更新するデプロイの版を指定してアップデート
これでWEBAPPのデプロイ版をアップデートしてくれる

**ステップ4 ブラウザで確認**

https://script.google.com/macros/s/{deploymentID}/exec

のdeploymentIDに自分のWebApp版でデプロイしたIDで置き換えてアクセスすれば開けた

#4.備忘録
今日時点ではこれで動いてるけど、clasp createの際にwebappを指定してもappsscript.jsonには上記のエントリはないので、いつか更新されるのを期待
Gitのインストール

## Introduction

分散型バージョン管理システムのGitのインストールを行うための一般的な方法（ソースからインストールしない方法）について記載する。

## Method

### macOS

macOSではGitがプリインストールされているが、最新バージョンではないのでアップデートすることが望ましい。[^1]

GUIでインストールする場合は[git公式](https://git-scm.com/)より　\>Downloads>Mac OS X　からインストーラーをダウンロードして実行する。ダウンロードは自動で行われる場合がある。

セキュリティの警告が出る場合、**システム環境設定\>セキュリティとプライバシー\>一般**からインストーラー起動の承認を行う必要がある。

#### Homebrewを利用する場合

Homebrewをインストールしている場合、以下のコマンドでもインストールが可能である。GUIでのインストールとは排他的なので注意されたい。

```Homebrew
brew install git
```

### Windows 10

GUIでインストールする場合は[git公式](https://git-scm.com/)より　\>Downloads>Windows　からインストーラーをダウンロードして実行する。ダウンロードは自動で行われる場合がある。手動で任意のインストーラーを選ぶこともできる。

インストール時にはオプションの選択が求められる。オプションの選択に応じてコマンドプロンプトでの使用の可否が左右されるが、インストール後にも変更可能である。

#### Chocolateyを利用する場合

Chocolateyをインストールしている場合、以下のコマンドでもインストールが可能である。GUIでのインストールとは排他的なので注意されたい。

```chocolatey
choco install git
```

または

```chocolatey
cinst git
```

なおChocolateyではインストール時のオプション設定が省略される。

### Linux/Unix

[git公式](https://git-scm.com/)より　\>Downloads>Linux　からディストリビューション毎に記載されているコマンドを実行する。代表的なディストリビューションでのインストールは以下のとおり。

#### Ubuntu

```apt
apt install git
```

#### Arch Linux

```pacman
pacman -S git
```

なお2020年3月3日現在、Red Hat Enterprise LinuxやCentOSでは公式で配布されるgitのバージョンが古い。tarballをダウンロードしてソースからのビルドするか、サードパーティーのリポジトリで最新版を使用できる。

## Result

ターミナルやGit Bush、コマンドプロンプトなどに以下のコマンドを入力してバージョンを表示してインストールされたGitのバージョンを確認できる。以下に例示する。

- macOSとターミナル（Z Shell）
 
```
> git --version
git version 2.25.0
```

- Windows 10とコマンドプロンプト

```
$ git --version
git version 2.25.1.windows.1
```

- Arch LinuxのBash

```
$ git --version
git version 2.25.1
```

## Discussion

Gitをインストールしバージョンを確認した。Windows 10やmacOSではGUIのインストーラーが標準だが、ChocolateyやHomebrewなどの環境があればLinux/Unix同様にコマンドでのインストールも可能で、こちらの方が簡便である。ただしGUIのインストーラーと併用すると両者が競合するので、どちらかに統一することが望ましい。

なおインストールされるGitのバージョンはインストールに使用するツールやLinuxのディストリビューションの違いなどによりそれぞれ異なる場合がある。実用上問題が起こる可能性は低いと考えられるが、もし必ず最新版を使いたい場合はソースからインストールする必要がある。[^2]

## Reference

[git公式サイト](https://git-scm.com/)
[^1]: [最新の Git を Mac にインストールする手順](https://qiita.com/normalsalt/items/f200ba50363ebfd46df0)
[^2]: [git公式 -  1.5 使い始める - Gitのインストール](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)
pythonのジェネレータのコードサンプル

# 事の発端
pythonで設定ファイルを作っていて、ジェネレータの使い方をすぐに忘れてしまうので、備忘のためにサンプルを作成・公開しました。

## コードサンプル
``` python3
def bundle_dict_set(dic, num_max, part_name_of_key, init_value=0):
    for i in range(1, num_max + 1):
        dic[str(i) + part_name_of_key] = init_value


def page_data_gene():
    for i in range(1, 3 + 1):
        yield str(i) + "ページ目の色"
        yield str(i) + "ページ目の幅"


def bundle_gene_dict_set(dic, init_value=0):
    for key in list(page_data_gene()):
        dic[key] = init_value


def main():
    dic_1 = {}
    dic_2 = {}
    dic_3 = {}

    print("=========↓for文で辞書を作成する場合=========================")
    for i in range(1, 3 + 1):
        dic_1[str(i) + "ページ目の色"] = 0
        dic_1[str(i) + "ページ目の幅"] = 0
    print(dic_1)

    print("\n=========↓for文を関数で実行して辞書を作成する場合===========")
    bundle_dict_set(dic_2, 3, "ページ目の色")
    bundle_dict_set(dic_2, 3, "ページ目の幅")
    print(dic_2)

    print("\n=========↓ジェネレータを使って、辞書を作成する場合==========")
    bundle_gene_dict_set(dic_3)
    print(dic_3)


if __name__ == "__main__":
    main()

```
## 実行結果
```
=========↓for文で辞書を作成する場合=========================
{'1ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の色': 0, '2ページ目の幅': 0, '3ページ目の色': 0, '3ページ目の幅': 0}

=========↓for文を関数で実行して辞書を作成する場合===========
{'1ページ目の色': 0, '2ページ目の色': 0, '3ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の幅': 0, '3ページ目の幅': 0}

=========↓ジェネレータを使って、辞書を作成する場合==========
{'1ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の色': 0, '2ページ目の幅': 0, '3ページ目の色': 0, '3ページ目の幅': 0}
```

## 追記）さらに、コードをかっこよくしてみた
``` python3
def bundle_dict_set(dic, num_max, part_name_of_key, init_value=0):
    for i in range(1, num_max + 1):
        dic[str(i) + part_name_of_key] = init_value


def page_data_gene(*args, max_num):
    for i in range(1, max_num + 1):
        for arg in list(*args):
            yield str(i) + str(arg)


def bundle_gene_dict_set(dic, *key_list, max_num, init_value=0):
    for key in list(page_data_gene(*key_list, max_num=max_num)):
        dic[key] = init_value


def main():
    tpl = ("ページ目の色", "ページ目の幅")
    page_num = 2
    dic_1 = {}
    dic_2 = {}
    dic_3 = {}

    print("=========↓for文で辞書を作成する場合=========================")
    for i in range(1, page_num + 1):
        dic_1[str(i) + tpl[0]] = 0
        dic_1[str(i) + tpl[1]] = 0
    print(dic_1)

    print("\n=========↓for文を関数で実行して辞書を作成する場合===========")
    for tp in list(tpl):
        bundle_dict_set(dic_2, page_num, tp)
    print(dic_2)

    print("\n=========↓ジェネレータを使って、辞書を作成する場合==========")
    bundle_gene_dict_set(dic_3, tpl, max_num=page_num)
    print(dic_3)


if __name__ == "__main__":
    main()

```

## 実行結果
```
=========↓for文で辞書を作成する場合=========================
{'1ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の色': 0, '2ページ目の幅': 0}

=========↓for文を関数で実行して辞書を作成する場合===========
{'1ページ目の色': 0, '2ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の幅': 0}

=========↓ジェネレータを使って、辞書を作成する場合==========
{'1ページ目の色': 0, '1ページ目の幅': 0, '2ページ目の色': 0, '2ページ目の幅': 0}
```
# 感想
特になし。
WordPressのAPIでログインしてるかチェックしたい

# はじめに
WordPressで独自APIを作ったときなどで、ログインしているユーザーにしか叩いて欲しくないAPIありますよね。
最初は、APIのコードの中で`is_user_logged_in()`とかやって、ログインしてなかったら弾けばいいや。と思ってましたが、話はそう簡単ではなかった。

[WP REST APIの公式の認証](https://ja.wp-api.org/guide/authentication/)の「クッキー認証」ところを読みましたが、結構ハマったので書いておきます。

ちなみに、ここに書いた方法が正しいかどうかはよくわからないけどね...

独自APIの作り方は[こちら](https://qiita.com/RinT_T/items/808941ba52ffa4b51833)を参考にしています。

# 流れ
APIの中ではユーザーがログインしているかどうかは判定できません。（なんでかは分からんけど）
というわけで、[WordPressのnonce](https://wpdocs.osdn.jp/WordPress_Nonce)を使用して、APIを叩く権限があるかどうかチェックしていきます。

今回は管理画面でのみ使用できるAPIを想定して説明します。

## nonceを作成

```php

//管理画面用のcssとjsを読み込む
add_action( 'admin_enqueue_scripts', 'load_custom_wp_admin_style' );

function load_custom_wp_admin_style($hook) {
    $apiArgs = [
        'root' => esc_url_raw( rest_url() ), //APIのルートURLが入ってます
        'nonce' => wp_create_nonce( 'wp_rest' ) //nonceの名前は「wp_rest」にします
    ];

    wp_enqueue_script( 'master-js', plugins_url('master.js', __FILE__), ['jquery'], '1.0');
    wp_localize_script( 'master-js', 'WP_API_Settings', $apiArgs );
}

```

### 解説
`wp_enqueue_script`の第一引数と、`wp_localize_script`の第一引数を揃えます。
ここで、どのjsファイルに紐付けるか設定しています。

`wp_localize_script`の第二引数`WP_API_Settings`は、その名前でオブジェクトを作成しますよと言っています。
`wp_localize_script`の第三引数で、オブジェクトに設定したい値を入れています。

今回は、「wp_rest」という名前でnonceを作成しました。

管理画面の対象のページに移動して、ページのソースを表示して、`WP_API_Settings`で検索してみてください。

![wp-api-setting-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392006/7400d076-13ea-df12-5cbb-a408d71b03c5.png)


このような感じで変数が出力されていると思います。
変数が出力されていなかったら、どこか間違っていますので修正してください。

## nonceをjs側で使用

続いて、JavaScript側に移ります。

```js

$.ajax({
    url: WP_API_Settings.root + '/wp/v2/test_api',
    type: 'POST',
    beforeSend: function ( xhr ) {
        xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
    },
    data: saveData,
})
.done( (data) => {
    alert('成功しました')
})
.fail( (data) => {
    alert('失敗しました')
})

```

### 解説
先程、`wp_localize_script`で作成した`wp_localize_script`を使用しています。
nonceは、`beforeSend`を使ってHTTPのヘッダーとして渡します。


## nonceをphp側でチェック

``` php
function test() {
    $response = new WP_REST_Response();

    $nonceResult = wp_verify_nonce($_SERVER['HTTP_X_WP_NONCE'], 'wp_rest');
    if(!$nonceResult) {
        $response->set_status(403);
        return $response;
    }
}

```

### 解説
`wp_verify_nonce`でnonceが正しいかチェックしています。

js側でヘッダーにセットした値は、`$_SERVER['HTTP_X_WP_NONCE']`で取得します。
※php側で受け取るときは、なぜかすべて大文字になりますのでご注意。

最初に`wp_rest`という名前で、nonceを作成したので、同じ名前でチェックします。


# まとめ
だいたいWordPressのAPIといえば、記事の取得とか誰が叩いてもOKなのが多いんですけど、そうでない場合もたまにありました。
全部のAPIが管理者しか叩けないようにしたい場合は、APIのフックがあって、そこで管理者かどうかのチェックを入れるコードもググったら出てきました。

途中で諦めそうになった...
複数のAWSアカウントにterraformを実行する

## やりたいこと
terraform workspaceを使って、複数AWS環境にterraformを実行したい

## 前提
この記事ではhogeプロジェクトに対して、dev/stg/prdそれぞれの環境を別AWSアカウントに作りたいという前提にします。
また各アカウントでリソースを作成する権限をもったaccess_key / secret_keyはすでに払い出してもらっており、それをterraformを実行するマシンの$HOME/.aws/credentialsに保存しているものとします。

## 実装
最終的な実装は以下の様な形になりました。

```~/.aws/credentials
[dev-hoge]
aws_access_key_id=AKIAXXXXXXXXXXXXXX
aws_secret_access_key=eD1vNSXXXXXXXXXXXXXXXXXXXXXXXXXXXX

[stg-hoge]
aws_access_key_id=AKIAXXXXXXXXXXXXXX
aws_secret_access_key=eD1vNSXXXXXXXXXXXXXXXXXXXXXXXXXXXX

[prd-hoge]
aws_access_key_id=AKIAXXXXXXXXXXXXXX
aws_secret_access_key=eD1vNSXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

```provider.tf
terraform {
  backend "s3" {
    bucket                  = "<bucket_name>"
    key                     = "terraform.tfstate"
    region                  = "ap-northeast-1"
    dynamodb_table          = "<dynamo_db_table_name>"
    shared_credentials_file = "$HOME/.aws/credentials"
    profile                 = "dev-hoge"
  }
  required_providers {
    aws = ">= 2.43.0"
  }
}

provider "aws" {
  shared_credentials_file = "$HOME/.aws/credentials"
  profile                 = "${terraform.workspace}-hoge"
  region                  = "ap-northeast-1"
}
```

2つの権限が必要になります。
1. terraform stateの保存先であるS3を参照する権限
2. terraform実行時に対象になるアカウントに対してリソースを作成する権限
1は上のコードのterraform {} に対応し、2はprovider "aws"の部分に対応します。

ポイントとしては、terraformは`${terraform.workspace}`にそれぞれworkspaceの環境名を埋め込んでくれるので、ここで読むべきprofileを切り替えています。

## 実行
```
## 開発環境への適用
terraform workspace select dev
terraform plan
terraform apply 

## 検証環境への適用
terraform workspace select stg
terraform plan
terraform apply

## 本番環境への適用
terraform workspace select prd
terraform plan
terraform apply
```

以上です。
もっと違うやり方だったり、自分はこうしているなどありましたら教えて下さい。
本日の精進:Union-Find木

Union-Find木については以下の記事が非常に分かりやすく纏めています。↓
[Union-Find木の解説と例題 by ofutonfuton氏](https://qiita.com/ofutonfuton/items/c17dfd33fc542c222396)
以下，UnionFindは上記記事のものを使用しています．

[ATC001-B Union Find](https://atc001.contest.atcoder.jp/tasks/unionfind_a)
UnionFind木の基本的問題。まずノード数Nが与えられる。次に流れてくるクエリに対して、適切な処理を行うという問題。クエリは以下の2種類。
・連結クエリ：ノードa,bを結合する。
・判定クエリ：ノードa,bが連結であれば"Yes"、そうでなければ"No"をそれぞれ返す。

以上の2種類のクエリは、一般的なUnion-Find木の"結合操作"、"判定操作"でそのまま行うことができる。

```AC.cpp

int main(void) {
  int N,Q; cin >> N >> Q;
  UnionFind tree(N);
  rep(i,Q){
    int p,a,b;cin>>p>>a>>b;
    if (p==0) tree.unite(a,b); //a,bを結合する
    else if (p==1){
      if (tree.same(a,b)) cout << "Yes" << "\n"; //同じ木に属するなら"Yes"
      else cout << "No" << "\n";
    }
  }
  return 0;
}
```


[ABC097-D Equals](https://atcoder.jp/contests/abc097/tasks/arc097_b)
指定された操作(配列における位置の入れ替え)群によって、配列をどれだけ順序通りにできるか、という問題。ここで言う順序通りとは、nums[i]==iである、という意味。
一見すると全くUnion-Find木を使うようには見えないが、紐解いていくと、
①入れ替えを実行できる組に辺を繋いでいく
②すると、ある木に属するノード同士は自由に入れ替えられる
③つまり、木の中で入れ替えにより順序通りにできるようなノード群が含まれているかを逐一調べればよい
と、Union-Find木による高速な動作が役に立ちそうな問題に帰着する。
自分のACコードでは入れ替え操作を愚直に行っているが、よくよく考えれば実際に入れ替えを行う必要は全くなく、③を満たすようなノードの数を数えればよい。ACコードの下にカウントだけ行うバージョンも追加した。
ただ、両者で実行時間はあまり変わらなかった。意外。

```AC.cpp

int main(void) {
  int N,M; cin >> N >> M;
  vector<int> nums(N);
  rep(i,N){
    cin >> nums[i];
    nums[i]--;
  }
  UnionFind tree(N);
  rep(i,M){
    int x,y; cin >> x >> y;
    tree.unite(x-1,y-1);//入れ替えできる配列箇所の組を辺で繋ぐ
  }
  while(true){
    int flag = 0;//入れ替えフラグ
    rep(i,N){
      if (nums[i]!=i && tree.same(i,nums[i])){//順序通りになっていない箇所であって、順序通りに入れ替え可能である(=順序通りにするために入れ替えるべき箇所が同じ木に属する)
        int temp = nums[nums[i]];
        nums[nums[i]] = nums[i];
        nums[i] = temp;
        flag = 1;
      }
    }
    if (flag==0) break; //入れ替えが発生していない=可能操作内では最大限順序通りになった
  }
  int ans = 0;
  rep(i,N){
    if (nums[i]==i)ans++;//順序通りになっている箇所のカウント
  }
  cout << ans << endl;
  return 0;
}
```

```AC(入れ替えなしバージョン).cpp

int main(void) {
  int N,M; cin >> N >> M;
  vector<int> nums(N);
  rep(i,N){
    cin >> nums[i];
    nums[i]--;
  }
  UnionFind tree(N);
  rep(i,M){
    int x,y; cin >> x >> y;
    tree.unite(x-1,y-1);
  }
  int ans = 0;
  rep(i,N){
    if (nums[i]==i) ans++; //そもそも順序通りなら、答えに加算する
    else if (nums[i]!=i && tree.same(nums[i],i)) ans++; //順序通りになっていない箇所であって、順序通りに入れ替え可能である(=順序通りにするために入れ替えるべき箇所が同じ木に属する)
  }
  cout << ans << endl;
  return 0;
}

```
保存した画像ファイルを回転させる方法

#はじめに
ある日社内のアプリにて、画像収集アプリを作っていたときの話です。
ユーザーによって写真を撮る向きがバラついているため、
確認画面で画像ファイルを回転したいYOと要望をいただきました。


#想定読者
画像をくるくるしたい方


#事前準備
ImageToolboxをインストール
https://www.outsystems.com/forge/component-overview/2/imagetoolbox

imagemagickをラップしたモジュールとなっているみたいです。


#ImageToolboxでできること
サンプルを確認すると画像に様々なエフェクトを与えることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130275/535c0e09-afd9-0e79-a3ab-a1576b12626d.png)

今回はRotate(-rotate90)を利用します。

#実装

回転させるBinaryデータと写真を回転させるボタンを準備
※ここのBinaryデータはDBに格納しているものを表示させる前提です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130275/90621dc3-f6ca-6456-9d02-c08c3303345f.png)


####1.Preparation時に、Binary変数に対象のレコードのBinaryデータを格納


####2.デコード処理
デコードに失敗すると画面には表示されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130275/a095a908-5c73-8aa6-7d08-a1b7eb71fa5b.png)

####3.画像回転ボタン押下して反転

任意のアクションを作成し、Binary変数の値をImageToolboxのConvert処理にかけ、
AjaxRefreshで画面の画像に上書きするようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130275/831c9857-68b1-f67f-d084-186bc700d6c0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130275/f8b32bcc-ba5e-31cc-ed76-6abfec5abb9d.png)

##見えた課題
回転ボタンを押下した後に回転が反映されるまでちょっと時間がかかります。
もっと早くしたいなぁと・・

#終わりに
知らないだけでけっこう便利なモジュールは意外にあるんですね:D
必須パラメータと任意パラメータ

#Route::get('hello/{name}',function ($name) { });

題のような必須パラメータはパラメータの部分

```

Route::get('hello/{ここのこと}',function ~~~

```

が空の時はサイトがNOT　FOUNDになる。
それでは、実用性があまりないですよね。


そんな時、空でもエラー出なく表示させるために任意パラメータを使います。

```

Route::get('hello/{name?},function ($name='no name.') { 
　　　　<p>こんにちは、{$name}さん</p>
});

```

パラメータの後に、？をつけて
URLのパラメータの部分が空だった時に代入する内容を[’’]で囲むと任意パラメータになります。


すると結果は

```
こんにちはno nameさん
```

こうなります。
参考にしたいポスティング広告

企画者の想いが伝わってくる工夫されたポスティング広告の備忘録。

水回りの修理業者さんのマグネット。
![iOS の画像_ki.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162913/d5f99bd4-652b-ee47-a40d-4016bb0afe2b.png)

可愛くて定規として使える。
「いかにして捨てさせないか」をよく考えられてると思った。

さらに最近はビックリマンシール（レア）のようなものも。
![iOS の画像_ki2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162913/6ca398b9-e5a6-6de7-9cec-896243d17194.png)

普通に集めたくなる、、、

あとはガソリンスタンドのチラシ。
![iOS の画像 (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162913/6ad50fc4-3ecb-d381-2299-1eb0edb8047f.jpeg)

車に乗らない生活だし全く興味がなかったが、三角くじのワクワクに勝てずつい開けてしまった。
![iOS の画像 (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162913/33dae67b-7a58-0b76-0217-75f4a4b4ab77.jpeg)

紙の広告は年々減りつつありますが、こういった工夫一つで消費者の心を捉えることが出来ると思います。良い刺激をいただきました。
IBM CloudのDb2をスケールさせる

#はじめに
「IBM CloudのDb2をスケールさせる」って言うとカッコ良く聞こえますが、単にDisk容量を増やしただけの記事です。ですが手順、表示される実画面、処理時間など、実際やってみると、「気づき」がありましたので共有させてください。


#クラウドなのにDiskが一杯？
事の起こりは下図のような「SQL0968C ファイルシステムがいっぱいです。SQLSTATE=57011」エラーが始まりでした。
![SQL0968C_20200226.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/1667e34d-7d77-9c92-4df1-d656b19b6891.png)

クラウドなので容量不足になるなんて思っていませんでした。私が使っているIBM CloudのDb2 Flexプランには「￥102.00 JPY/GB of Storage (for each node)」と書いてますが「容量が不足したら自動的に1GB単位102円で増加します」とは書いていません。自分で明示的に増やす必要があると分かると[以下のような記述](https://cloud.ibm.com/apidocs/db2-on-cloud/db2oc_scale_exp#scale-a-service-instance)がすぐに見つかりました。
![0.ScaleServiceInstance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/b5f23372-aab4-0592-133f-23c81e3495eb.png)
2GB,10GB,25GB,50GB,100GB,150GB,250GB,500GB,1TB,2TB,4TB単位で増やせる（増やさないと一杯になる）わけです。自動的容量が増えると勝手に解釈していた「おめでたい」自分が恥ずかしくなりました。

#Scale Instanceメニューを使う
明示的に自分で増やさないとイケない・・と思うと、今まで見えていなかった管理画面の「Scale Instance」の文字がドーーンと目に入ってきました。
![0.ScaleInstance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/498a15f1-c9b4-68a6-7eb4-03628f894005.png)


上図の「Scale Instance」をクリックすると、下図のようにストレージが2GBしかない事が分かります。これを右側に引っ張って10GB,25GB,50GB,100GB・・・と増やすします。

![1右に引っ張る.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/897783d1-1690-b139-d11b-c030f3e27937.png)


10GBにすると2GBの時19300円/月だったのが20116円/月となりそうです。
「Choose Timeline」で「Immediate」を選択して即時適用としてみました。
![2.chooseTimeLine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/39ca1215-c4fe-c1ff-89ae-c648d3de432f.png)

確認画面が表示されるので「Notification Email」にメールアドレスを入れて「Confirm Request」をクリックします。
![3.RequestToScaleInstance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/3c0283f0-27af-513b-b739-e6b131200140.png)

下図のように「Success」表示されるまでドキドキしながら20分程度要しました。
![6.Success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188447/59cab0fe-a457-fb5b-ff5c-3d4f95c201b4.png)




#反省
クラウドは「使ったら使った分だけ支払う」のような営業トークを技術的な設定と混同してしまいました。あえて言うなら「契約したら契約した分使って支払う」と思いました。
[android studio] ideavim使っていてマクロが効かない時

# はじめに
マクロを設定してショートカットに割り当てたんだけどキーが実行されなかったので調べました

マクロでできることはプラグインに頼らないほうが良さそうだと思ったので。こちらの記事を参考。
https://qiita.com/chocomelon/items/46810763a5be0a433158

# 解決法
Settings >> Editor >> Vim Emulation

**対象のShourtcutのHandlerをIDEにする。**

VIMになっていると駄目。
オンラインミートアップに使えるチェックインシステムの紹介

昨今のコロナウィルスの影響により、オンラインイベントが盛んに行われています。そんな中、受付システムがうまく提供できないのが問題でした。全体的に参加率が低めになる傾向がありますし、誰が参加していて、誰が参加していないのかも分からないのは問題です。もちろんYouTube Liveのように誰でもすぐに参加できる仕組みもありますが、それでも事前に参加登録している方くらいは分かっておきたいと思うものでしょう。

ということで、connpass用にオンラインイベントのチェックインシステムを作成したので、紹介します。Googleスプレッドシート + Google Apps Scriptで作っているのでサーバレスで使えます。

## ユーザ側の仕組み

ユーザ側の仕組みは簡単です。まずイベント管理者から送られてくるURL（全員共通）にアクセスします。そうすると、以下のようにチェックイン用の画面が表示されます。

<img width="1275" alt="ScreenShot_ 2020-03-03 14.19.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e9717214-2f02-d193-8b26-14081f44e29d.png">

ここでconnpassの受付票の番号を入力します。受付票のURLは分かっているので、クリックしてログインすれば自分の受付番号が表示されます。

<img width="1319" alt="ScreenShot_ 2020-03-03 14.30.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/49257396-c764-6ed1-180b-3f4efdd8b585.png">


受付番号を入力した後、チェックインボタンを押します。正しい受付番号であれば、以下のようにモーダルが表示されます。モーダル内のURLをクリックすれば、配信用URLにジャンプできます。

<img width="642" alt="ScreenShot_ 2020-03-03 14.19.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/1f19208c-e990-563e-ad51-08439d2806d8.png">



番号が間違っている場合はアラートが出ます。 **なお、番号があっていても、connpassの参加者データが古い場合もこのエラーになってしまいます**。

## 管理者側の仕組み

管理者側はGoogleスプレッドシートで操作します。メニューにconnpassが追加されているので、それを選びます。まず参加者取り込みを選びます。

<img width="750" alt="ScreenShot_ 2020-03-03 14.52.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/1c184c8f-1a22-b05c-e2aa-509d9ed09fd0.png">


この処理はconnpassにログインして、参加者一覧をCSVから取り込みます。必要な情報は下記の通りです。この辺りの情報は初回実行時にプロンプトで聞かれるので準備しておいてください。

- connpassのログインID
- connpassのパスワード
- イベントページのURL

connpassのログインIDとパスワードはUserProperties、イベントページのURLはDocumentPropertieに保存しています。何度もログインしてconnpassに負荷をかけるのはよくないので、ログインが成功した際には、そのセッション情報をUserPropertiesに残しています（セッション切れの場合は再度ログインを行います）。

CSVが取得できたら、participatesシートにその内容をべた貼りしています。

<img width="1261" alt="ScreenShot_ 2020-03-03 14.20.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/40c311b2-a291-030e-cfaa-3c742c1a4083.png">


### 配信用のURL設定

オンラインイベントの配信用URLが決まったら、connpassメニューのオンライン配信URL設定を選びます。そしてプロンプトでURLを設定します。

<img width="387" alt="ScreenShot_ 2020-03-03 14.20.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/23f88760-a398-63a5-6440-ea655a2c0abb.png">

### チェックインデータについて

参加者取り込みを行うと全データが差し替えになるので、チェックインデータは attendees シートに追加されるようになっています。同時にチェックインした場合は現時点では考慮されていません（上書きされるかも…）。イベントが終わった後、LOOKUP系の関数でparticipatesシートと照らし合わせればいいかと思います。

## 配布について

Googleスプレッドシートで作った場合、簡単に配布ができないのが難点です。初期不具合があっても対応が難しくなるので、もし興味がある人がいれば <a href="mailto:atsushi@moongift.jp">atsushi@moongift.jp</a> または<a href="https://twitter.com/goofmint">@goofmint</a>までご連絡ください。シートを共有して利用してもらいつつ、デバッグに協力してもらえれば。
ubuntu18.04上でdjango動かしてhello worldする on docker


# 前提

 - Macを利用している
 - docker-composeが利用できる
 - 補足ですが、djangoはおろかpythonもほぼ初めて
 
# あらすじ
 - python3.8でdjangoを動かす開発環境を作りたかった
 - ubuntu上で構築、とか、dockerでcentosで構築、は見つかったが ubuntuでdockerで、というのが見つからなかったので、作った
 - ついでにdocker-compose.ymlも用意して、簡単なコマンドで構築できるようにした
 - hello world的なものまで実施した


以下の２記事を足してプラス調味料加えた的な内容です。ありがとうございます。

[Python3.7 + Pipenv環境をUbuntu18.04 LTSに構築](https://qiita.com/sabaku20XX/items/67eb69f006adbbf9c525)
[Djangoの開発環境をDockerで作ってみた
](https://qiita.com/homines22/items/2730d26e932554b6fb58)

# やったこと

以下にソースはあります。
https://github.com/papettoTV/start_django_on_docker

そのまんまですが、ダウンロードして

```sh
docker-compose up --build
```

すれば `http://0.0.0.0:8000/polls/` にアクセスすれば動きます。あら簡単。

# 解説

## 方針

* pythonも日々versionアップするので、必要な環境は仮想環境に入れ込む
* 本番環境と開発環境を分ける、など、開発プロセスに関わる話は一旦考えない
  * ソースを全て含めたイメージにしている
* `hello world` ができる状態にまで持っていって開発サイクル回せることをゴールとする

## フォルダ(mysite,polls)について


コンテナ上でプロジェクト/アプリを作成したもの。以下はコマンド例で、名前の`mysite`や`polls`はdjangoサイトの[イントロダクション](https://docs.djangoproject.com/ja/3.0/intro/tutorial01/)そのまま。

```
# mysite プロジェクト作成
docker exec ${CONTAINER ID} django-admin startproject mysite .

# polls アプリ作成
docker exec ${CONTAINER ID} python3.8 manage.py startapp polls
```

### ALLOWED_HOSTS

最初、`0.0.0.0` でアクセスすると `ALLOWED_HOSTS`に無い！と怒られたので、以下に追加しました。

```mysite/settings.py
ALLOWED_HOSTS = ['0.0.0.0']
```

## hello world

これもdjangoサイトの[初めてview作成](https://docs.djangoproject.com/ja/3.0/intro/tutorial01/#write-your-first-view)の通り進めただけで以下のように動作した。

![スクリーンショット 2020-03-03 14.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1533/a7eaa023-e4ee-bc2a-104d-1d918f14792a.png "スクリーンショット 2020-03-03 14.56.09.png")


これができれば、後はひたすら、ソースコード修正→ブラウザで確認、というサイクル回せる！

# 課題

気になるのは、コードでコンパイルエラーがあったときに`docker-compose up`で動くコンソール上にエラーがすぐに出ず（以下のように正常にリロードされて終わる）、別のコード修正をしたときにそのエラーが表示されること。

```
app_1  | Run 'python manage.py migrate' to apply them.
app_1  | March 03, 2020 - 05:43:00
app_1  | Django version 3.0.3, using settings 'mysite.settings'
app_1  | Starting development server at http://0.0.0.0:8000/
app_1  | Quit the server with CONTROL-C.
app_1  | Watching for file changes with StatReloader
```

ワンテンポ遅れて出る感じ。なにかオプションがあるのか、設定が悪いのか。。。

# おわり

以上です。細かいパラメータの修正など、気付き次第updateしていこうと思っております。

追記）2020/03/06 pythonのversionを3.7 → 3.8 にupdate
(勉強ログ)暗号技術入門はじめの一歩

# 執筆の目的
情報セキュリティとか暗号技術特論とかの授業で暗号化の方式とか特徴とか勉強したけど、結局この方式どんなんだっけとか最終的にどれが主流なんだとか、単位はとったけどそこらへんうやむやなのでまとめとこうという感じ。

なので大学の授業で取り扱う暗号化技術について、大学の授業の流れに沿って要点重視で復習していきます。
執筆の際には以下の本をもとにしました。
<a str="https://www.hyuki.com/cr/">暗号技術入門 第3版 秘密の国のアリス</a> (著: 結城 浩)

(いずれ実装してみた系記事も書けたらな)

あと、とりあえずここでは暗号アルゴリズムに限定して書く。署名とかは別記事で。

# 良い暗号とは
どの暗号分野の本でもきっと冒頭に書かれるであろう要点

- 良い暗号とは、より機密に作られ誰もが解けないもの、ではない。

- そのアルゴリズムが世に公開されてもなお強い解読困難性をもち、さらにあらゆる実装環境において使用することができるもの（個人的解釈）

本に書かれてた暗号とセキュリティの常識

- 機密な暗号アルゴリズムは使わないべし
    - 「独自に考案した完璧な暗号アルゴリズム」は必ず解読されるから、大人しく一般的なのを使おう。隠すことによるセキュリティ(Security by obscurity)は危険かつ愚かな行為。
- どんな強力な暗号でも必ず暴かれる
    - 今までに絶対に暴かれなかった暗号は存在しない
    - そして暴かれたときには大きな損失を伴う
    - どんな暗号もいつかは必ず解かれるが、それに要する時間と、それによって入手されてしまう情報の価値のトレードオフを考えることが大事
- 強い暗号アルゴリズムを生み出すことは非常に困難
    - 非常に困難なのでみんなで協力して作り上げよう。
    - 暗号の強さとは、世の中に存在するあらゆる暗号スペシャリストたちが解読に挑み、それでも解読できなかった、という実績である。
- 弱い暗号は暗号化したほうが危険
    - 利用者の「暗号してるから安全」という意識が生まれる一方、解読者からしたらいい鴨になってしまう。暗号を過信してはいけない。
- 暗号はセキュリティのほんの一部
    - 暗号化したからといってその秘密が完全に守られてるわけではなく、例えばその前後に盗聴されてたり、サポートセンターを装い利用者に一時的に暗号化を停止させたりなど、情報を盗み取る手段は無数にある。
    - 結局これらの暗号を利用する「人間」次第

# 基本的な暗号化の構成
<img width="551" alt="Screen Shot 2019-07-03 at 17.44.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344282/c3897b61-cc7c-f20e-b0fb-b64ff80297d7.png">

- 暗号アルゴリズムは公開されているもの
- 鍵は秘匿情報

# 暗号技術の大まかなジャンル分け(WIP)
やっぱり全体像は把握しておいたほうがいい。ここで取り扱ったものを生やしていくような形にする。

- 対称暗号（共通鍵暗号）
    - ストリーム暗号
        - シーザー暗号
        - 単一換字暗号
    - ブロック暗号
- 非対称暗号（公開鍵暗号）


# 攻撃法について
暗号技術を考える際、それに対して攻撃法についても知っておく必要があるのでここで簡単にまとめとく

-  ブルートフォースアタック（全文探索）
   候補となりうる全ての鍵で解読を試みる方法
-  頻度分析
    その言語で使用される文字および単語の出現頻度から、変換表を推測していく方法

# 対称暗号（共通鍵暗号）
暗号化鍵と復号鍵が同一。そのため鍵配送問題が生じる。
- ストリーム暗号
    データの流れ（ストリーム）を順次処理していく
- ブロック暗号
    64bit ごと、とか一定長の文字列ごとに暗号化する

## シーザー暗号
### アルゴリズム
ここ最近はやっている謎解きにもよくみられる、それぞれの文字を50音表で何文字ずらしてってやつ。例えばアルファベットでやると

answer

という平文に対して、それぞれの文字を2つずらすことで暗号化するとしたら

abcdefghijklmnopqrstuvwxyz

↓

CDEFGHIJKLMNOPQRSTUVWXYZAB

という対応関係になるから

CPUYGT

という暗号文が完成する。CPUYGT って文字列を見ても一見なんのことかわからない。この時の暗号アルゴリズムと鍵は

暗号アルゴリズム　→　文字を規則に従ってずらす

鍵　→　ずらす文字数（上記の例では2）

### 攻撃法
鍵の種類がたかが知れてる（ex.アルファベットの場合26種類)→ブルートフォースアタックが有効

## 単一換字暗号
### アルゴリズム
文字変換表に従って平分の文字を置き換えていく。（これも謎解きでよく見られる。例えばPCのキーボードを変換表に見立てて「アルファベット→ひらがな」みたいな。実際に利用するなら対応表は独自で作成すると思いますが）

この時の暗号アルゴリズムと鍵は

暗号アルゴリズム→対応表をもとに文字を変換

鍵→対応表

### 攻撃法
鍵空間（鍵の候補の集合）の大きさを計算すると、使用可能な文字の種類をn種とするとn!もの鍵の候補が存在するので、シーザー暗号に比べかなり多くなり、ブルートフォースアタックでの攻撃は困難

それぞれの文字の変換先が常に固定であるため、頻度分析が有効（平分が何かしらの文章じゃなくて頻度分布に偏りがない文字列なら大丈夫？例えば「使い捨てパッド→単一換字」とか、でも結局鍵管理の問題とか鍵配送問題とかはあるけど）

## エニグマ
単一換字暗号の鍵の部分を日毎に変えたり、特殊な機械を使って複雑化したりしたやつ。結城さんの本では詳しく説明されてるのでそちらを参考に。

要点としては、鍵の中にさらに暗号アルゴリズムを導入したもの
(初学者) Amazon Athena tutorial 体験記

(注) IT 初学者による誰のためでもない勉強ログですので有益な情報はほぼありません。

# 経緯
数ヶ月前からクラウドサービスに触れる機会があり AWS という単語を耳にするようになって、興味あるし、しかも学生のうちなら無償である程度のことはできると聞いていたので、せっかくなら活用していきたい。でも提供してるサービスが多すぎて、色々できるという印象はあるものの実際に自分がそれを運用してるとこをイメージできない。

なのでせっかく用意されてるチュートリアルにしたがって１から AWS のサービスを体験しようと思い実施した。（AWS Athena ではなく Amazon Athena なので AWS のさーびすじゃないのか？細かいところはよくわからない）

# Amazon Athena の特徴
* Amazon S3 内のデータを標準 SQL を利用して簡単に分析
* サーバーレスのためインフラの管理は不要
* 課金は実行したクエリに発生
* 分析用データを準備するための複雑な ETL ジョブが不要
* デフォルトで AWS Glue データカタログと統合されてるため様々なサービスでメタデータの統合リポジトリを作成可能
    * データソースのクロールとスキーマの検出
    * 新規および修正したテーブル定義とパーティション定義のカタログへの入力
    * スキーマのバージョニング保持
    * Glue のフルマネージド型 ETL 機能を使用することで、データ変換や列思考の形式への変更が実行でき、コストを最適かしてパフォーマンスを向上可能



# チュートリアル開始
## Create Table Wizard
> To query the ELB log files, you need to define a corresponding table in Athena.

とのこと。ELB ログファイルにクエリする（操作要求する）ためのテーブル Wizard を作成。Athena に corresponding table を用意する必要がある、らしい。

> A table is a metadata definition that represents your data. Tables are grouped in a database. We’ve created some sample tables for you to query under the default database.

テーブルとはあなたの（用意した）データを表すためのメタデータの定義。これらは一つの DB の中にて保管される。Default DB にサンプルテーブルを用意しておいたよ!とのこと。ありがとう！

sampledb の中に elb_logs が用意してありました。

## Name and Location
使用する DB と テーブル、データセットのある場所を指示通り指定。

<img width="1331" alt="Screen Shot 2019-08-01 at 12.39.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344282/902977a6-f737-560e-bdfe-35e7b87da57f.png">

## Data Format
なの通りデータのフォーマットを指定。 Apache Web Logs というのを選択。Regex の方も用意されたものをコピペ。
 
## Columns
データセットのカラムを入力。

> You need to define the column names and types that map to the corresponding fields in the ELB log files.

>Each file has multiple columns, including Timestamp, ELBName, RequestIP, and RequestPort.

>Click here to fill in corresponding column names and types in the form.

`here` をクリックして自動的に設定してもらいました。

## Partitions
オプションで Partitions （DBないの仕切りみたいな）を追加できる。今回はなし。

## Running DDL to Create Table
> The above DDL statement for the elb_logs table was generated by your entries in the wizard. When you choose Run Query, the table is created.

> To view sample rows of the elb_logs table, choose the corresponding Preview Data icon in the Tables list on the left pane.


これにてめでたくテーブルを作成できた

<img width="1331" alt="Screen Shot 2019-08-01 at 13.01.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344282/3374cdf5-3d91-fdfc-95db-1d209d34e9e6.png">

## S3 とか AWS Glue とか使う経験をしとかなきゃ
To be continue...
JavaScriptで世界のナベアツを作ってみよう！

#はじめに

最近、面白い芸人がたくさん出ていますね。

牛奶男孩（Milk Boy）とか、ぺこぱとか、かまいたちとか。

その中でも、僕の中では流行している芸人さんがいます。

###世界のナベアツです。

##世界のナベアツとは

2011年くらいになぜか流行した芸人で、

3のつく数字か3の倍数を言うと「アホ」になるという単純なネタが有名です。

それでは、世界のナベアツを理解したところで、本題に入ります。

##実際にプレイする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515312/039dae34-baf5-1936-7710-206c18e038f1.png)

[実際にナベアツをプレイする](https://nakano1120.github.io/dokaben/aho)

#ソースコード
```js:script.js
        ///アホ
        const aho_1=["","いちっ！","にっ！","さーんっ！","よんっ！","ごっ！","ろくっ！","ななっ！","はちっ！","きゅうっ！"]
        const aho_10=["","じゅう〜！","にじゅ〜！","さん！じゅ！","よんじゅ！","ごじゅ！","ろくじゅ〜っ！","ななじゅ！","はちじゅっ！","きゅうじゅ！"]
        const aho_100=["","ひゃく！","にひゃく！","さんびゃ〜く！","よんひゃく！","ごひゃく〜！","ろっぴゃっく！","ななひゃくっ！","はっぴゃ〜く！","ん〜きゅうひゃく！"]
        ///通常
        const normal_1=["","いち","に","さん","よん","ご","ろく","なな","はち","きゅう"]
        const normal_10=["","じゅう","にじゅう","さんじゅう","よんじゅう","ごじゅう","ろくじゅう","ななじゅう","はちじゅう","きゅうじゅう"]
        const normal_100=["","ひゃく","にひゃく","さんびゃく","よんひゃく","ごひゃく","ろっぴゃく","ななひゃく","はっぴゃく","きゅうひゃく"]
        function ahonumber(){
            let normalnum=document.getElementById("nabeatsu").value///ボックスから持ってくる
            if(normalnum>999 || normalnum<1 ){
                alert("値が不正！オモロー！")
                return
            }
            ///0をつけてエラー回避
            let num="00"+normalnum
            num=num.split("")
            let result=""
            ///スーパーアホモードか３がつくか３の倍数でアホ。それ以外は通常。
            if(document.getElementById("everyaho").checked || normalnum % 3 == 0 || num.indexOf('3') != -1){
                result=aho_100[parseInt(num[num.length-3])]+aho_10[parseInt(num[num.length-2])]+aho_1[parseInt(num[num.length-1])]
                document.getElementById("result").style.color="#ff0000"///アホは赤い
            }else{
                result=normal_100[parseInt(num[num.length-3])]+normal_10[parseInt(num[num.length-2])]+normal_1[parseInt(num[num.length-1])]
                document.getElementById("result").style.color="#000000"///通常は黒い
            }
            document.getElementById("result").innerHTML = result+"("+normalnum+")"///描画する
        }
```
拙いコードですが許してください。

##がんばった点

 - アホになる時どういうことを言うか
 - アホになる時は文字を赤くしてわかりやすくした
 - ずっとアホになる「スーパーアホモード」をつくった
 - 本家を忠実にエラー時に「オモロー！」も言ってくれる

#最後に

###くれぐれも千円札に「世界のナベアツ」とは書かないようにしましょう！

みんなも楽しくナベアツしようね！
TeamCityビルドエージェントコンテナにJDKを追加する

## 目的

自分の環境にあったビルドエージェント環境のコンテナを用意できる。

## ビルドエージェント構成方針

ビルドエージェントの構成は２つの方針がある。

#### １：リッチなビルドエージェント

ビルドエージェントは無料枠では２つまでなので、小さなプロジェクトユースでは（費用を払わない限り）１つのエージェントに複数のビルド環境をもたせたいだろう。
おそらく、個人ユースでもおおよそ同じと思う。

#### ２：最小限のビルドエージェント

ビルドエージェントを最小限にすることで、ビルド環境のセットアップは簡単に済ませることができるし、Dockerfileも小さくて済む。
反面、エージェントの管理を手動で行わなければならない。

## ベース検討

特に要件がない場合には、[通常のエージェントコンテナ](https://github.com/JetBrains/teamcity-docker-agent)を出発点として作業するのがいいだろう。.NETビルドやDockerデプロイ用の環境セットアップもなされているようだ。
一方、ハードなプロジェクトユースでコンテナを少しでも小さくしたかったり、完全に管理するため余計なものを入れたくない場合には[最小のエージェントコンテナ](https://github.com/JetBrains/teamcity-docker-minimal-agent)を利用するかもしれない。

どちらにせよ、エージェント自体の実行環境としてJava8は入っている。（OpenJDK派生のAmazon Corettoを利用しているようだ）

## 本題：JDKの追加

今回は[最小のエージェントコンテナ](https://github.com/JetBrains/teamcity-docker-minimal-agent)を出発点としてJDKを追加することにした。
使用するコンテナは`linux`(Ubuntu)タグの`latest`である。

#### Javaの追加先

[エージェントのJava環境自動検知](https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#PredefinedBuildParameters-DetectingJavaonAgent)を参照すると以下のディレクトリ群で自動検知されるとのこと。
あとは設定ファイルに探索先パスを設定できるようだ。

> Unix
> 
> The following directories are searched for Java subdirectories:
> 
> /usr/local/java
> /usr/local
> /usr/java
> /usr/lib/jvm
> /usr
> 

#### サンプル：[Amazon Correto 11](https://docs.aws.amazon.com/ja_jp/corretto/latest/corretto-11-ug/downloads-list.html)

jdk11を追加する場合のDockerfileサンプル。
[最小のエージェントコンテナ](https://github.com/JetBrains/teamcity-docker-minimal-agent)のJDK追加をパクってドン。


```dockerfile:Dockerfile
FROM jetbrains/teamcity-minimal-agent:latest

LABEL maintainer=""

# JDK preparation start

# Amazon Coretto 11
ARG MD5SUM='08a0cea184824c5477a62ce6a6a0fb0b'
ARG JDK_URL='https://d3pxv6yz143wms.cloudfront.net/11.0.3.7.1/amazon-corretto-11.0.3.7.1-linux-x64.tar.gz'

RUN set -eux; \
    curl -LfsSo /tmp/openjdk.tar.gz ${JDK_URL}; \
    echo "${MD5SUM} */tmp/openjdk.tar.gz" | md5sum -c -; \
    mkdir -p /usr/local/java/openjdk; \
    cd /usr/local/java/openjdk; \
    tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
    rm -rf /tmp/openjdk.tar.gz;

# JDK preparation end
##################################
```

AGENT_NAMEを使ってエージェントに名前をつけておくとサーバーで管理しやすい。

```yml:docker-compose.yml
version: '3'

services:
  ci-agent-java11:
    build: ./ci/ci-agent/
    environment: 
      - SERVER_URL=ci:8111
      - AGENT_NAME=Java11
    ports:
      - 9090:9090
  　volumes:
      - ./ci/agent-java11/conf:/data/teamcity_agent/conf
```

#### ビルドステップにJDK11を指定する

ビルドステップはデフォルトだとビルドエージェントの`JAVA_HOME`のJDKを使用するので設定したJDKを指定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166036/7a45c4b5-a35e-8fc9-ee4c-2dde19330761.png)


#### 参考：エージェントをプールに割り当てる

`default`プールのみでは管理しにくい場合はエージェントを適切にプールで分けてプロジェクトごとに割り当てましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166036/0b64b860-c759-7180-20df-992bdc82416b.png)

いじょ。
Twitter Developerの開発者申請（例文あり）とAPIキー取得方法まとめ

【質問あればコメント欄でお願いします】
[Twitter Developerの開発者申請（例文あり）とAPIキー取得方法まとめ【2019年版】](https://digitalnavi.net/internet/3072/)
を参考に開発者申請とAPIキー取得
あわせて、
[サーバーレス開発プラットフォーム Firebase入門 （単行本）](https://www.shuwasystem.co.jp/book/9784798057750.html)
参考にしました(P59～)

※[アプリ登録のページ](https://developer.twitter.com/en/portal/projects-and-apps)
iOS 13.3.1 dyld: Library not loaded の私的回避法

iosでchartsを使いたくなったので、cocoapodsでインストールするも、実機テストで
`code signature invalid for "path/to/charts.framework"`
とかのエラーが出て動作しなかったので、
https://github.com/Alamofire/Alamofire/issues/3051
あたりを参考にして、
Podfileの
`use_frameworks!`
を
`#use_frameworks!                                                            
use_modular_headers!`
に書き換えると、今のところうまく動作するようです。
フリーの開発アカウント使ってると起きるようですね。

>"Apple blocked utilizing external frameworks on free accounts with 13.3.1 upgrade. Try downgrading to 13.3."

>https://stackoverflow.com/a/60211703/8574785
Dockerに構築したRails + MySQLに対し、DBeaverで接続する

ポートフォワードを理解しないでハマったので記述。
タイトルの通り、「DockerでRailsとMySQLで環境を作り、DBeaverでMySQLに接続する」ということをしたかったので、それまでの道のり。

# 環境
- Docker
- Rails 5.2.4.1
- MySQL 5.7
- DBeaver

## Dockerに接続設定をする
ホストからDockerに接続するので、「この番号のポートに接続したら、こっちのポートに転送してください」という設定をします（ポートフォワード）。

```yml:docker-compose.yml
version: '3'
services:
  web:
    （略）
  db:
    image: mysql:5.7
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD:****
    ports:
      - 3006:3306
```

肝は`ports`です。

`- (ホストから接続するポート):（転送先のポート）`

上記の設定だと「3006のポートに接続したら、dockerの3306ポートに転送する」という設定です。
MySQLはデフォルトで3306ポートを使用していますので、環境構築時に特に指定していなければこのままで。

## DBeaverの設定

接続設定を開き、MySQLを選択。
<img width="929" alt="スクリーンショット 2020-03-03 14.33.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527789/653c2924-f10e-cfea-b0c8-470c07cc7710.png">

Server Hostに「127.0.0.1」を設定。
Portは先程Docker-compose.ymlに設定した左側のポート番号。
あとはユーザー名とパスワードを設定し、テスト接続ボタンを押して繋がればOK。
<img width="939" alt="スクリーンショット 2020-03-03 14.35.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527789/18a694d2-4f63-f1f9-db8f-fb3e2748ffef.png">
Pythonのイテレータのkeyとvalueどっちも使いたい

for 文の中で dict をイテレータとして使う時 key と value 両方使いたい時ってありますよねー。あと list をいてラータにする場合の index も。

毎回結局どう書けばいいんだっけってなるのでメモに残す。

ちなみに今まではこうしてた↓

```python:python3
>>> dict = {'a': 10, 'b': 20, 'c': 30}
>>> for key in dict.keys():
>>>    print('key: ', key, ' ,value: ', dict[key])
key:  a  ,value:  10
key:  b  ,value:  20
key:  c  ,value:  30
```

for の後ろの変数を二つにして dict.items() を渡せばそれぞれに key, value が入るらしい

```python:python3
>>> dict = {'a': 10, 'b': 20, 'c': 30}
>>> for key, value in dict.items():
>>>    print('key: ', key, ' ,value: ', value)
key:  a  ,value:  10
key:  b  ,value:  20
key:  c  ,value:  30
```

list で index と value を使いたい場合は enumerate(list) を使用する

```python:python3
>>> l = ['a', 'b', 'c']
>>> for index, value in enumerate(l):
>>>    print('index: ', index, ' ,value: ', value)
index:  0  ,value:  a
index:  1  ,value:  b
index:  2  ,value:  c
```

pandas の Series や DataFrame でも同じようにできますが、index にセットした値は適用されず index=0,1,2,... となります

ちなみに js の Map では同じようなことを分割代入的に書きますよね
メモ: リスト内包表記

# リスト内包表記
めちゃくちゃ使うのでメモ。例えば n^2 の数列を作りたい時

```python:python3
>>> l = [i**2 for i in range(6)]
>>> l 
[0, 1, 4, 9, 16, 25]
```

このように `f(x) for x in <イテレータ>]` とすれば `f(x)` のリストを返してくれます。

## if を併用
こう書けば条件を満たす要素のみに絞れます

```python:python3
>>> l = [i**2 for i in range(6) if i%2 == 0] // i が 偶数の場合のみの i**2 を返す
>>> l 
[0, 4, 16]
```

## if else を併用
条件を満たすかどうかで値を変化させたい時は if else を使ってこんな感じにします

```python:python3
>>> l = [i**2 if i%2 == 0 else 'odd' for i in range(6)]
>>> l 
[0, 'odd', 4, 'odd', 16, 'odd']
```

## ネスト
ネストすることも可能

```python:python3
>>> l = [(i, j) for i in range(3) for j in ['a','b','c']]
>>> l
[(0, 'a'),
 (0, 'b'),
 (0, 'c'),
 (1, 'a'),
 (1, 'b'),
 (1, 'c'),
 (2, 'a'),
 (2, 'b'),
 (2, 'c')]
```
UIImageViewのdraw(_ rect: Rect)は呼ばれない

`UIImageView` を丸くしたくて

```swift
myImageView.layer.cornerRadius = myImageView.frame.width * 0.5
myImageView.layer.masksToBounds = true
```

をいろんなところに書いている。

CircleImageViewみたいなUIImageViewのサブクラスを作って`draw`メソッドをオーバーライドすれば共通化できるんじゃないかと考えて以下のようなクラスを作ってみた。

```swift
import UIKit

@IBDesignable class CircleImageView: UIImageView {

    override func draw(_ rect: CGRect) {
        layer.cornerRadius = rect.size.width * 0.5
        clipsToBounds = true
    }
}
```

が、丸くならない…

調べてみると[UIImageViewのdrawRectメソッドは呼ばれない](https://stackoverflow.com/questions/9405094/drawrect-not-getting-called-for-uiimageview)らしいということがわかった。まじかよ…

UIImageViewではなくUIViewを継承してdrawメソッドで独自の描画処理をしようねって書いてある。
いい感じの解決法はまだ見つけられていない。
【Tips】VSCode Live Share で.gitignore に記述したファイルが共有されない

開発のリアルタイム同期プラグインであるVisual Studio Code Live Share において、一部ファイルが共有されないことを確認。

ファイル同期されないのは`[セッションホスト] -> [セッション参加者]`の向き。

# 原因

デフォルトだと`.gitignore`に記述したファイルを共有対象から除外される（セキュリティを考慮した作り）。

# 対処

これを無効化するには**セッションホスト**側で以下のファイルをプロジェクトフォルダ直下に配置する

- `.vsls.json`

```json
{
    "$schema": "http://json.schemastore.org/vsls",
    "gitignore":"none"
}
```

なお`.vsls.json`自体はセッション参加側に共有されない様子。
その他にも特定のファイルやフォルダのみを除外したい、といったやり方も可能。

```json
{
    "$schema": "http://json.schemastore.org/vsls",
    "gitignore":"none",
    "excludeFiles":[
        "*.p12",
        "*.cer",
        "token",
        ".gitignore"
    ],
    "hideFiles": [
        "bin",
        "obj"
    ]
}
```

参考: [Security features of Live Share](https://docs.microsoft.com/en-us/visualstudio/liveshare/reference/security#controlling-file-access-and-visibility)
メモ: Python

# Python

可読性を重要視したプログラミング言語のひとつ。特徴は以下のようなものがあげられる。

- ドキュメントが充実
- インタプリタ型
- オブジェクト指向をサポート
- 動的データ型
- インデントによるブロック定義
- 充実した機械学習モジュール
- アプリケーションに組み込んでスクリプトインタフェースとして利用することが可能
- Windows、Linux/Unix、OS/2、Mac、Amiga など様々な OS で実行可能
- オープンソースライセンス


公式ドキュメント: https://docs.python.org/ja/3/

# 2.x 系と 3.x 系
メジャーバージョンが 2→3 になるにあたり、関数の定義変更や廃止、型のルール変更などの割と多くの変更点があります。また、拡張 Iterable Unpacking という技も使えるようになります。

例えば 2.x 系では `1 > 'a'` が True になるところ、3.x 系では TypeError を投げてくれるようになってます。

相違点をまとめた記事をみると全体的に改良的な変更が多い（当たり前）ので、3.x 系を使った方が良さそうですが、当然 Python 2.x でしか動かないモジュールとかあるはずなので調整しなければいけません。

2 → 3 へのコード変換も十分に整備されてそうです。

https://docs.python.org/ja/3/library/2to3.html

ただ v2 に慣れている人にとっては割と抵抗があるのか、時と場合によって v2 と v3 を使い分けるような人も見かけます
RapberryPi4でWebカメラLogitech, Inc. Webcamを使う

# RapberryPi4でWebカメラLogitech, Inc. Webcam C270を使う
RapberryPi4とIntel Myriad VPUでUSB接続のWebカメラ利用をするための解説になります。
特に（大きな）問題があるわけではないので、初めてWebカメラを利用する方向けの内容になります。
注意）ほぼ同様ですので、例はWebcam　C720で行います。

## Logitech, Inc. Webcam C270
Logitech製のWebカメラ、2,000円強で買えます。
![IMG_4310.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273559/8437d7d7-9e06-7d5b-fe54-b9e27af5a422.jpeg)

## Webカメラ認識前　の状態を確認しておきます
Webカメラ接続後に増えたものが、認識されたもの　と特定できるので。
Webカメラが認識されると /dev/video＜ID＞ が増えます。

~~~
pi@raspberrypi:~/OpenVINO $ ls -lt /dev/video*
crw-rw----+ 1 root video 81, 2  1月  9 22:52 /dev/video10
crw-rw----+ 1 root video 81, 3  1月  9 22:52 /dev/video11
crw-rw----+ 1 root video 81, 4  1月  9 22:52 /dev/video12
~~~

## このWebカメラは挿せばRapberryPiが認識してくれます
WebカメラをUSBポートに挿して、lsusbコマンドで認識されたか確認してください。
以下のRapberryPiはIntel Myriad VPUもUSBポートに接続されています。

~~~
pi@raspberrypi:~/OpenVINO $ lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 1997:2433  
Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 003: ID 03e7:2150 Intel Myriad VPU [Movidius Neural Compute Stick]
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
~~~

## Webカメラ認識後　の状態を確認しておきます
Webカメラが認識されると /dev/video＜ID＞ が増えます。
/dev/video0と/dev/video1が増えました。

~~~
pi@raspberrypi:~/OpenVINO $ ls -lt /dev/video*
crw-rw----+ 1 root video 81, 0  1月 10 14:07 /dev/video0
crw-rw----+ 1 root video 81, 1  1月 10 14:07 /dev/video1
crw-rw----+ 1 root video 81, 2  1月  9 22:52 /dev/video10
crw-rw----+ 1 root video 81, 3  1月  9 22:52 /dev/video11
crw-rw----+ 1 root video 81, 4  1月  9 22:52 /dev/video12
~~~

## 必要なアプリケーションのインストール
fswebcam
コマンドラインからWebカメラで撮影が可能なアプリケーション

~~~
pi@raspberrypi:~/OpenVINO $ sudo apt-get install fswebcam
~~~

## テスト撮影
撮影したファイルを保存するディレクトリへ移動します
以下のコマンドを入力します

~~~
fswebcam test_pic.jpg
~~~

GUIディスクトップから、ファイルマネージャーでファイルをダブルクリックすると簡単に確認できます。
上手く撮影できていることを確認してください。

オプションを増やして試してみます。
解像度はC270の最大にしていますので、他のWebカメラであれば機種の仕様に合わせてください。
オプション
　-r 解像度を設定
　--no-banner バナーを削除

~~~
fswebcam -r 1280x720  --no-banner test_pic2.jpg
~~~
URLに含まれる「？」の意味

#はじめに
URLを意識し出してから、中身に含まれている、***?○=△△***の意味について気になったので、残しておきます。
##データベースに対する要求を意味している。
このことをクエリパラメータという。
簡単な例で言うと
Googleで、***ほげ***と検索すると、URLの末尾の方で
　***?q=ほげ***　というクエリパラメータが存在する。
クエリパラメータの先頭には必ず***？***が付いてその後に***変数名***　***イコール***　***値***という形になる。複数のパラメーターをつけたい場合は***&***を使用する。
編集・削除機能へのユーザー制限

##編集・削除機能へのユーザー制限
条件分岐によって機能を制限します。
ユーザーがログインしているかどうかという実装は**<% if user_signed_in? %>**ですが、これだけではログインしているユーザー全員が投稿・編集・削除ができてしまいます。
ユーザーがログインしている」かつ「ログインしているユーザーと投稿したユーザーが一致している」という条件を満たした場合のみ機能が使用できるようにします。
【例】モデルをPostとします

```
if user_signed_in? && current_user.id == @post.user_id
```
上記の記述でユーザー制限ができます。
編集・削除を投稿したユーザー自身にだけ許可する場合、今回はshowに記載します。

```app/views/posts/show.html.erb
〜略〜
<% if user_signed_in? && current_user.id == @post.user_id %>
  <div class="XXXX">
    <%= link_to "編集", edit_post_path(@post.id), class: "YYYY" %>
    <%= link_to "削除", post_path(@post.id), method: :delete, class: "ZZZZ" %>
  </div>
<% end %>
〜以下略〜
```
上記のように記述します。
ツイッターの自動的なフォローをpythonとseleniumで！(RPA)

http://www.minekawada.com/twitter-auto-login-python.html
まずは上の記事から自動的にログインする方法を習得します

http://www.minekawada.com/twitter-auto-follow-python.html
次の記事でフォローの自動化を計りましょう
メモ: ORM

# オブジェクト関係マッピング
オブジェクト関係マッピング（ORM: Object Relational Mapping)とは、**オブジェクト**指向なブログラミング言語とリレーショナル(**関係**)データベースを結び付ける(**マッピング**)ことです。

言い換えると、DB言語ではなくサーバサイドで使用している言語を使ってリレーショナルデータベースを弄るためのツールです。(ツールの場合は OR Mapper ですね)

例えば、アプリの中で User というデータを扱う必要があった場合、アプリ側では User を表すクラス、DB 側では User テーブルを作成することになると思います。本来この DB 側のデータは SQL 文を叩いたりして編集することになると思いますが、そうではなく User クラスや user インスタンスを扱うのと同じように DB のテーブルやレコードを編集できると言ったことを実現するのが ORM の役割です。

つまり、RDB のレコード操作を、オブジェクトとして直感的に操作できる、ということです。

これの ~~せい~~ (おかげ)でエンジニア2年目だけど、SQL 文は滅多に書いたことがありません()

## 具体例
- Sequelize
    - Node.js ⇄ MySQL, PostgreSQL, SQLite など 
    - GROWI.cloud で使用
- ActiveRecord
    - Ruby on Rails で用意されてる(?)ライブラリだそうですが、使ったことがないのでわかりません
- Core Data
    - こちらも使ったことないですが iOS 系のアプリに用いられてるそうです

## やってくれてること
- 与えられたクラスの属性とテーブルカラムの対応付けをもとにマッピング処理
- 与えられたマッピング定義をもとにスキーマやクラスのソースを自動生成
- SQL を用いたデータ操作を行ってくれる API の提供

### 設計思想の異なるデータモデルを結びつける
ORMオブジェクト指向は「**現実世界の物事に即したデータモデル**」である一方で、
関係データベースは「**検索やCRUDなどの処理に最適化されたデータモデル**」となっている。

このような設計思想の違いを、インピーダンス・ミスマッチといい、ORM はこれを解消し、互換性をもたらしている。

# ODM

MongoDB にも ORM 的な存在がいますが、MongoDB はドキュメント指向 DB なので **O**bject **D**ocument **M**apper となります。

僕が知ってるのは mongoose とかですかね

# 参考
https://qiita.com/yk-nakamura/items/acd071f16cda844579b9
メモ: SAML

> SAMLとは、Security Assertion Markup Language の略称であり、OASISによって策定された異なるインターネットドメイン間でユーザー認証を行うための XML をベースにした標準規格です。

- OASIS が公開してるサイト
    
    http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html

- 日本語でいろいろまとまってるサイト

    https://techinfoofmicrosofttech.osscons.jp/index.php?SAML
    
## で、結局のところ SAML って何者？

SAML があると、シングルサインオン(SSO: single sign on)が利用できる。

> シングルサインオンとは...
> 
> 一度のログインで、複数のサービスやアプリケーションにアクセスできるようにする仕組み
> 
要は、一回 Google にログインしたら、他のアプリケーション使うときにログインしなくてもアクセスできるみたいな機能。とても楽。

そんな楽機能を実現するときに出てくるいろいろな決め事を規定しているのが SAML プロトコル。特に XML 形式というマークアップ言語をセキュリティ情報交換に利用する点が特有らしい
mh-z19bのZEROキャリブレーションの方法

二酸化炭素濃度センサー「mh-z19b」のZEROキャリブレーションの方法。いくつか方法があるのだが、一番簡単な方法。

1. 400ppmの場所（屋外やよく喚起された屋内など）で20分動作させる
2. HDピンをGNDに7秒接続する。この瞬間が400ppmとして調整される。

これで完了。他にはプログラムから指示する方法、ZEROだけでなく2000ppmあたりの値も使うより正確な方法もある。
詳しくはデータシート（[mh-z19b-co2-ver1_0.pdf](https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf)）を参照。
それって車輪の再発明じゃない？

僕が書いたこのコードはどうやら「車輪の再発明」らしい → そうだよだからはよドキュメント読め

## 車輪の再発明とは

>「すでにあるものの存在を知らない、またはそれがある意味を誤解しているために、すでにあるものを一から作ってしまうこと」という意味の慣用句

## ことの経緯
ある wiki 作成アプリの、ページカテゴライズのためのタグ機能の実装をすることになった

アプリ開発初学者の僕「ページにタグをつける機能を実装するんだから、Tag モデルは作らなきゃだよな」

ちなみにこのアプリは mongoose という MongoDB 用の ODM を使っている

「ページとタグは多対多の関係だから... なるほど page id と tag id という情報を持つモデルを作ればいいのか。これをPageTagRelation として...よし、これでページとタグの紐付けが表現できた！」

...

「指定したページに紐づいてるタグを全部返してくれるようなメソッドが欲しいな。**よく使いそうだし**、PageTagRelation の static メソッドとして追加しておこう:bulb:」

```javascript:src/server/models/page-tag-relation.js
  /**
   * find all tag related to the page
   *
   * @static
   * @param {page} page
   * @returns {Promise<ObjectId[]>}
   * @memberof PageTagRelation
   */
  static findAllTagIdForPage(page) {
    return new Promise((resolve, reject) => {
      // 指定した page id を持つ PageTagRelation モデルを全て取得
      this.find({relatedPage: page._id}, function(err, relations) {
        if (err) {
          reject(err);
        }
        // 取得した PageTagRelation モデルから tag id だけ取り出して返す
        resolve(relations.map(rel => rel.relatedTag));
      });
    });
  }
```

「よし、紐づいてる tag id 取れた！」

ノ[PR](https://github.com/weseek/growi/pull/838)

**レビュワー「select 使えばよくない？」**

select... ああ確かに、そんな字面を Mongoose の公式 Docs で見かけたぞ。本当だこれ使えば一発解決じゃん

> select という車輪を再発明しようとしてました。

これ以外にも、Tag モデルに createTag() っていう Tag モデルを新規作成するメソッドとか、タグを id 指定で取得してくれる getOneById ってメソッド作ってたけど、これも Mongoose が用意してる create とか find とか使えばいいんじゃん、早く言ってよ

## 自分で調べましょう

ライブラリにもよるけど、基本的に用意してあるモジュールやメソッド等はドキュメントにちゃんと記載してあるから、こういうメソッドが欲しいなとか思ったら一旦ドキュメントを漁ってみよう。

自力で見つからなかったら、知ってる人に、こういうメソッドはないのかと聞いてみよう。初学者からはわかりづらい表現で記載されてる場合もあるので。

それでもなさそうなら作る。なんでないんだよと愚痴をこぼしながら作りましょう。

# まとめ
あらゆるライブラリは基本的に便利に作られてて、欲しいメソッドとかは大抵用意されてる。全部が全部そうではないから自分でカスタマイズしなきゃいけない場合もあるけど、まずは本当に用意されてないのかちゃんと確かめよう。気付いてないだけだったらライブラリ開発者が泣いてます。
.env ( dotenv ) ファイルの定義をシェルの環境変数として export するコマンドの例 ( set -a; source .env; set +a; ) #Linux #Shell

# Command

```
set -a; source .env; set +a;
```

# Example

```
echo "EXAMPLE1=ABC\nEXAMPLE2=DEF" >> .env

cat .env
EXAMPLE1=ABC
EXAMPLE2=DEF

env | grep EXAMPLE

set -a; source .env; set +a;

env | grep EXAMPLE
EXAMPLE1=ABC
EXAMPLE2=DEF
```


# set -a とは

set -a で変数定義を、環境変数定義である export 扱いにできるようだ。
それを source コマンドで、シェルの「表側」で実行する。

```
NAME
    set - Set or unset values of shell options and positional parameters.

SYNOPSIS
    set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...]

DESCRIPTION
    Set or unset values of shell options and positional parameters.
    
    Change the value of shell attributes and positional parameters, or
    display the names and values of shell variables.
    
    Options:
      -a  Mark variables which are modified or created for export.
```

http://linuxcommand.org/lc3_man_pages/seth.html

[Linux — source コマンドは何をしているのか > 実は環境をリロードするためのものではない - Qiita](https://qiita.com/YumaInaura/items/00437e6ab14d96adb71f)

# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3008








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->