OmniAICreator/Qiita-1.07M huggingface dataset:
新規事業の立ち上げ・グロースに必要な力と、QA業務を通して養うことができる力

# 1.この記事について
## 1-1.対象
- 今後の職種を選択する機会に直面している方（これから社会人となる就活生・新卒内定者/転職や部署移動を考えている方）
- 将来的に自分でITサービスを立ち上げたいが、具体的な構想が持てていない方

## 1-2.読了時間
5分

## 1-3.まとめ
以下の力が新規事業の立ち上げ・グロースには必要であり、QA業務を通して養うことができる

- 課題発見&解決力
- プロジェクトマネジメント力
- サービスのシステム構造の理解
- サービス開発体制の理解
- アライアンス力

# 2.はじめに
皆さま、はじめまして。
この記事では、社会人1年目（現在QA歴5ヶ月弱）の私自身が、日々業務を行いながら「将来こういったことをやりたいが、現在の業務はそれとどう繋がっているのか」を内省した結果をまとめます。

以下が私の経歴です。
①新規のファンコミュニティサービス開発に、QAテスターとして参画
②Vtuberのリアルイベント代行サービスの責任者として、事業のグロースに従事
③長期運用ゲームのQA体制管理

①②を通して、将来的には「エンタメ領域のプラットフォームサービスを自分自身の手で立ち上げ、事業化したい」と考えるようになりました。
そのために必要な力が、現在従事しているQA業務（③）を通して養われていくのではないかと思い、日々業務に取り組んでおります。

# 3.新規事業を成功させるために必要な力
ここでは、新規サービスの開発チームにいた経験や、事業のグロースを担ってみた経験から、私自身がサービスを作る上で必要だと感じた力について書きます。
※もちろんこれさえ出来ていれば事業を成功させられるという主張ではありませんので「新卒1年目だとそんなふうに捉えているのか」程度に読んでください。

## 3-1.サービスの立ち上げに必要な力
### 3-1-1.企画段階
サービスの企画段階では、ユーザーの求めている価値（抱えている課題）を特定し、その解決策としてサービスの提供価値を設計する必要があります。
そのため『課題発見&解決力』が求められます。
私がテスターとしてリリースに携わらせていただいた新規ファンコミュニティサービスの責任者の方も、成熟したモバイルゲーム市場でのマーケティング課題の解決を出発点に、事業着想を得たと仰っていました。

### 3-1-2.開発段階
サービスの開発段階では、与えられた予算・期限内にサービスをリリースできるように、開発チームのハンドリングを行い、開発の推進を行う必要があります。
そのために、リリースまでに必要なタスクを洗い出し、開発メンバーに役割と納期を割り振り、合意形成を図りながら、進捗を管理するといった汎用的な『プロジェクトマネジメント力』が求められます。
また、ITサービス開発において、上記のプロジェクトマネジメントを解像度高く行うためには、他の類似サービスがどんな作りになっていて、どういったプロセスで作られるのかを理解する必要があります。
すなわち『サービスのシステム構造の理解』と『サービス開発体制の理解』をしておくことも重要になってくると思います。

## 3-2.サービスのグロースに必要な力
サービスのリリース後、収益を上げていくためには、いいモノを作るだけではなく、その価値を伝え、サービス使っていただく必要があります。
そのためには、他社やクライアントとのパートナーシップを結び、ビジネスチャンスを広げていく『アライアンス力』が不可欠になってきます。

# 4.QA業務を通して養われる力
ここでは 3. で挙げた力が、現在行なっているQA業務のどういった部分で養われるのかについて書きます。
※「実際に身につきましたよ」といった主張ではなく「業務を行なっていく中で身についている（いく）のではないかと感じています」といったスタンスですので、こちらもあくまで1つの参考として読んでください。

## 4-1.身についてきていると実感している力
### 4-1-1.課題発見&解決力
課題発見&解決力は障害削減と費用削減を通して養われていっていると感じています。
障害削減では、発生した障害（市場環境での不具合）1件1件に対して、開発のどの工程に問題があったのか・QAで検知できなかった要因は何か特定し、対策を講じます。
また、費用削減に関しても、現状検証工数が膨らんでいる理由や、検証の削減余地のある部分を特定し、TC項目削減やデータ検証への置換などでの対応を図ります。
障害削減と費用削減共に、現状分析→要因特定→解決策考案を繰り返し行うので、思考回数を重ねる機会を与えられていると感じます。

### 4-1-2.プロジェクトマネジメント力
QAの業務は、様々な方（開発・業務委託・テスター・他のQA社員）と連携をとって、障害削減やQA効率化などの改善プロジェクトを進める機会が多いです。
その中で感じるのは、以下3つの重要性です。

- 握る：関係者と早い段階で合意形成をした上で、動き出す
- 振る：「何を」「誰に」「いつまでに」やってもらうのかを割り振る
- 決める：「こういった方向で進めるべき」という解を自分の中に持つ

すなわち、ステークホルダーを巻き込みながら、自分で判断し、物事を前に進めるプロジェクトマネジメント力が求められると感じています。

### 4-1-3.アライアンス力
QAの業務は、自分のチーム外の人（開発・業務委託）との交渉・折衝・調整を行う機会が多いです。
業務委託先からのご提案を受ける機会や、プロダクトのプロデューサーへQA予算承認をいただく際などがそうですし、障害削減を行う際にも関係性構築力がかなり重要になってくると感じます。
例えば、障害の原因の多くが開発側の仕様書にある場合があります。
その際には、ただ現状を報告して是正を求めるだけではなかなか改善されないことが多く、「どう伝えたら動いてもらえるか」といった伝え方の部分や、開発側の体制・状況のことまで考えて歩み寄っていく必要があります。

## 4-2.今後身につきそうだと期待している力
### 4-2-1.サービスのシステム構造の理解
サービスのシステム構造の理解するためには、必ずしも開発エンジニアを経験しなければいけないといった訳ではなく、テストケースの作成・データ検証ツールの作成・ホワイトボックステストの実行を行なっていくことでも十分身に付いていくのではないかと思っています。

### 4-2-1.開発体制の理解
開発体制の理解に関しては、QA体制だけを見ていては難しいと思います。
しかし、検証部分だけではなく、開発工程全体の効率化を図るなど、役割範囲を広げていくことで、開発者よりも開発全体を考える機会を儲けることができるのではないかと感じています。

# 5.今後の展望
QA業務に携わってまだ半年も経っていませんが、QAには「テスト管理力（体制構築・費用管理力）」と「テスト技術力（設計・実行力）」が必要だと感じています。
現在は前者のみを担っているため、今後は後者にも着手していきたい（サービスのシステム構造の理解・開発体制の理解も進めていきたい）と思っております。
この記事が、これから社会人となる方がキャリアの選択肢としてQAを考えるキッカケや、現在QAに携わっている方の今後のキャリアの参考になれば幸いです。
Numpy (n,)と(n,1)表記の違いについて【横ベクトルか縦ベクトルかの違い】

## はじめに

Numpyのndarray配列のshapeを見ると、(n,1)や(n,)といった表記を目にします。「1次元配列なら【,】も【1】も不要なのでは？」と思って調べてみました。
##結論
### (n,)は横ベクトルの配列(ただの1次元配列)  
###(n,1)は縦ベクトルの配列
![コメント 2019-12-22 203229.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526723/a35b6050-da30-c610-fec4-2037edc8a8b4.png)

※1次元の配列は.Tでは転置に変換することができないので、reshapeを用いる必要があります。
[cakephp] Queryオブジェクトから実行したSQL生成

フレームワークのクエリビルダでQueryを作って、データを取り出して実行！としたときに、期待した結果が得られないのは日常茶飯事です。そんなとき原因調査の一つとして、実際に実行されたSQLの確認することがあります。

今回はそんなときに使える「実行したQueryのオブジェクトからSQLを生成」する方法です。

# SQLを取り出す（ただしバインド前）
cakephpのAPIリファレンスを眺めていると、[sql()](https://api.cakephp.org/3.8/class-Cake.ORM.Query.html#_sql)という、まんまなメソッドが見つかります。

さあこれで一件落着、とはいきません。なぜなら、このメソッドで出力されるのは各種値がバインドされる前のSQLとなります。例えば以下のような感じです。

```MySQL
SELECT * FROM tab WHERE id=:c0
```
WHEREなどに指定した値が「:c0」のようなプレースホルダになっていて、実際に実行したSQLそのものではありません。

# バインドを取り出す
ならば、ということでバインドのデータを取り出します。[getValueBinder()](https://api.cakephp.org/3.8/class-Cake.ORM.Query.html#_getValueBinder)というメソッドになります。さらに続けて、**bindings()**というメソッドを実行すればバインドの情報を取得できます。バインドの情報は以下のような配列でした。

```PHP
$bind = $query->getValueBinder()->bindings();

/* bindの中身
[
  ":c0" => [
    "value" => "11",
    "type" => null,
    "placeholder" => "c0"
  ]
]
*/
```

キーがプレースホルダ、valueが置き換える値になっているようです。

# バインドを適応する

やり方はいろいろあると思いますが、例えば以下です。

```PHP
function toSql($query)
{
    $sql = $query->sql();
    $bind = $query->getValueBinder()->bindings();

    // プレースホルダを値で文字列置換。値はクォートで囲むこと。
    foreach($bind as $param => $value) {
        $sql = mb_ereg_replace($param, "'{$value["value"]}'", $sql);
    }
    return $sql;
}
```

こんな関数を用意しておけばQueryのオブジェクトを渡すだけでSQLを取得できます。

# おわりに

デバッグコンソールからアクセスできるようなところにこんな関数を定義しておくと、Query実行後あたりでブレークしてその場で呼び出し、実行したSQLを確認できるので便利です。

余談ですが、laravelでも同じ流れでSQLを構築できます。メソッドがそれぞれ[toSql()](https://laravel.com/api/6.x/Illuminate/Database/Query/Builder.html#method_toSql)、[getBindings()](https://laravel.com/api/6.x/Illuminate/Database/Query/Builder.html#method_getBindings)になります。
Google SpreadSheetから設定値を読み取るgssetting

[aratana Advent Calendar 2019](https://qiita.com/advent-calendar/2019/aratana)の20日目です。
![aratana](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150455/e79ba51f-0163-246b-ad7b-998f8f442d72.png)

最近、設定ファイルにサイト情報を書いて、チェックしにいくというスクリプトを動作させているのですが、チェック対象サイトが増えるたびに、設定ファイルを自分で書き換えるという作業が発生しているので、改善したいなと思ってきました。

現状だいたい下記のような流れです。

```
マネージャーががんばる -> サイトオープン -> サイトオープンしたよ連絡
　-> シートにサイト情報を記入
　-> 私に連絡が来る -> 私がサーバに入って、設定ファイルに反映
```

私の作業は数分ですが、めんどうだなーって思ってきました。
スプレッドシートに書いてあるサイト情報をマスタにして、スクリプトから取得すれば解決だな！と思ったので、スプレッドシートから設定値を楽に取得するパッケージを作りました。Pythonで。

# [gssetting](https://github.com/deresmos/gssetting)
安直ですが、Google SpreadSheet Settingの略で、gssettingと命名してみました。
設定情報のモデルを作成し、それを元にシートから情報を取ってくるイメージです。
[gspread](https://github.com/burnash/gspread)依存です。

2019/12/20現在、急ぎで作ったのでアルファ扱いで、完成度は低いですが、今後改善していきます。（テストファイルとかつくらなきゃ

## 利用方法

### はじめに
まず、サービスアカウントを作成して、対象のシートに設定してください。
下記サイトをご参考に。。。
[PythonでGoogleスプレッドシートを編集](https://qiita.com/akabei/items/0eac37cb852ad476c6b9)

### インストール
PyPiに登録しているので、ピップ！します。（現在は、Python3.7以上対象となってます。

```python
pip install gssetting
```

### モデル作成
下記シートから、name列とvalue列のみを抽出しようかと思います。

<img width="310" alt="スクリーンショット 2019-12-19 16.48.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243612/661a8b34-34b3-5e2a-463b-3f0ef9007afc.png">



```python
from dataclasses import dataclass

from gssetting import GSSetting


@dataclass
class Setting(GSSetting):
    username: str
    value: str

    headers = ["name", "value"]
```

### 実行
下記コードを実行することにより、読み込み完了です。
後はいい感じに加工したりしていけばおｋですね！

```python
from dataclasses import dataclass

from gssetting import GoogleSpreadSheetSetting, GSSetting


@dataclass
class Setting(GSSetting):
    username: str
    value: str

    headers = ["name", "value"]


if __name__ == "__main__":
    gs_setting = GoogleSpreadSheetSetting(
        "./service_account.json", "document_id"
    )
    settings = gs_setting.load("sheet_name", "A1:C3", Setting)
    print(settings)

    for setting in settings:
        print(setting.username.value)
```

実行結果

```
[Setting(name=<Cell R2C1 '私'>, value=<Cell R2C3 'ひらがな'>), Setting(name=<Cell R3C1 '僕'>, value=<Cell R3C3 '漢字'>)]
私
僕
```


# まとめ
楽に、シートから設定を取得できるパッケージは完成したので、色々なところで使いまわししていこうかなと思ってます。
GCPのCompute Engineで定期スナップショットを取得する(GCPコンソール編)

# はじめに

以前投稿した[記事](https://qiita.com/average_nobody/items/9f6487c879207f87c20b)にCloud SDKを利用してCLIからスナップショットを取得する方法を記載したが、その折GCPコンソールからも定期スナップショットが取れることを知ったので本稿ではその内容について記載する。

恐らくこちらの方が簡単であろうことは想像に難くない。

## 概要

毎日夜間にスナップショットを取得
7日以上前のスナップショットは削除

## 環境

クラウド:Google Cloud Platform

## スケジュールを作成

まずはスケジュールを作成してみよう。

GCPコンソール→Compute Engine→スナップショット
画面中央のパネル「スナップショット スケジュール」を選択して、「スナップショット　スケジュールを作成」を押下。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/1fa0a5b9-fdc5-bf27-731c-a979cff4b96f.png)

とりあえず以下のように設定してみた。

名前:デフォルトのもの
リージョン:asia-northeast1(東京)
スナップショットの場所:asia-northeast1 (今回はソースと同じリージョンにした)
スケジュール頻度:毎日
開始時間:GMT18:00 - 19:00 (早朝3時～4時に取得したいため)
スナップショットの自動削除までの時間:8(日後)
削除ルール:8日より前のスナップショットを削除する。
VSSを有効化:なし(Windows OSではないため、チェックしない)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/7062097d-61b9-01d0-fa1c-2d2eec125452.png)

## ディスクを関連付ける

スナップショット スケジュールを作成したら、スナップショットを取りたいディスクに関連づけよう。

GCPコンソール→Compute Engine→ディスク
スナップショット取得対象のディスクを押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/1ec1e472-25cf-8143-c75e-926e22122b37.png)

ディスクの画面で編集を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/d30de311-de4b-47dd-351a-e5e26383351f.png)

先程作成したschedule-1を選択し、保存
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/70130686-9345-590c-2aae-77bb9dd7941b.png)

## 確認

GCPコンソール→Compute Engine→スナップショット
の画面にて以下のようにバックアップが取得できてた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527863/07dd3e0a-d448-81a6-e7b0-f11a000757c9.png)

# まとめ

何も参照することなく実装できたので、CLIでやるよりはるかに簡単でした。

一応[こちら](https://cloud.google.com/compute/docs/disks/scheduled-snapshots?hl=ja)に詳細な説明があります。

スナップショット取得失敗の際にエラー通知させるには、別途Stackdriver Error Reportingの機能を利用する必要があるとのことだ。
知ると便利なスプレッドシートの機能

アドベントカレンダーのネタを考えてたとき、何気なく上司に伝えた機能が意外と知られてないことに気づいたので、スプレッドシートの便利な機能やショートカットをまとめてお伝えしようと思います

## フィルタ機能
スプレッドシートで共有されている表を、ソート変えたり絞り込みしたいときは多いと思います。
しかし、そのままソートしてしまうと同じシートを見ている別な人も影響を受けてしまい、大人数で管理してるスプレッドシートほど気軽にはソートできないはず。

そんなときに、「自分用フィルタ画面」を作ってそこでソートなどすることで、他のユーザーには迷惑をかけずにソートや絞り込みができちゃいます。

やり方は、メニュー一覧にある[データ] -> [フィルタ表示] -> [新しいフィルタ表示の作成] でフィルタを作成できます。
![スクリーンショット 2019-12-19 18.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/66a70648-6ce9-d323-b6e4-00b35df4f6a1.png)
![スクリーンショット 2019-12-19 18.41.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/6ef47b32-bfe5-eabb-7992-c7d39ff0ab45.png)

フィルタ名に自分の名前など入れれば自分用フィルタとして好きに使えます。
作成したフィルタは他のユーザーからも参照できるので、機能別に分けて「No昇順ソートのフィルタ」「名前順ソートのフィルタ」など使い分けして共有することもできます。

## 画像の貼り付け
実は、わざわざメニューから [挿入] -> [画像] と選ばなくてもコピー&ペーストで画像が貼り付けできます。
macならFinder上から、貼り付けたい画像をコピーして、貼り付けたいシートを選択してペーストするだけです。

画像のメニューから[選択したセルに画像を置く] を選ぶことで、セル内に画像を挿入する形にもできます。
![スクリーンショット 2019-12-19 18.38.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/2db893eb-83ea-43cf-a236-6a7c2edcf8d0.png)

## カーソルを最終行・列まで移動
スプレッドシート上にながーい表を作ってる場合、表の一番下まで一気に移動したい時があると思います。
そんな時はCmd + 矢印（winなら Ctrl + 矢印）で、矢印方向の記入済みの最後までカーソルが一気に移動してくれます。
ちなみに、表が穴あきだった場合は穴あき部分まで飛びます。
空欄から初めた場合は記入済み部分まで飛びます。

いちいちスクロールする必要は無いですよ！

## 指定セルの変更履歴を確認
あれ、このセルいつの間にか書き換わってる・・・でも前何が入ってたかわからない・・・
そんなとき、確認したいセルを 右クリック -> [編集履歴を表示]
![スクリーンショット 2019-12-19 18.57.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275926/1b898b06-bfe9-0998-2fcb-9a58c8284f93.png)
指定のセルのみで、いつ、だれが、どう変更したのかわかります。
全体の変更を見る必要は無い時とかに便利です。

## おわり
以上、スプレッドシートを使うとき知っておくと便利な機能でした。
スプレッドシートは関数やショートカットキーが大量に用意されているので、調べるほど便利に使えます。
時間があればちょっと調べてみると、作業時間が大幅に短縮したりします。

今回紹介した以外にも知っておくと便利な機能はいっぱいあるので、興味がありましたら以下参考リンクなどどうぞ。

[【保存版】実務の効率化が加速するスプレッドシートのショートカットキー18選！](https://tanuhack.com/shortcutkey-17/#i-5)
openssl コマンドを使用して AWSのsmtp サーバからメールを送信する

## 前提条件

-   送信元 From メールアドレスも送信先 To メールアドレスも SES で認証済みであること

## メール送信できる IAM ユーザーを作成する


```json:policy
{
"Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ses:SendRawEmail",
            "Resource": "*"
        }
    ]
}
```

## SMTP ユーザ名、パスワードを作成する

1.  SMTP ユーザー名は AWS アクセスキー ID と同じ

1.  SMTP パスワードを IAM シークレットアクセスキーから生成する

1.  以下のコードで変換する(Bash)

1.  export AWS_SECRET_ACCESS_KEY={IAM シークレットアクセスキー} 後、bash実行

    ```bash
    #!/usr/bin/env bash

    # These variables are required to calculate the SMTP password.
    VERSION='\x02'
    MESSAGE='SendRawEmail'

    # Check to see if OpenSSL is installed. If not, exit with errors.
    if ! [[ -x "$(command -v openssl)" ]]; then
    echo "Error: OpenSSL isn't installed." >&2
    exit 1
    # If OpenSSL is installed, check to see that the environment variable has a
    # length greater than 0. If not, exit with errors.
    elif [[ -z "${AWS_SECRET_ACCESS_KEY}" ]]; then
    echo "Error: Couldn't find environment variable AWS_SECRET_ACCESS_KEY." >&2
    exit 1
    fi

    # If we made it this far, all of the required elements exist.
    # Calculate the SMTP password.
    (echo -en $VERSION; echo -n $MESSAGE \
    | openssl dgst -sha256 -hmac $AWS_SECRET_ACCESS_KEY -binary) \
    | openssl enc -base64
    ```
1. 生成されたSMTP パスワードを確認する

-   [IAM 認証情報から SMTP 認証情報を生成する](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/example-create-smtp-credentials.html)

## AWS アクセスキー ID と生成した SMTP パスワードを base64 エンコードする

-   AWS アクセスキー ID を base64 エンコード

    -   `echo -n "{AWS アクセスキー ID}" | base64`

-   SMTP パスワードを base64 エンコード
    -   `echo -n "{SMTPパスワード}" | base64`

1. AWS SMTP サーバへの TLS 接続をテスト
    -   `openssl s_client -crlf -quiet -starttls smtp -connect email-smtp.us-east-1.amazonaws.com:587`

1. 接続後、メール送信をテスト

```command
AUTH LOGIN
{base64 エンコード後AWS アクセスキー ID}
{base64 エンコード後AWS シークレットアクセスキー}
```

-   235 Authentication successful. と表示後、以下を入力

```mailtext
MAIL FROM: {送信元メールアドレス}
RCPT TO: {送信先メールアドレス}
DATA
from:{送信元メールアドレス}
to:{送信先メールアドレス}
Subject: Test Subject
Test Mail
.
QUIT
```

1. メールが受信できることを確認する

# 参考リンク

-   [Amazon SES での SMTP の接続またはタイムアウトの問題をトラブルシューティングする方法を教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/smtp-connectivity-timeout-issues-ses/)
エンティティについて

# エンティティ

###エンティティとは
- エンティティとは、共通項を持ったデータの集合体
   - データの対象を抽象的に捉える。
   - 属性とインスタンスを持っている。
   - 簡単に言うとER図の箱のこと
   - サービスで扱う情報のこと



###（例）車をWeb上で販売するアプリ
　　物理的なエンティティ　社員・店舗・車、顧客など
　　概念的なエンティティ　購入履歴・進捗状況など

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510019/c2ef9563-8424-7948-83eb-cdd1ae87f2e3.png)

###エンティティの属性
エンティティの属性とはエンティティが持つ個別の情報のことをいう
車のエンティティの属性は価格、色、大きさ
顧客のエンティティの属性は　名前、年齢、性別


##memo
- エンティティはDB設計する上で大切な情報。
- DB設計するときは、粒度の大きなエンティティから作成していくといい。

## 参考
https://wa3.i-3-i.info/word11594.htmlv
BigQueryのドキュメントのNULLIFの和訳がおかしい

BigQueryのNULLIF関数の説明
> NULL が true の場合は expression = expression_to_match を返し、それ以外の場合は expression を返します。（以下略

https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators?hl=ja

チョットナニイッテルノカワカラナイ

こんな時は原文をみてみよう。（URL直だと日本語版にリダイレクトされるので右上のLanguageのプルダウンでEnglishを選ぶとよい）
> Returns NULL if expression = expression_to_match is true, otherwise returns expression. （以下略

https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators

【和訳】 *expression = expresson_to_match* がtrueの場合は*NULL*を返し、それ以外の場合はexpressionを返します。

'NULL' と 'expression = expresson_to_match'の位置が逆じゃねーか！

IFNULLの和訳も同様に'NULL'と'expr'が逆の様である。

一般的なRDBMSのSQL関数知ってれば調べないのかもしれないが、一応・・・。
CloudSearchのストップワードチューニング

# 概要
2019年現在、CloudSearchではデフォルトのストップワードに長音記号が入っていないので、検索結果で今一つしっくりこないときはStopWordに長音記号「ー」を登録しておくと良いでしょう。

# 背景
サイト内の検索で「ハニー」を検索するとレシピ名としてハニーとは関係のないタイトルが検索結果として出ていました。

- リースミートローフ・とろーりチーズフォンデュ風
- マヨなしヘルシーおいしー　しらすと大葉のマカロニサラダ
- やみつきー　蓮根と鶏ひき肉のガーリックのり塩ペッパー炒め
- とろちーずクリームのふわふわオムレット【簡単・HM使用】
- サクッじゅわー　旨味たっぷり　鶏胸肉の海苔チーフライ

CloudSearchに登録する際、タイトル名の文字列以外に、類義語である「蜂蜜」なども一緒に登録しているので、何らか「ハニー」や「蜂蜜」が前面に出てきている料理名であることを期待しているのですが、あまりにもかけ離れている、という状況がありました。

# 調査
AWSコンソールのCloudSearchではテストサーチが出来るので、検索用のサーチドメイン「my-test-domain」の検索窓に下記を打ち込みます。（OptionはStructuredを選択しています。）

```
(and title:'ハニー'  source_disp_name:'レシピ' status:1)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102740/86321169-26ea-c556-90a9-89b8ecaf2803.png)


Goボタンを押すと、下記のような感じの結果が出てきます。
※これはストップワードを調整済みのものです。
画像だと見づらいのですが、検索ヒットした文字が太字で表示されます。（この場合は「ハニー」が太字になっています。）

![good.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102740/625f1e14-3840-f064-2b8b-7bc993b9481b.png)

ストップワード調整前のタイトルを簡略化して抜き出すと下記のような感じ、でした。

| 項目 |                                                                      |
|:----|:--------------------------------------------------------------------|
|title|リースミ**ー**トローフ・とろ**ー**りチ**ー**ズフォンデュ風                           |
|title|マヨなしヘルシーおいし**ー**　しらすと大葉のマカロニサラダ                     |
|title|やみつき**ー**　蓮根と鶏ひき肉のガーリックのり塩ペッパ**ー**炒め                    |
|title|とろち**ー**ずクリ**ー**ムのふわふわオムレット【簡単・HM使用】                     |
|title|サクッじゅわ**ー**　旨味たっぷり　鶏胸肉の海苔チ**ー**フライ                         |


チューニング前の状態では、「ー」が太字になっていました。
ということは、CloudSearchの中のIndexで「ー」で検索ヒットするリストが出来上がっているため、「ー」での検索結果が表示されているのではないかと思うのです。（詳しい動作はブラックボックスでよく分かりませんが）

形態素解析で長音記号「ー」を一つの文字として分離させないような設定があれば・・・・と思ったのですがCloudSearchには分離させないような・・・設定はありません。代わりにこれを防ごうとするのが「ストップワード」の登録となります。


# 変更の実施
右メニューのAnalysis Schemesを開きます。
![スキーaム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102740/a6d44a87-fc89-ef21-0037-2a68d6cd5b77.png)

 から、CloudSearchドメインに適用中の形態素解析名をクリックすると、ストップワードのタブが開かれたモーダルがでてきます。AddStopword のところに１つづつ適用しても用意ですし、CurrentStopwordsに直接書きこんでもよいでしょう。
画面では「ー」の他に今一つ検索結果として良くなかったものも登録しています。助詞はあらかじめStopwordに登録されているような記述をどこかで見かけましたが
![StopWord.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102740/f5fb4d89-c7e9-7ed9-e866-fed6a6e7280b.png)

Updateボタンを押すとストップワードの登録は完了です。
「Run Indexing」ボタンが出てくるので、インデックスを再構築します。

# 参考
Amazon CloudSearch のテキスト分析スキームの設定
https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/configuring-analysis-schemes.html

デフォルトのストップワードディクショナリ
https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/text-processing.html#japanese
2019年12月現在、ストップワードは下記の通りでした。
の に は を た が で て と し れ さ ある いる も する から な こと として い や れる など なっ ない この ため その あっ よう また もの という あり まで られ なる へ か だ これ によって により おり より による ず なり られる において ば なかっ なく しかし について せ だっ その後 できる それ う ので なお のみ でき き つ における および いう さらに でも ら たり その他 に関する たち ます ん なら に対して 特に せる 及び これら とき では にて ほか ながら うち そして とともに ただし かつて それぞれ または お ほど ものの に対する ほとんど と共に といった です とも ところ ここ
Gitでやると嫌われること

#Gitアドベントカレンダー18日目です。遅れてすいません。
##Git使うことによる利点はいっぱいですがこんな人は嫌われます。
自分も最近はGIT使いになったのでこれまでのあったことで困ったことを書いておこうと思います。
思い当たった人ごめんなさい。
##しょっ中git pushしちゃう。
これ実は前職で経験したのですがSubversionと間違えてしょっ中Gitに上げちゃう人います。
これ一番困るんです。
何故なら常に開発していると一定間隔でcommitするんですが。
割り込まれちゃうので自分のソースが消えると言ったことが何度かありました。
Subversionみたいに常にリモート管理んらやらなくてはいけませんが。
Gitは分散管理です。
自分のところで開発コードが管理できます。
Git commitは自分のパソコンにソースを管理するコマンドです。
開発がある程度迷惑をかけない時点でgit pushでリモートサーバにソースコードを送信します。
これがGitの鉄則です。
##自分の開発ソースがどこになるか見失う
実はこれは私が去年やらかしました。
あまりにもGit pushの嵐に困った自分は開発用gitと管理用Gitに分けて自分のパソコンに保存していました。
ところがそのソースがないとできない開発が出てきてしまい管理用に一回git pullしてから開発すればいいやと思い作業していましたが間違って開発用のgitにpullしてしまい開発ソースを何回か破棄してしまいました。
当然開発は遅れます。
##ビルドが遅れる
これは実は半年前の職場でやってしまったことなんですが。
gitブランチが多くあるとどのブランチが自分の環境にあるか確認する作業が当然のことでてきます。
実際は上司がやることになっているのですが忘れてしまい何度かビルドが止まると言う手痛い事がありました。
自分はハード系は初心者でしたがLinuxの知識はそれなりにありました。
でも、それではハード系では生きてはいけないと言うことを痛感させられました。
あと言えることは先輩のミスでもかぶるのはビルダーということです。
今はどのようにしてるかはわかりませんがプレビルドしてるのかな？？？。

##嫌なことばかり書いてますが。いい点は多いですよ。
良い点といえば自分の環境が汚れない。
これに越したことはありません。
SubversionやCVSの嫌なところはリモートで管理しているため簡単にソースが他の人に書き換えられてしまいしょっ中修正に追われてしまうことです。
Gitは分散管理なので自分のコードを書いてテストしてからあげることができます。
その分だけ人の修正が減るわけです。

最後にこれだけは言わせてください。
<strong>Linusさすが俺たちのGod様こんなツール作ってくれてありがとう</strong>
Notionのいろいろな検索

[アルプ株式会社](https://thealp.co.jp/)でエンジニアをしています、集約のエンティティ@pictinyです。
これは[Notion Advent Calendar 2019](https://qiita.com/advent-calendar/2019/notion) 19日目のエントリです。
18日目は[Notionでストーリーポイントを集計する](https://qiita.com/pictiny/items/27161d704ac156be6a59)でした。

# 検索
Notionの検索機能にはいろいろあります。

## Quick Find
ページを検索します。
ページ内のコンテンツはほとんどヒットしません。

サイドバーから使えます。

<img width="470" alt="検索1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/e147fa2b-cb5f-eaec-63a8-f584aa236171.png">

<img width="588" alt="検索2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/bb287912-1a3b-39f7-aa69-f1566730d251.png">

ページタイトルで引っ掛けたいときに使うとよいでしょう。

## データベース
データベース内にあるデータを検索できます。
データベース右上から入力します。

<img width="447" alt="Screen Shot 2019-12-19 at 16.36.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/1768a5e6-4683-af69-d49c-582ef8dd3724.png">

タイトルを部分一致で絞り込めるので、データベースが大きくなるとよく使います。

## リレーション
リレーションプロパティで関連付けているデータベースのデータを検索できます。
リレーションプロパティから入力できます。

<img width="866" alt="Screen Shot 2019-12-19 at 16.36.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/5610cbf5-7638-49fe-35c8-add862243ce9.png">

タイトル検索ですが、前方一致なので注意しましょう。

# まとめ
Notionの検索はかなり低機能です。
ページの情報を構造化したり、データベースを駆使するように心掛けるとよいでしょう！
Lambda関数をそのままAWS Batchに乗せ換える(Python3.6限定)

この記事は [アラタナアドベントカレンダー2019](https://qiita.com/advent-calendar/2019/aratana) 19日目の記事です。

Serverlessでサービスを展開していると、どうしてもRDBを使いたくなったり、どこかでバッチ処理をしたくなったりしますよね。

RDBについては、RDS ProxyがLambdaから使用可能になりそうでいい兆しですね。

[Amazon RDS Proxy](https://aws.amazon.com/jp/rds/proxy/)

バッチ処理についてはかつてからAWS Batchというのが用意されています。

これはECRに用意したDockerを使って処理を実行できる環境です。Queueも用意されているのでかなり自由なバッチ設計ができると思います。

さて、Serverlessで環境を構築している場合、まず選択肢として、AWSBatchよりもLambdaを考えると思います。

StepFunctionやQueue、S3などのイベント、並行処理を駆使して構築する場合もあるでしょう。

ただ、Lambdaの実行制限は現状15分でそれを超える場合は何か手を考えなくてはいけません。

処理を分割する、並行処理を考える等あると思いますが、選択肢としてAWS Batchへの移行も入ってくるかと。


しかし、問題はECR上にDockerを用意しなくてはいけない点です。


AWS Batchを使用する場合、Lambdaで使っているソースコードをわざわざDockerに乗せて、ECRのレポジトリに突っ込まなくてはいけない。

これは非常に面倒臭いです。


そこでServerlessFrameworkを使っている場合限定ですが、いいPluginを見つけました。


[serverless-aws-batch](https://github.com/justinram11/serverless-aws-batch)

※　ただしこちらは不具合が多いので注意。。。


このプラグインはServerlessFrameworkからデプロイする際に、Lambdaで使用するソースをそのままDockerに乗せ、ECRにPushしてくれて、
Batchの設定まで行ってくれる夢のようなPluginです。

イベントや環境変数もそのまま使えます(Batchを起動するLambdaが自動で作られ、イベントや環境変数の設定も引き継がれる)。
serverless.ymlをちょっと書き換えるだけで全部構築してくれます。

ただ、先の大元のPluginはちょっとバグが多かったので、せめてPython3.6で動くように手を入れたものがこちらになります。

[serverless-aws-batch](https://github.com/YasuhiroKimesawa/serverless-aws-batch/tree/revert-2-revert-1-bugfix/various_bugfix)

※ 大元をForkしてまだプルリクを出していないのですが、取り込まれたらリンクを修正します。

使い方はGithubのReadmeに詳しく書いてあります。
基本serverless.ymlでiamにbatchを許可するのと、該当のfunctionにbatchの項目を付け足すだけで済むはずです。

ぜひ使ってみてください＼(^o^)／

Python3.6以外では動く保証はないですが、同じような修正をすればいけると思うので、ForkしてPRしてもらえればいいかと思います。

あらためて、大元のjustinram11さんありがとうございます！ナイスアイデアです！
2019年に実施したセキュリティ対策

世間でも騒がれるほど大きいサイバー攻撃が目立つ年だった2019年ですが、弊社で実施したセキュリティ対策についてまとめておきたいと思います。

## フォームへの不正アクセス対策
Webサービスローンチ直後から、海外のIPアドレスから「お問い合わせフォーム」へ不正なアクセスが繰り返された案件がありました。
即座に対応が求められたため、Googleの reCAPTCHA v3 を導入して解決しました。

### reCAPCHA v3
不可視のスコア判定をしてくれるモジュールで、v2の画像認証と比較してエンドユーザーのコンバージョンを落とすことなく導入できます。
導入した画面には reCAPCHAのアイコンが表示されます。
![recaptcha_icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18074/811c8886-88ab-3ee6-023d-954fff6a2d18.png)
画像認証がないため、ユーザーには優しい反面、スコアや独自のロジックで OK/NG を決めるため、不正なアクセスでなかったとしても NG と判定されてしまう可能性があります。
v3はAI判定とのことですが、それが一定量まで無料で使用できるのでおすすめです。
下記で詳しく紹介されていますので、参考までに。

> 「reCAPTCHA」って？スパム対策に効果的なreCAPTCHAをフォームに入れてみた
https://www.synergy-marketing.co.jp/blog/using_recaptcha_on_form

> reCAPTCHA v3 入れてみた
https://www.techscore.com/blog/2018/12/06/recaptcha-v3/


## AWS IAMでの不正アクセス対策
これはお恥ずかしい話になりますが、弊社で過去検証用に利用していたものの、最近はノーメンテナンスとなっていたAWSアカウントのIAMに対して、不正アクセスをされてしまいました。
検証用ということもあり、過去に担当した人間が、簡易のパスワードでIAMアカウントを発行しており、以降更新されていなかったことが原因でした。
侵入されて、バージニアのリージョンでp3インスタンスを2日間起動されて、AWSからのアラートで検知しました。

本件を皮切りにAWSアカウントへは下記対応を実施しました。
・GuardDutyの有効化 (全リージョン)
・不要なIAMの整理
・IAMユーザーの２段階認証化

GuardDutyについては下記で詳しく紹介されていますので、参考までに。

> 【初心者向け】AWSの脅威検知サービスAmazon GuardDutyのよく分かる解説と情報まとめ
https://dev.classmethod.jp/cloud/aws/guardduty-summary/

## SSHでの不正アクセス対策
SSHというと今更な感じもあり、秘密鍵を共有するのはどうなのか、という論点もあるかと思いますが、ひとつの案件で採用された方式のご紹介です。
SSHの接続元は「踏み台サーバ」に集約して、各サーバの秘密鍵を踏み台サーバに置かずに、ローカルに管理することで、鍵とIP制限の両軸でのセキュリティ強化を試みた例となります。

<img width="380" alt="スクリーンショット 2019-12-03 17.54.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18074/08425acd-448a-aaf1-1b83-d659994eb921.png">

```bash:ssh_command
ssh web@yyy.yyy.yy.yyy –p20022 -o ProxyCommand='ssh -W %h:%p fumidai@xxx.xx.xx.xxx -p20022' -i ~/.ssh/id_rsa_web
```

無料で手軽に使えるSaasが増えた反面、過去に使った・作ったものをそのままにしておかず、きちんと管理・メンテナンスにもコストをかけていきましょう、という教訓を身を以て体感した1年でした。
obnizとMATLAB/Simulinkをつなげてみた（Websocket使用）

# この記事で行うこと
- ObnizとMATLABをつなげる。
- MATLABコードをSimulink側で呼び出して、SimulinkとObnizをつなげてみる。

# 注意点
- PCのファンがガンガン回ります。
- 非公式ライブラリを使用します。
- ソースコードの使用は自己責任で。

# 用意するもの
- Obniz Board(私はBasic Licenseを使ってるけども、LANで何とかするのならHobby Lite Licenseでもいいかな？)
- MATLAB R2019b (Home License)
  - 使う人はSimulinkも導入
- ネットにつながるWifi環境(ポートがふさがれていないような優しい世界)
- [MatlabWebSocket](https://jp.mathworks.com/matlabcentral/fileexchange/50040-jebej-matlabwebsocket)
    - 導入方法はOverviewのGithubを参照すること。本記事ではこのライブラリのインストール方法は割愛します。

# 使い方
公開しているソースコードをローカルに持ってくる。zipをダウンロードしても、cloneして手元に持ってきてもどちらでもOK。
https://github.com/henjin0/ObnizforMATLAB

matlabrcに追記してツールボックスみたいに使ってもいいのだけれども、個人的には一発ネタなプログラムと受け取っているのでMATLABで使用する現在のフォルダに一式を丸ごとコピーしてすぐ捨てられる使い方のほうがいいかも。

使い方としては、こんな感じ。

```matlab:sample.m
% とりあえずワークスペースを初期化しよう 
clear
% ObnizのIDをコンストラクタに入れます。 
ows = ObnizWS('XXXX-XXXX');
% Obnizと実際につなぎます。
ows = ows.connect();
% Obnizの液晶に文字を表示します。
ows = ows.displayChar('Hello, Obniz!',true);
% ２番のGPIOに５Vを出力します。
ows = ows.gpioOutput(2,true);
% 0番のGPIOをGndにします。
ows = ows.gpioOutput(0,false);
% pwmも可能。ちなみに複数のピンでpwmをする際には、
% pwmNumを0から5の範囲でユニークな値を入れる。
% その他パラメータは https://obniz.io/ja/doc/sdk/doc/websocket/pwm 参照
%ows = ows.pwm(pwmNum,outputPin,freq,pulse,groundPin);
% AD変換値を取得します。
ows = ows.adStream(1);
% AD変換値の取得を終了します。
%ows = ows.deinitAd(1);
% ObnizWSを終了します。
%ows = ows.close();
```

本ライブラリは中に入っているクラスを呼び出して、Websocket経由でObnizに様々な命令を投げていく。
また、A/D変換値に関しては取得した値をMATファイルに書き込んでいく感じで、欲しいタイミングでMATファイルへ
load関数を呼び出してMATLAB/Simulinkで使用できる形にする。ファンが唸りを上げるのは大体これのせい。



Simulink側から呼び出すとこんな感じ。
![Simulink例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/af29045b-b442-47dd-db6d-2b64a85a475f.png)

手前にあるやつがケースに入ったObnizで可変抵抗が刺さっております。この可変抵抗の値を変えると、
Obniz2MATLABが数ms刻みで書き込みにかけているMATファイルの値をSimulinkが読み込み、その結果を
scopeに反映している。
...とはいいつつも、ネットを介している分レイテンシは存在するため注意すること。


# まとめ
- ObnizとMaTLAB/Simulinkをつなげてみた。
- ファンが回って怖い。
- MATLAB/Simulinkはいいぞ。
cwltoolをインストールしたくない

CWLを始めようとしたらまず cwltool をインストールしろなどと言われ、まじむり…となったことはありませんか？

何も考えずに以下のコマンドを打つべし (良い子のみんなは何も考えずにコマンドを打ってはいけない)

```
$ echo "alias='docker run --rm -it -v $(pwd):$(pwd) -w $(pwd) -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker:/var/lib/docker -v /tmp:/tmp commonworkflowlanguage/cwltool:1.0.20191022103248 --outdir $(pwd) --cachedir $(pwd)'" >> .bashrc && exec bash
$ cwltool --version
/usr/local/bin/cwltool 1.0.20191022103248
```

イエーイ！

`zsh` をお使いのみなさんは `.bashrc` を `.zshrc` に変えるとよろしいです。

## これはなに？

CWLはツールをコンテナで動かすのに runner である cwltool はコンテナで動かされへんの？という当然の疑問に答えるための魔法です。非常に邪悪な魔法であるためいくらか気をつける必要があります。一番気をつけるべきは `$(pwd)` をマウントしているため、 `commonworkflowlanguage/cwltool` のコンテナ内のファイルシステムに予め入っているディレクトリと衝突すると大変なことになります。 `/home` 以下とか `/Users/` 以下とかでやりましょう。
DOM操作

#DOM操作基礎編

###DOM操作とは
DOM操作とはhtml要素(ノード)を取得して、jsで操作してビューに動きをつけたりすることです。

###ノードの取得方法
1.　querySelector
　でcssで指定する時に使うセレクタで指定（同じセレクタが複数ある場合は一番最初に見つかったもののみ）
　querySelectorAllで同じセレクタ名のノード全てを取得
2.　getElementById
　でid名で取得
3.　getElementsByClassName
　で指定したクラス名の要素を全て取得

##〇〇.addEventListener('アクション', 関数)

〇〇に対してアクションが起きたら、関数が実行される。

```js
*例1
btn = document.getElementById('btn');
btn.addEventListener('click', 関数);
#btnがクリックされたら関数の処理がされる。

*例2 
btn = document.getElementById('btn');
btn.addEventListener('click', () => {
 console.log('hello world');
});

#{}の中の処理を行う
```

##Classを変更する

classList.add('クラス名') またはremoveでクラスをつけたり外したりできる。

```js
*add
btn = document.getElementById('btn');

btn = document.getElementById('btn');
btn.addEventListener('click', () => {
 btn.classList.add('red');
});　　　　　　 ※.remove('red')でredクラスを外す。
#redクラスがidがbtnの要素に加わる

```
Flutterで作ったアプリをfastlane+App Distributionで配信する（Android編）

[iOS編](https://qiita.com/renoinn/items/e082d43dc4973b0c0ba7)の続き。AndroidもローカルからApp Distributionに配信できるようにする。

#1.環境を分けてビルド
Androidの方はめちゃくちゃ簡単で、この記事を参考にgradleで設定すれば問題なくできる。flavorはiOSの側と合わせておく。
https://medium.com/flutter-community/flutter-ready-to-go-e59873f9d7de

#2.fastlaneのインストール
[iOS編](https://qiita.com/renoinn/items/e082d43dc4973b0c0ba7)でインストールしているのでスキップ。

#3.Fastfileを作る
flavorを分けたら、それに合わせてFlutterでビルドする。App DistributionではAppBundleに対応していないので、apkで出力する。
テストする端末に合わせて32bit版と64bit版両方欲しかったので、オプションに`--split-per-abi`も追加する。

```
$ flutter build apk --split-per-abi --release --flavor staging --target lib/main_staging.dart
$ cd android
$ fastlane init
```

`fastlane/Fastfile`と`fastlane/Appfile`ができるので、`Appfile`の`package_name`を確認しておく。今回はスキップしたけど、GooglePlayStoreにアップしたい場合は、GooglePlayConsoleから`json_key_file`を用意する。
https://docs.fastlane.tools/getting-started/android/setup/#collect-your-google-credentials


#4.Firebase App Distributionにアップする
androidの側でもfirebase_app_distributionプラグインをインストールする。途中、Gemfileを作るか聞かれるので"y"と答えておく。

```
$ fastlane add_plugin firebase_app_distribution
```

`Gemfile`と`Gemfile.lock`、`fastlane/Pluginfile`が作成される。あとはFastfileにfirebase_app_distributionのアクションを追加する。

```
default_platform(:android)

platform :android do

  desc "Submit a new Staging Build to Firebase AppDistribution"
  lane :staging do
    firebase_app_distribution(
      app: "（Firebase上のアプリ ID）",
      groups: "dev",
      release_notes: "from fastlane (32bit)",
      apk_path: "../build/app/outputs/apk/staging/release/app-staging-armeabi-v7a-release.apk",
      firebase_cli_path: `which firebase`.strip()
    )
    firebase_app_distribution(
      app: "（Firebase上のアプリ ID）",
      groups: "dev",
      release_notes: "from fastlane (64bit)",
      apk_path: "../build/app/outputs/apk/staging/release/app-staging-arm64-v8a-release.apk",
      firebase_cli_path: `which firebase`.strip()
    )
  
  end

end
```

apkの出力自体はすでにFlutterで行っているので、firebase_app_distributionの`apk_path`に出力されたapkのパスを渡してある。

firebase_app_distributionの実行にfirebase-toolsのバージョン7.4.0以上が必要なので、インストールする。今回はローカルからのアップなので、Firebaseへのログインもしておく。
すでにインストールしている場合はここはスキップする。

```
$ npm install -g firebase-tools
$ firebase login
```

以上で準備は整ったので、fastlaneを実行する。Gemfileを作ったので、`bundle exec`から起動する。

```
$ bundle exec fastlane staging
```

#おわり
[iOS編](https://qiita.com/renoinn/items/e082d43dc4973b0c0ba7)と同様に、App Distributionに配信できるようになった。
次はGithub Actionsでビルドしたり、GooglePlayにアップできるようにもしたいところ。
macOS Catalinaにしたら、PhpStormでgitのエラーが出た

# 唐突なPhpStormのgitエラー

```
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
```

いろいろ調べてみると、```xcode-select --install```が必要とのこと。

やってみる -> 正常終了 -> エラー変わらず。

さらに調べて

```
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```

が出てきたけど、そもそもXcodeを最近アップデートしてなかったので、AppStoreからアップデート。

その後

```
sudo xcodebuild -license
```
して、最後まで読んで```agree```とタイピング＆エンター

んでPhpStorm再起動したら直りました。

誰かの助けになれば幸いです。
cronを実行したログを出力する設定

# はじめに
cronを実行したログを、ログファイルに出力する設定を行った。

# 環境
Ubuntu 16.04.5 LTS

# 手順
このファイルを開く。

```
vi /etc/rsyslog.d/50-default.conf
```

デフォルトで、この行がコメントになっている筈なので、コメントを外して更新する。

```
cron.*                         /var/log/cron.log
```

しかし、ReadOnlyになっていたため更新ができず。書き込みできるように、パーミッションを変更する。

```
sudo chmod 646 /etc/rsyslog.d/50-default.conf
（変更前）　-rw-r--r--
（変更後）　-rw-r--rw-
```

cronのログは、rsyslogで出力する仕組みになっている。上記の設定を有効にするために、rsyslogのサービスを再起動する必要がある。

```
service rsyslog restart
```

サービスが起動されている事を念のために確認しておく。

```
service --status-all | grep rsyslog
```

ログはここに出力されるようになります。cronのログを、rsyslogが、書き込んでくれます。最初のログが、cron.logに書き込まれるタイミングで、cron.logが新規作成されます。

```
/var/log/cron.log
```

# 参考
https://qiita.com/skyfish20ch/items/46db70e1ff88166ead87


# cronのログを、rsyslogで書き出すのではなく、cronで実行されたコマンドを、標準出力したログにする

**crontab -e**で、下記の２行を追加する。

```
1 12 * * * cd $HOME && date "+\%Y/\%m/\%d \%H:\%M:\%S" >> ./syori01.log
2 12 * * * cd $HOME && /usr/local/rbenv/shims/ruby syori01.rb >> ./syori01.log
```

# 解説

12時01分に、システム日付と時間を、syori01.logに書き込み、続けて、12時02分に、syori01.rbというrubyのブログラムを実行して、syori01.logに標準出力します。注意点は、cronに日付を指定する場合、%をエスケープしないと上手くいなかいという点です。

・rubyが入っている場所は、**which ruby**をして見つけます。
・当処理は、Homeディレクトリで実行しています。

# 参考
https://qiita.com/doitnow420@github/items/70bc148d782e96492282
AWS Developer Associate 合格記録

# どした

**AWS Certified Developer Associate** を取得したので
どんなことやったのかなど、いつものように記録します。

# about me

**インフラエンジニア**で、開発知識は少ないです。(2019/12時点)
運用、開発者系のAWS資格では、SysOps Administrator Associateのみ保持しています。

今まで、ネットワーキング設計やサーバー構築など、いわゆるAWSでいう
**ソリューションアーキテクト側の経験を実質１年以上** 積んでいます。

今回の対象分野で言えば、ElasticBeanstalk, Lambda, CloudFormation等の経験はありますがまだ浅い状態です。
つまり、この記事はあくまで**インフラエンジニア目線**での学習記録になります。

# about DVA試験

AWS 認定開発者 – アソシエイト
https://aws.amazon.com/jp/certification/certified-developer-associate/

・AWSクラウドの基本的な考え方、主要なサービス、ベストプラクティス、使用方法などの理解
・AWSサービスを利用した**アプリケーションの開発、デプロイ、デバッグ**に関する各サービス知識
が必要です。

# 何をやったのか

### 試験ガイドとサンプル問題

こちらにAWS公式から「AWS 認定試験に備える」情報が記載されています。
「試験ガイドとサンプル問題」は、**はじめに必ず確認**ですね。
https://aws.amazon.com/jp/certification/certification-prep/

また、サンプル問題で言えば、以下のQiita記事が助かりました。
AWS認定デベロッパー アソシエイト 練習問題を解こう
https://qiita.com/Junpei_Takagi/items/9fa884e35990768c66de

サンプル問題に対して根拠などをシンプルに記載されており、学習のヒントを見つけることができました。
サンプルと言えど、答えしかないとなかなかどこから入ればいいか時間がかかったりしますからね。。
**※ちなみに模擬試験は、答えもワカラナイ**


結果、サンプル問題を解いて確認してことで、**「いま何が足りないか」**を明確にできた気がします。



### 模擬試験

下調べをした際に、試験を受けた皆さんが各種記事、ブログでよく書かれていたことは
Solution Architectなどの他のAssociate試験を受けたことがある人は、
**まず模擬試験を受けてみるべきだ**と言うことでした。

はい、その通りでした。(いわゆる腕試し的な)
結果は50%代で、足りないのはAPIやSQSのオプション機能、そしてDynamoDBの超基本DB系知識だということが判明したので、そこから始められました。

### 書籍

まず、以下の本で基本的なサービスの機能、動作を**主に図で**覚えました。
現状で足りない基本知識の補完作業です。

以前他のAssociate本でも活用しましたが、今回が一番役に立ちました。
SQS, Lambda, DynamoDBで特に助かりました。やはり図があるとスピーディに理解ができますね。

**AWS認定アソシエイト3資格対策~ソリューションアーキテクト、デベロッパー、SysOpsアドミニストレーター~**
https://www.amazon.co.jp/dp/4865941991
※直リンク

### 問題集

こちらは定番で、AWS問題集サイトです。
DVAに関しても少し問題が出ています。
https://aws.koiwaclub.com/

問題数も少ないので知識的な意味でも完璧にしてしまいます。

### udemy

有名なサイトですが、私が購入したのは以下２点です。

・Ultimate AWS Certified Developer Associate 2020
https://www.udemy.com/course/aws-certified-developer-associate-dva-c01/
・AWS 認定デベロッパー アソシエイト模擬試験問題集
https://www.udemy.com/course/aws-31955/

１つ目は、AWSドキュメントの長い文章を読む前に図示された説明をみる事で、頭に入りやすくするためです。
2つ目の問題集は、単純に演習がやりたかったからです。
この問題集は**本番よりもレベルが高い問題もある**と感じたので、あくまで演習や知識補完と考えていいと思います。
私はProfessionalも受ける予定なので、むしろ**今後に使える**かな、と感じました。


# 結果&まとめ

無事**合格**しました。
手応え的にはギリギリだったかもしれません。

今回の全体の感想はこんな感じです。

・学習してきた内容は本当にコアな内容だったから、やってよかった
・概念的な知識は、Solution Architectの学習で十分(CloudWatchなど、**開発者も使用するような被るサービス**もある)
・アプリケーションデプロイ時の用件に対して、DBやSQS、デプロイ系サービスのどのオプションを使えばいいのか、単純に知識がまだ少し足りない部分があった
・Code系サービス(CodeDeployなど)経験が少ないため、少しでも具体的に聞かれるとわからない事がある
・**ECS**をもっと色々使おう...
・ElasticBeanstalkって意外と色々ある
・**Professionalがもう心配**

また、最近よくAWS試験を受けていますが試験当日のルーチンは大事だなと強く思いました。


今後は、AWS資格に関しては残りの**DevOps Engineer Professional**を取得してから、**Specialty**へ進みます。
また、色々できなかったAWSの検証系をどんどんやってみて、公開しようと思います。*(ツッコミ、welcome)*
vagrant upで No usable default provider could be found for your system.

`vagrant up` で以下のエラーがでた。

```
No usable default provider could be found for your system.

Vagrant relies on interactions with 3rd party systems, known as
"providers", to provide Vagrant with resources to run development
environments. Examples are VirtualBox, VMware, Hyper-V.

The easiest solution to this message is to install VirtualBox, which
is available for free on all major platforms.

If you believe you already have a provider available, make sure it
is properly installed and configured. You can see more details about
why a particular provider isn't working by forcing usage with
`vagrant up --provider=PROVIDER`, which should give you a more specific
error message for that particular provider.
```

Vagrant がインストールされている VirtualBox のバージョンに対応していないときにでるエラー。環境構築し直したときによくなるやつ

上記は 

- VirtualBox 6.1.0
- Vagrant 2.2.6

で発生したので、VirtualBox のバージョンを 6.0.4 に下げたところ問題なく起動した。
眺めて覚えるGo言語

#眺めながら学習する超簡単言語!
## いきなりGOLANG
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274715/8b26feee-97c6-4b1e-f4a4-5f2745dceb81.png)

```ex1.go
package main
import (
	"fmt"
)
func main() {
	fmt.Println("hello world")
}
C:\Users\hirat\go-work\web\hello>go run ex1.go
hello world
```
#お決まりのコンソール出力

```ex2.go
package main
import (
	"fmt"
)
func main(){
	a:=10
	b:=20
	c:=a+b
	fmt.Println(a,b,c)
}
C:\Users\hirat\go-work\web\hello>go run ex2.go
10 20 30
```
#単純整数計算でした。
```ex3.go
package main
import (
	"fmt"
)
func main() {
	a := 10
	b := 20
	c := a * b
	fmt.Printf("a=%v b=%v c=%v", a, b, c)
}
C:\Users\hirat\go-work\web\hello>go run ex3.go
a=10 b=20 c=200
```
#Printfを使って出力

```ex4.go
package main

import (
	"fmt"
)

func main() {
	a := 10
	b := 3
	c := a / b
	fmt.Printf("a=%v b=%v c=%v", a, b, c)
}
C:\Users\hirat\go-work\web\hello>go run ex4.go
a=10 b=3 c=3
```
#整数で計算でした

```ex5.go
package main

import (
	"fmt"
)
func main() {
	a := 10.0
	b := 3.0
	c := a / b
	fmt.Printf("a=%v b=%v c=%v", a, b, c)
}

go run ex5.go
a=10 b=3 c=3.3333333333333335
```
#浮動小数点になります

```ex6.go
package main

import (
	"fmt"
)

func main() {
	s:=0
	for i:=1;i<=10;i++{
		s+=i
	}
	fmt.Printf("s=%v",s)
}
C:\Users\hirat\go-work\web\hello>go run ex6.go
s=55
```
#サクッとfor loop

```ex7.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	for i:=1;i<=10;i++{
		s:="Hello "+strconv.Itoa(i)+"\n" //intから文字列に変換
		fmt.Printf(s)
	}	
}
C:\Users\hirat\go-work\web\hello>go run ex7.go
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Hello 8
Hello 9
Hello 10
```
#整数を文字列に変換して(strconv.Itoa)

```ex8.go
package main
import (
	"fmt"
)
func main(){
	a:=[]int{1,2,3,4,5,6,7,8,9,10}
	for _,x:=range a{
		fmt.Println(x)
	}	 
}
C:\Users\hirat\go-work\web\hello>go run ex8.go
1
2
3
4
5
6
7
8
9
10

```
#サクッとforeach

```ex9.go
package main
import (
	"fmt"
	"strings"
)
func main(){
	a:=strings.Split("千葉,西千葉,稲毛,新検見川,幕張,幕張本郷,津田沼,東船橋,船橋,西船橋,下総中山,本八幡,市川,小岩,新小岩,平井,亀戸,錦糸町,両国,浅草橋,秋葉原",",")
	for i,x:=range a{
		fmt.Println(i,x)
	}	 
}
C:\Users\hirat\go-work\web\hello>go run ex9.go
0 千葉
1 西千葉
2 稲毛
3 新検見川
4 幕張
5 幕張本郷
6 津田沼
7 東船橋
8 船橋
9 西船橋
10 下総中山
11 本八幡
12 市川
13 小岩
14 新小岩
15 平井
16 亀戸
17 錦糸町
18 両国
19 浅草橋
20 秋葉原

```
#サクッとforeach index

```ex10.go
package main

import (
	"fmt"
	"strings"
)
func main(){
	a:=strings.Split("千葉,西千葉,稲毛,新検見川,幕張,幕張本郷,津田沼,東船橋,船橋,西船橋,下総中山,本八幡,市川,小岩,新小岩,平井,亀戸,錦糸町,両国,浅草橋,秋葉原",",")
	for i,x:=range a{
		if strings.Contains(x,"新"){
			fmt.Println(i,x,"新が含まれます。")
		} else if strings.Contains(x,"橋"){
			fmt.Println(i,x,"橋が含まれます。")
		} else {
			fmt.Println(i,x)
		}		
	}	 
}
C:\Users\hirat\go-work\web\hello>go run ex10.go
0 千葉
1 西千葉
2 稲毛
3 新検見川 新が含まれます。
4 幕張
5 幕張本郷
6 津田沼
7 東船橋 橋が含まれます。
8 船橋 橋が含まれます。
9 西船橋 橋が含まれます。
10 下総中山
11 本八幡
12 市川
13 小岩
14 新小岩 新が含まれます。
15 平井
16 亀戸
17 錦糸町
18 両国
19 浅草橋 橋が含まれます。
20 秋葉原
```
#とりあえずこれだけ知っていたら書けます。
Introduction to Geochemical modeling, Cambridge

```{r}

#Introduction to Geochemical modeling

#p.85

m=5;vec=sample(1:10,m,replace = T)

A=t(t(vec))%*%t(vec)+diag(vec)

x0=t(t(c(rep(1,m))))

t=seq(0,10,0.01)

U=eigen(A)$vectors

lambda=diag(eigen(A)$values)

inv_U=solve(U)

U%*%lambda%*%inv_U

t_vec=seq(0,5,0.01)

X=array(0,dim=c(ncol(A),length(t_vec)))


for(t in 1:length(t_vec)){  
  
x=array(0,dim=c(ncol(A),1))  
  
for(j in 1:ncol(A)){
  
x=x+as.numeric(exp(eigen(A)$values[j]*t_vec[t]))*as.numeric(t(t(U[,j]))%*%t(inv_U[j,])%*%x0)

}

X[,t]=x
  
}



```

```{r}

#p.95

m=5;vec=sample(1:10,m,replace = T)

A=t(t(vec))%*%t(vec)+diag(vec)

x0=t(t(c(rep(1,m))));b=rep(1,m)

U=eigen(A)$vectors

lambda=diag(eigen(A)$values)

inv_U=solve(U)

U%*%lambda%*%inv_U

t_vec=seq(0,5,0.01)



for(t in 1:length(t_vec)){  
  
x=array(0,dim=c(dim(A)))  
  
for(j in 1:ncol(A)){
  
x=x+as.numeric(exp(eigen(A)$values[j]*t_vec[t]))*(t(t(U[,j]))%*%t(inv_U[j,]))

}

exp_At=x

X[,t]=-solve(A)%*%b+exp_At%*%(x0+solve(A)%*%b)
  
}


```

```{r}

#Introduction to Geochemical Modeling p.150

library(dplyr)

k=0.1;m=5

E=100;N=50

alpha=0.1;k=1-alpha


cost=function(ep_i,a,b){

ni=exp(a*ep_i/k);N=sum(ni)  
  
S=k*sum((ni/N)*log(ni/N))

S_star=S+a*(sum(ep_i*ni)-E)^2+b*(sum(ni)-N)^2

print(S_star)

}



h=0.01

epsilon_i=sample(1:10,m,replace=T)/10

ite=1000;eta=0.0001

for(l in 1:ite){
  
vec=epsilon_i  
  
 for(j in 1:m){
   
vec_sub=vec;vec_sub[j]=vec_sub[j]+h   
   
epsilon_i[j]=epsilon_i[j]-eta*(cost(vec_sub,alpha,beta)-cost(vec,alpha,beta))/h   
   
 } 
  
ni=exp(alpha*epsilon_i/k);N=sum(ni)  

S=k*sum((ni/N)*log(ni/N));

print(cost(epsilon_i,alpha,beta))
  
}

```

```{r}

#p.308

m=5

x0=t(t(sample(1:100,m,replace=T)))

x=t(t(sample(1:100,m,replace=T)))

vec1=sample(1:10,m,replace=T);vec2=sample(1:10,m,replace=T)

g1=function(y){
  
return(sum(((y-vec1)^2)))  
  
}

g2=function(y){
  
return(sum(((y-vec2)^2)))  
  
}

ite=100

h=0.01

for(l in 1:ite){
  
f1=c();f2=c();x_vec=x

for(i in 1:m){
  
x_vec_sub=x_vec;x_vec_sub[i]=x_vec_sub[i]+h  
  
dg1=(g1(x_vec_sub)-g1(x_vec))/h  
 
dg2=(g2(x_vec_sub)-g2(x_vec))/h 

f1=c(f1,dg1)

f2=c(f2,dg2)
 
}

f_mat=cbind(f1,f2)

x=x0-f_mat%*%solve(t(f_mat)%*%f_mat)%*%(t(f_mat)%*%(x-x0)-t(t(c(g1(x),g2(x)))))

lambda=solve(t(f_mat)%*%f_mat)%*%t(f_mat)%*%(x-x0)

cost_c2=as.numeric(t(x-x0)%*%(x-x0))-sum(lambda*t(t(c(g1(x),g2(x)))))  

print(cost_c2)

}


```

```{r}

#p.337

B=t(matrix(c(1,2,0,0,0,0,2,0,0,1,0,2,1,1,0,0,0,0,0,2,0,1,0,0),nrow=4))

P=diag(1,nrow(B))-B%*%solve(t(B)%*%B)%*%t(B)

n=t(t(rep(10,nrow(B))))

C=sample(1:10,nrow(B),replace=T)/10

ite=10000;alpha=0.001

for(l in 1:ite){
  
  n_pre=n
  
  dG_pre=c(C+log(n+1)-rep(1,nrow(B))*as.numeric(log(t(rep(1,nrow(B)))%*%n)))
  
  #n=n+alpha*(P%*%dG)
  
  mu=C+log(n/sum(n));
  
  mu_pre=mu
  
  if(sum(n+alpha*mu>0)==nrow(B)){
 
  n=n+alpha*mu  
   
  dG=c(C+log(n+1)-rep(1,nrow(B))*as.numeric(log(t(rep(1,nrow(B)))%*%n)))
  
  }
  
  mu=C+log(n/sum(n));
  
  f=as.numeric(t(P%*%mu_pre)%*%(P%*%mu))
  
  G=sum(c(n)*(C+log(c(n+1))-log(sum(n))))
  
  #print(G)
  
  print(f)
  
}

```
Canvas: trying to draw too largeで落ちる

こんなエラーが出て落ちる

java.lang.RuntimeException: Canvas: trying to draw too large(******bytes) bitmap.

drawableに大きい画像があると出るそうですが、なかった。

代わりに、AndroidManifest.xmlの<application>内に

        android:hardwareAccelerated="false"

を入れたら直りました。
本番運用しているサーバーでうっかり「chown root:root -R /」 してsuコマンドが死んだが、なんとか復活できた話

# 前文
巷で
[本番環境でやらかしちゃった人 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/yarakashi-production)
こちらのアドベントカレンダーが流行っていると聞いたので、私もうっかりやらかしたこと、反省点、そしてそこからサーバーを復帰させるまでの話を書こうと思います。

なお、この物語は実話のため少しteratailを検索すると事例が見つかると思いますので先に晒します。
[CentOS6 chown root:root / したらsuが使えなくなった](https://teratail.com/questions/112705)
当時の私の焦りなどが出ているかと思います。当時答えてくださったかた（すでに退会されているようですが…）本当にありがとうございます。


# 事件が起きるまでの概要
1. すでに開発者の退社した本番運用されているシステムの入っている仮想サーバーに、apacheのバーチャルホストを追加してシステムを新しく建てることになった。
+ やってみたが、どうもパーミッション関係で上手くいかない。
+ 「あ、アップロード時にファイルが全部root権限になっちゃってる！じゃあこのディレクトリまるごとapacheの持ち物にしたら動くよね！」
+ `# chown apache:apache -R /`　←ドットの付け忘れタイポする。
+ ルートディレクトリが丸ごとapacheの持ち物に！？！？ とりあえずrootに戻してもう一回apache権限を正しい範囲につけなおそ！
+ `# chown root:root -R /`
+ ほかにも `ls -l`の力を借りてmysqlやapache、/devや/homeの中身等は一通り他の同時期に借りたサーバーと突き合わせて権限修正。
+ とりあえずWebサーバーは見えたぞ！解決だな！と`exit`でrootアカウント及びapacheアカウントを抜ける。
+ 「解決！やったあ！もう一度サーバーに入って改めて見直そう！」
+ 「あれ？　`su -`が効かないぞ・・・？」

事件の流れを追えばわかります。**この事件を起こした人はアホです。**まあ私なんですが。


# サーバーの状況を確認
teratailのページを見ればわかるのですが、回答者がこう答えています。

>可能であれば新規に環境を構築し、必要なファイルのみ移すといった対応がよいかと思います。

**私もそう思います。**しかし、このサーバーにはその時点では「**私がソースコードを所持していない、管理者が退職したシステム**」が動いていました。
その上Webサーバーとしては表面上全く問題なく動いているため、現行で使ってる人は居ます……。問題が発生するまでは時間の問題ですが。


# まずは`su`の復活から
突然ですが、何故`chown root:root -R /`したら`su`コマンドが動かなくなったのでしょうか？

それは**setuid**というものが`chown`による権限変更をすると強制的に外れてしまうからです。

ではこの**setuid**というのは何でしょうか？

それについてはこのように説明されています。

> プログラム実行時アカウントを一時的に変更することで一般ユーザが投入したコマンドの中で特殊な権限を必要とする処理の実行を可能にする機能である。
-- [IPA ISEC 第７章　セキュアUnix/Linux プログラミング](https://www.ipa.go.jp/security/awareness/vendor/programmingv1/b07_03.html)より

とのことです。この件で初めてその存在を知りました。

幸いにも仮想サーバーだったため、サーバーそのものの提供されている機能を利用してsuは使えなくてもroot権限でコンソールに入ることは出来ました。
（また、エラー文から検索をかけ、sudoコマンドを復帰させることは成功しました）
そして`/bin`に入って`ls -l`をしてみたところ、出るわ出るわ、大量のuidのセットされていない、本来ならばセットされているはずのアプリケーションを発見しました。

ここまで原因を絞り込んだら大丈夫です、setuidをすればいいのです。
`# chmod u+s /bin/su`
ついにsuが復帰しました！


# すべては力業
ここまで解決したところで一つの欲が頭をもたげます。
「もしかして、二つのサーバーの状態を突き合わせたら、全部`setuid`または`setgid`でなんとかなるんじゃないか？」
「特殊なアプリケーションは導入していないのは知っているし、ログ等もしっかりともう一つのサーバーと見比べて`chown`していったら治るんじゃないのか？」

結論から言いますと**やりました**そして**わりとなんとかなりました**ただし**丸一日かかりました**その上**今でもたまにそれ由来のエラーが出ます**。
多分素直にサーバー移転したほうがかかった時間は短かったと思います・・・半分以下くらい・・・。


# 反省
まずそもそも仮想サーバーならば「**問題が起きたら崩して作り直す**」を鉄則としましょう。
世の中`chown root:root -R /`なんてやるアホ、そしてそこから継続して動かそうとするアホは滅多にいないため、想定外・調べても出ないエラーがいっぱい出ます。
やらかした人が社内に私一人のため原因がわかりやすく問題が大きくなりづらいですが、これが複数人重なったら大事件です。

そもそもタイポしない。そしてタイポ内容を送信しないようにする。
**完全にタイポをなくすのは不可能です。**せめてコマンドを入力する際はダブルチェック、指差し確認、コマンドそのものを別の場所に保存して確認を行いましょう。**入力して即エンターなんて以ての外です。**

**`chown apache:apache -R /`の段階で立ち止まる。**
そもそも詳しい人、やらかしたことがある人は気が付くと思います。
`chown apache:apache -R /`だけなら処理は途中で止まっているハズなのです。
つまりここの一番の失敗は**apache権限になったroot画面を見て焦って `chown root:root -R /`したこと**なのです。
人間焦って進めようとせず、ちゃんと一呼吸置くことが必要かと思います。**たいてい悪いことが起きます**。


# おわり
以上、私の本番環境でやらかしたことでした。
みなさんもchownコマンドを使うときは、うっかりやってしまわないようにしっかり注意して行いましょう。
お疲れ様でした！
Shapely インストル時にerrorが発生した件

#####Window 10にpythonのshapely ライブラリーをpip install時に下記のようなエラーが表示された：


```
Collecting shapely
  Downloading https://files.pythonhosted.org/packages/a2/fb/7a7af9ef7a35d16fa23b127abee272cfc483ca89029b73e92e93cdf36e6b/Shapely-1.6.4.post2.tar.gz (225kB)
    100% |████████████████████████████████| 235kB 6.2MB/s
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\AppData\Local\Temp\pip-install-gcveixsc\shapely\setup.py", line 80, in <module>
        from shapely._buildcfg import geos_version_string, geos_version, \
      File "C:\Users\AppData\Local\Temp\pip-install-gcveixsc\shapely\shapely\_buildcfg.py", line 200, in <module>
        lgeos = CDLL("geos_c.dll")
      File "C:\Users\AppData\Local\Continuum\anaconda3\lib\ctypes\__init__.py", line 356, in __init__
        self._handle = _dlopen(self._name, mode)
    OSError: [WinError 126] 指定されたモジュールが見つかりません。
<font color='red'>Command "python setup.py egg_info" failed with error code 1 in C:\Users\AppData\Local\Temp\pip-install-gcveixsc\shapely\</font>
```
以下の方法を試しましたが失敗、、、
- pip upgrade
- tar.gzをダウンロードしてインストール

#####Pypiのshapely project descriptionにこの記載を見つけった：
>Windows users have two good installation options: the wheels at http://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely and the Anaconda platform’s [conda-forge](https://conda-forge.github.io/) channel.

それから、システム(32-bit or 64-bit)とPythonのバージョンを確認した上、
https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely
から.whlファイルをダウンロードしてもう一回試しました。

```
pip install C:\Users\Downloads\Shapely-1.6.4.post2-cp37-cp37m-win_amd64.whl .
```

今回は無事にインストールできました〜
Vue.jsでmethodsのテストを書く方法

公式とかみてもあんま直接的な情報はなかったので。
複数人で開発していると、本来サーバー側でやってほしい処理などがmethodsとかに入り込みがちですよね。
誰もメンテできなくなる前にしっかりとテストコードを書いておき、いつでもリファクタリングできる状態をキープしておきたいものです。

# 環境
- vue-test-utils
- jest

テスト環境の構築方法などは[vue-test-utils](https://vue-test-utils.vuejs.org/ja/)や[jest](https://jestjs.io/ja/)の公式を参考にしてください。

# サンプル

こんな感じのかなり尖ったコンポーネントがあるとします。

```vuejs
<template>
  <button @click="sampleMethod()" />
</template>
<script>
export default {
  methods: {
    sampleMethod() {
      return 'hello'
    }
  }
}
</script>
```

このコンポーネントの`methods`のテストは下記のように書くことができます。

```javascript
import { createLocalVue, shallowMount } from '@vue/test-utils'
import Vuex from 'vuex'

import SampleComponent from '~/components/SampleComponent'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('SampleComponent', () => {
  let wrapper

  beforeEach(() => {
    wrapper = shallowMount(UseHighway, { localVue })
  })

  it('sampleMethodsのテスト', () => {
    expect(wrapper.vm.sampleMethod).toBe('hello') // 通る
  })

  it('sampleMethodsのテスト(落ちるパターン)', () => {
    expect(wrapper.vm.sampleMethod).toBe('hey') // NG
  })
})
```

# 解説
[vue-test-utils](https://vue-test-utils.vuejs.org/ja/api/wrapper/#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)のドキュメントには、

> wrapper.vm を使って vm のプロパティとインスタンスメソッドにアクセスできます。これは、Vue コンポーネントのラッパもしくは Vue コンポーネントをバインディングしている HTMLElement のラッパにのみ存在します。

とあります。なので、上記のコードにおいては`wrapper.vm`で`methodsを`呼び出せるわけですね。
実際には上記のコードのように副作用の全くないコードは珍しく、実際には`setProps`や`setData`などで値をセットしてからテストすることになると思います。


# まとめ
エンジニアとして、やはりフロントエンドにおいてもできるだけ副作用が少なく、テストしやすいコードを書くことを意識していきたいですね。