OmniAICreator/Qiita-1.07M huggingface dataset:
AWS Summit - AWS Glue, AWS Lake Formation で実現するServerless Analystic

## AWS Glue概要
- マネジメントサーバレスETLサービス
- 開発者、データサイエンティスト向けのサービス
- 35+ 機能
- データのカタログ化
  - Auto Glowing
  - Apache Hive Metastore互換
  - 分析サービスとの統合
- サーバレスエンジン
  - Apache Spark
  - Python shell
  - Bach job
  - インテラクティグ?
- Auto Scalation
  - Schedule

## データディスカバリー
- Performance
  - 1日9000万

## サイエンス
- Apache Spark
  - provision、管理不要
  - Auto Scaling
  - オンデマンド
- Apache Spark Core: RDD
- Data Frame
  - SparkSQL core data
  - SQLのような分析に適合
- Dynamic Frame
  - Recored schema every data, 前列のスキーマ不要
  - 単一パスで多数のフローを実施する
  - Glue Parquet Writer
    - 標準Parquet Writer
    - Glue Parquet Writer
  - Performance
    - 構成, 10DPU, Apache Spark 2.      
    - WorkLoad
      - JSON -> Parquet
    - DynamicFrame 78s
    - DataFrame 195s

## AWS Glue実行モデル
  - Driver -> Multiple Executor
  - 連続敵なLogging
  - Remove Apache Spark log message filter out
  - Progressbar 
  - Job Metric
    - base Apache Spark metrics
    - driver egze
      - 30s summary
      - real time cloudwatch
  - Memory monitoring
    - DataFrame many small file task, too more task, too many memory used
    - DynamicFrame auto group task by small file
    - worker types
      - default
      - G.1x
      - G.2x 
  - Python shell
    - SQL base anaylice
    - middle size ML
    - Python 2.7 / 3.6 supported
      - boto3, awscli, numpy, scipy, pandas,... installed
    - spinup: under 20s
    - netword address supported. 
    - size: 1DPU, 1/16 DPU
  - Python shell filtering
    - cost : 0.6$

## auto scalition
  - event base
    - lambda
  - schedual event
  - entity
    - glue 
    - job
    - trigger
  - event
    - schedule
    - event
    - extenal
  - control
    - ...
- workflow feature
  - authoring DAG
  - workflow rerun
  - moniting
- Updates
  - network
    - Reverse DNS support
    - VPC endpoint support for Glue
  - Job, trigger -> Resouce tagging
  - notifications

- AWS Lake Formation
  - secure construction and manage
  - Sample of steps required
    - Find Resouces
    - Create S3 locations
    - Configure access policies
    - Map tables to Amazon S3 locations
    - ETL jobs
    - Create metadata access policies
    - Configure access from analytics services
    - Rinse and repeat for other
    - Manaul | Error | ??
  - Collecting and クレンジング
  - dataをセキュアに保管
  - Security
    - SQL style grant revoke permissions
    - EMR-Spark, Athena, Redshift, Glue
  - Collection
    - ML transforms for fuzzy record matching
    - Blueprints: Cloudtrail / ALB
  - Data discovery
  - Rap of Glue
  - コンプラインア
    - HIPAA BAA
    - ISO
    - PCI
    - ???
Railsで基本情報技術者試験の過去問題サイトを作る（７：Herokuデプロイ〜自動化編）

# はじめに
ゆる〜く学ぶ。みんなのWeb勉強コミュニティー。　「にゅ〜ぶる会」を運用中です。
https://newburu.github.io/

そこで、何か教育用のコンテンツが欲しいなぁ〜と思い立ち、今回の企画をスタートしました！

Railsで基本情報技術者試験の過去問題サイトを作ります！

# 最終目標
- 問題・回答の登録は、Scaffoldで簡易でOK
- APIを用意して、ランダムに問題を抽出する機能を追加する
- TwitterBOT、LINEBOT、SlackBOTが出来たら良いな

# 履歴
１：構築編
　　https://qiita.com/newburu/items/ed59f47ac645b19620f6
２：日本語化(i18n)編
　　https://qiita.com/newburu/items/4f12fdb61bf6cd601545
３：親子関係、登録編
　　https://qiita.com/newburu/items/f2a20289be5ec1fc1b77
４：親子関係、参照編
　　https://qiita.com/newburu/items/51b11bd02691efc2cc0d
５：API編
　　https://qiita.com/newburu/items/89f9f847a2648bdd006c
６：SlackBOT編
　　https://qiita.com/newburu/items/aeeb9acb453da786bd59
７：Herokuデプロイ〜自動化編
　　本ページ

# 今回やる事
- Herokuにデプロイし、定期的にSlackに投稿する設定を行う。

# Herokuにデプロイします

## 1. GitHub、Herokuにプロジェクトを新規作成します。

この辺りは、既に色々情報が溢れていると思いますので、省略させて頂きます。

- GitHubにソース一式をプッシュします。
- HerokuとGitHubを紐付け、Herokuにデプロイします。
- MySQLを追加します。
- DBを作成する。（rake db:migrate）
- DBに問題を登録する。

## 2. 環境変数を設定しましょう。

デプロイまで完了したら、本プロダクトに必要な環境設定を行いましょう。

その前に、自由に設定変更出来るように、ソースに環境変数を追加しました。

```ruby:lib/tasks/slack_bot.rake
require 'slack'

Slack.configure do |config|
  config.token = ENV["SLACK_API_TOKEN"]  # BOTのトークンは公開できない為、環境変数で設定します。
end

namespace :slack_bot do
  desc 'ランダムに問題をSlackに書き込む'
  task :random_question => :environment do
    question = Question.random
    source = "出典：#{question.category1}年度 #{question.category2} 基本情報技術者試験 午前 問#{question.category3}"
    msg = "【問題】\n#{question.msg}\n\nア　#{question.answers[0].msg}\nイ　#{question.answers[1].msg}\nウ　#{question.answers[2].msg}\nエ　#{question.answers[3].msg}\n\n#{source}"

    # text:出力テキスト、channel:出力先のチャンネル名、username:表示ユーザ名
    Slack.chat_postMessage(text: msg, channel: ENV["SLACK_CHANNEL_NAME"], username: ENV["SLACK_BOT_NAME"])
  end
end
```

設定する環境変数は以下の通り。

|環境変数|内容|
|:--:|:--:|
|SLACK_API_TOKEN|スラックのBOTトークン|
|SLACK_CHANNEL_NAME|出力先のチャンネル名|
|SLACK_BOT_NAME|表示ユーザ名|

## ３. 定期的に実行する設定を行います。

Herokuに「Heroku Scheduler」というものがありますので、それを追加してください。
<img width="193" alt="Heroku Scheduler" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118481/3b209943-a5ae-f2c4-c4d3-8d09109aad3a.png">

設定内容は、以下の通り。

|設定値|内容|補足|
|:--:|:--:|:--:|
|実行時間|毎日PM３時|UTCでの設定のため、日本時間(JST)にすると、毎日AM0時|
|実行コマンド|rake slack_bot:random_question||

## 4. 手動で動かして問題ないか確認しましょう。

```terminal:手動実行
heroku run rake slack_bot:random_question --app {Herokuアプリ名}
```
これで思い通りのチャンネルに投稿されればOKです！


# 今回はここまで
ありがとうございました！
次回は、基本情報の問題には、表が含まれる場合がありますので、問題に画像を登録出来るようにしたいと思います。
三項演算子のネスト

# 概要
三項演算子のネストについての記事をあまり見つけることができなかったので、少し多くネストさせてみました。

# 実際にネストさせてみる

この三項演算子の結果はどのようになると思いますか？
予想してみましょう！


```js
const prefecture = ["Tokyo", "Osaka", "Kyoto", "Aichi"];

prefecture.length < 4
  ? prefecture.push("Aichi")
  : prefecture.length === 1
  ? prefecture.push("Osaka")
  : prefecture.length === 2
  ? prefecture.push("Kyoto")
  : !prefecture.length
  ? prefecture.push("Tokyo")
  : console.log(prefecture);

console.log(prefecture);

```


### 結果
```js
["Tokyo", "Osaka", "Aichi", "Aichi"]
["Tokyo", "Osaka", "Aichi", "Aichi"]
```
`prefecture`変数のlengthが4なのでどの条件にも当てはまらず、console.logが2回実行されました。

## if文だとこうなる

```js

const prefecture = ["Tokyo", "Osaka", "Kyoto", "Aichi"];

if (prefecture.length < 4) {
  prefecture.push("Aichi");
} else if (prefecture.length === 1) {
  prefecture.push("Osaka");
} else if (prefecture.length === 2) {
  prefecture.push("Kyoto");
} else if (!prefecture.length) {
  prefecture.push("Tokyo");
} else {
  console.log(prefecture);
}

console.log(prefecture);

```

こうすれば見慣れた感じになるので少しだけ整理ができると思います。

---

どれだけ三項演算子をネストさせても、**AでなければB**ということを覚えておくだけで、実装中にごちゃごちゃになることは防ぐことができると思います。
もし、ごちゃごちゃになりそうだったら、コメントを残していくのもいいですね。

```js
const prefecture = ["Tokyo", "Osaka", "Kyoto", "Aichi"];

// lengthが4より少なければprefecture.push("Aichi")
  prefecture.length < 4
  ? prefecture.push("Aichi")

// lengthが1であればprefecture.push("Osaka")
  : prefecture.length === 1
  ? prefecture.push("Osaka")

// lengthが2であればprefecture.push("Kyoto")
  : prefecture.length === 2
  ? prefecture.push("Kyoto")

// lengthが0であればprefecture.push("Tokyo")
  : !prefecture.length
  ? prefecture.push("Tokyo")

// 上記の条件がどれも当てはまらなければconsole.log(prefecture)
  : console.log(prefecture);

console.log(prefecture);

```

# さいごに
開発中のコードは上記のようにシンプルなものではないと思います。
特にJSXで三項演算子を実装すると、もっと複雑になりそうですよね。
ですが、どれだけ複雑になろうと処理方法は変わらないので自分のソースコードを整理しながら実装していくのが自分にも読み手にも優しいのかなと思いました。
ffmpeg-pythonで動画分割

##**はじめに**

pythonからffmpegを使って動画の加工を行うのが目的です。
動画にフィルターをかけたら音声は消えてしまい、別々に処理する必要があったため備忘録として残します。
~~ffmpegを直接使えばいいのでは？~~


##**対象**

pythonを使ってプログラミングができる人
pythonのコードだけで完結させたい人


##**環境**
**テスト環境**
OS:  Windows 10, 64bit
python:3.6 (Anaconda)

ffmpeg: 4.1.3(ffmpeg-20190610-80e68ce-win64-static.zip)
ffmpeg-python: 0.1.18


##**準備**

**1. [ffmpeg](https://ffmpeg.org/)のダウンロードとPath設定**

　・[リンク先](https://ffmpeg.zeranoe.com/builds/)のDownload Buildをクリックしてzipファイルをダウンロードする。
　・適当な場所で展開する。
　・binフォルダのpathを環境変数pathに設定する。


**2. [ffmpeg-python](https://pypi.org/project/ffmpeg-python/)のインストール**


```python:ffmpeg-pythonインストール
pip install ffmpeg-python
```


##**Code**





```python:動画にフィルター適用

import ffmpeg

fname = 'in.mp4'

#動画全体の時間を調べる
video_info = ffmpeg.probe(fname)
duration = float(video_info['streams'][0]['duration'])

#後半の半分だけ取り出し
divide_sec = duration / 2

stream = ffmpeg.input(fname, ss=divide_sec, t=divide_sec)

#音声取り出し
audio_stream = stream.audio

#開始から5秒かけてフェードイン
stream = stream.filter('fade', type='in', start_time=0, duration=5)
audio_stream = audio_stream.filter('afade', type='in', start_time=0, duration=5)


stream = ffmpeg.output(stream, audio_stream, 'out.mp4')

ffmpeg.run(stream)

```
フェードアウトはtypeにoutを入れるとできます。

##**追記 200405**

ノイズ除去の場合
パラメータは参考サイト参照

```
audio_stream = audio_stream.filter('afftdn')
```




##**参考・資料サイト**

- [ffmpeg-pythonを使ってみた Qiita](https://qiita.com/ayumu838/items/4f20d47ca7e9f5fbcfca)

- [Add `.audio` + `.video` operators](https://github.com/kkroening/ffmpeg-python/commit/881ae4efff9e3c0d987217f9437a254730315b87)

- [afftdn](http://ffmpeg.org/ffmpeg-filters.html#afftdn)
Jetson NanoでDockerを動かす(実践編)

# はじめに

この記事は「[Jetson NanoでDockerを動かす(後編)](https://qiita.com/yamamo-to/items/51bfb92a00033257f02a)」でJetson Nanoにデフォルトで含まれているdockerでGPUにアクセスできることを確認できたので実際に何か動かしてみる。ここでは`ResNet50`を使って画像を識別する。

# 準備

Dockerfile等はgithubに置いた。
https://github.com/yamamo-to/jetson_nano/tree/master/docker_example

使い方は下記の通り。

```bash:ターミナル
git clone https://github.com/yamamo-to/jetson_nano.git
cd jetson_nano/docker_example
./build.sh
./run.sh
```

build.shの実行でイメージを作成する。1時間程度掛かる。
またrun.shでコンテナを作成する。

# サンプルの実行

コンテナが実行された後、必要なライブラリをインストールしてサンプルを実行する。

```bash:コンテナ内
pip3 install pillow
python3 scripts/resnet50_keras.py data/boss.jpg
```

# 結果

以下に実行結果を貼る。ResNet50がコンテナ内から正しく実行されていることが分かる[^1]。

[^1]: 「[Jetson NanoでDockerを動かす(解決編)](https://qiita.com/yamamo-to/items/ec8aa4a1fe8263b53280)」でも触れているが、これは正しく実行されていない。

![docker_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/3161db48-17e2-143c-0eeb-2d4b41ef663c.png)

# 問題点
<font color="Salmon">
以下に記載されている問題点は「[Jetson NanoでDockerを動かす(解決編)](https://qiita.com/yamamo-to/items/ec8aa4a1fe8263b53280)」で解決済み。
</font>

前の画像では分かりにくいが、CUDAが利用可能なデバイスが見つからないと表示されている。TensorFlowは動いたけれどもCUDAは使われていないのだろうか。

```
failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected
```

GPUの使用状況を確認すると確かにGPUは動作している。
![GPU_log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/05d8b053-1371-511e-6b7d-ad2f5d14f7e9.png)

考えられることの一つは/procを参照していないことだろうか。追って調査する予定だが、そろそろ環境を手間なく分けるためのDockerの導入が、Docker+GPUに関する環境構築そのものが面倒になってきた。

# まとめ

Jetson NanoでDockerを立ち上げ、TensorFlowを使ってResNet50による画像識別を行った。GPUは使われていたようだが、CUDAデバイスが見つからないなど課題点が残る。
Amazon Linux 2でmysql2を使ったRidgepoleを動かすために必要だった奴ら

完全に自分用のメモです。

Amazon Linux 2 AMI (HVM), SSD Volume Type - ami-00d101850e971728d

```sh
sudo amazon-linux-extras install -y ruby2.4
sudo yum install -y gcc redhat-rpm-config mysql mysql-devel ruby-devel
sudo gem install mysql2 --no-ri --no-rdoc
sudo gem install bigdecimal --no-ri --no-rdoc
sudo gem install ridgepole --no-ri --no-rdoc
```
ServiceNowで添付ファイルを管理しているテーブルを確認する

Version: London
# やること
特定のレコードに添付したファイルを
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/8ece435d-a6dc-c1ef-db22-1eb03e545b20.png)
管理しているテーブルを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/8c2c2f44-367a-8d6c-7f44-81d3c2be13f8.png)
# 特定のレコードへのファイルの添付
ファイルを添付するレコードはどんなものでも(Incident, Problem, Change, User, Group...)構いませんがここの手順では差し当たり[Configuration] - [▼ Servers] - [Linux]でCMDB(構成管理)のLinux一覧を表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/8ec908ee-37bb-7347-3f39-6d1b31f716f2.png)
特定のレコードを選択してフォームを表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/eddd8d17-962a-dd2f-0a12-bc0efdc8aec8.png)
画面右上のクリップのアイコンをクリックして
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/75817d26-e8af-24b5-22fa-3737482f1560.png)
ファイルの添付ダイアログを表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/14138844-d408-36ef-2762-9a3b06bc2dad.png)
[Choose File]をクリックして添付するファイルを選択するエクスプローラーを表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/6dbcb652-e336-b68a-fea2-b0b37ba672f9.png)
添付するファイルを選択して
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/8ece435d-a6dc-c1ef-db22-1eb03e545b20.png)
[開く]をクリックするとアップロード処理のプログレスバーが表示されます
<img width="1600" alt="20190612-2059.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/0f523bf3-87e9-c6f3-a412-ad5a4df7d341.png">
アップロードが完了すると添付したファイルのリストが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/efcb7363-25fa-bd2a-3781-0114e3c7ffac.png)
ダイアログの[×]をクリックして閉じるとフォームの画面上部にファイルが追加されたことが確認出来ます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/4c1d7690-c16f-501a-f814-2abf72c0d70d.png)
# 添付ファイルを管理しているテーブルの確認
Filter Navigatorに"sys_attachment.list"と入力するとsys_attachmentテーブルのレコード一覧が表示されます([System Definition] - [Tables]からテーブル一覧を表示してテーブル名で検索しても見つかります)画面上部に今回添付したファイルの一覧が追加されていますTable sys IDカラムにはファイルを添付したレコードのsys IDが表示されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/8c2c2f44-367a-8d6c-7f44-81d3c2be13f8.png)
複数のレコードに添付したファイルを一覧で表示したり検索したり

特定のレコードのsys IDを調べたあとにGUIを使わずにこちらのテーブルにRESTで直接レコードを追加して添付したことにしたり(RESTで追加するときはファイルをBASE64エンコードした値を設定する必要があったような気がします)

使い道は色々ありそうです
システム開発村に宗教は必要か？

# はじめに

単なるポエムです。

宗教にも政治にも教育にも詳しくないです。ポエムでフィクションです。読んでいただいた方には雰囲気だけ伝われば幸いです。

## 私はシステム開発村の役員です

私はシステム開発村の村役場の役員です。
システム開発村に住む人たちが豊かになるように、村の中を回って悩みを聞いたりしています。

村の特産品は珈琲です。特産品をもっと多く作って村を豊かにするために、生活指導をしたり、条例を作成したりしてきました。最近はアニミズム[^1]を元にした経典を作成し、布教をはじめました。村の人たちには物を大切にする習慣がつき、特産品の品質が向上し、生活も豊かになってきていると感じています。

[^1]: 生物・無機物を問わないすべてのものの中に霊魂、もしくは霊が宿っているという考え方。https://ja.m.wikipedia.org/wiki/%E3%82%A2%E3%83%8B%E3%83%9F%E3%82%BA%E3%83%A0

## 昔はシステム開発国の法律を作ってました

昔は、システム開発国の法律を作成していました。国ほどの規模になると、治安維持のために法律が欠かせませんが、経済の発展には直接結び付かないことが多いと思います。(といっても、現代ほど豊かでなかった時代では、法律が非常に重要でした。)

経済発展のためには、生産者に寄り添うことが大事という風潮が高まり、地方分権が進んでいるように感じます。

## システム開発村に宗教は必要か

村の経済は順調です。しかし、最近は他の村から越してきた人とのトラブルが起きています。アニミズム自体には悪い教えは無くても、少し閉鎖的な雰囲気になっているのかもしれません。

システム開発村に宗教は必要だったのでしょうか。

人の出入りも激しくなってきたので、今後に一抹の不安を感じます。経済の発展だけを考えて教育をおろそかにしてしまったかもしれません。

私は、宗教と教育を、明確に区別はできません。
しかし、村でできる教育、国でできる教育に、どう取り組んでいくのかが、私の今後の課題です。

## 教育だけでいいのか？

教育に力を入れて、３年の月日が経ちました。

システム開発村の住人は、教養が身につき、多くの物事に対応できるようになりました。
人の出入りも多い中、閉鎖的な雰囲気も解消されたように感じます。

しかし、経済の発展には課題があります。
教養が身につき多様性を重視した結果、選択と集中が出来ていないのかもしれません。

## システム開発国に戻ったら

そんな中、私は昔法律を作成していたシステム開発国に短期的に戻る機会がありました。

システム開発国では、選択と集中が出来ています。
システム開発国の法律は、より洗練されていました。

しかし、法律の変化に対して、国の雰囲気や経済の変化は少ないと感じざるを得ませんでした。

## 法律でも宗教でも教育でもなく

私は、住人に与える面を重視し過ぎていたのかもしれません。

もしかしたら、コミュニティのような、住人の自発的な活動を促すもの、それを支援するルールが経済の発展には必要だったのではないかと思っています。

私は、またこれから別の土地に行きます。
そこでは、もっと広い視野で色々なものを眺めていきたいと思います。
AWS Summit - クラウド運用管理の最前線

# クラウド運用管理の最前線 ～日米の最新状況から～
AWS Summit Tokyo 2019 1日目のセッションです。
日本と米国双方、運用の課題やその取込を紹介するセッションで、大きな違いは**運用規模**です。

プレゼンのキャプチャー簡単に取れますので、**ライブストリーミング**で視聴できるセッションはOnline視聴をお勧めします。

## 日本の課題
- マネジメントコンソールは誰のもの
  - AWS is IT Toolbox
  - アプリチームとインフラチーム両方使用する

### 従来運用体制
一般的には下記構成になってます。

- サービスマネージャー
  - アプリチーム
  - インフラチーム
  - 運用チーム

### AWSで従来運用体制の課題
- コミュニケーションのコスト(App <-> Infra)
  - App: この設定をお願い
  - Infra: 分かりました、試してみて
  - App: できていません...
  - Infra: すいません...
- インフラチームでAWSを全部管理するのは無理です
  - LambdaのDeploy
  - IAM

### 課題の解決方法
インフラチームの管轄範囲の変更、共通運用に集中する、下記は一部例

- アカウントの払い出す
- ベースネットワーク、セキュリティー
- 監視の運用

## マルチアカウント戦略
システムごと、アカウントを切り離すこと、色々なメリットが得られます。

### マルチアカウント管理のゴール
- 監査可能
- Scalable
- フレキシブル
- 自動化
- カードレール
- セルフサービス

### 共通運用チームの責任範囲
- 請求アカウント
  - 請求集約
  - アカウントの払い出す
- セキュリティー & 監査
  - ログの集約
  - 監査の実施
- 共有サービス
  - 共有ネットワーク(VPC)
  - 共有サービス(AD)

### 責任分離パターン
- DevOps Team
  - 個別運用まで見るように
- App Team & Infra Team
  - 各自責任範囲明確、共通範囲あり
- Research Team
  - 全サービス活用
  - Full Permission
  - Securityの理解が必要

## 米国
圧倒的に規模感が違い、次世代マネジメントの取込が多い。

### コントロール
- 大規模環境では特に重要
- ガードレールの設定(Guardrails setting)
  - AWSが提唱
  - 最初から制限なし
  - CloudTrailでユーザ操作を監視、Guardrailsに抵触したユーザを制限する
- CloudFormation
  - cfn-lint
    - テンプレート検証 
    - セキュリティーリスク検知
  - cfn_nag_scan
    - セキュリティーリスク検知

### コスト最適化
レコメンデーションをどう活用するかが重要、他社の管理ツールも運用視野に入れるべき

- リフト&シフト最適化
  - ライトサイジング
  - 伸縮性向上
  - RI/Spot最適化
- Trusted Advisor
- EC2 Right Sizing
  - 2週間の使用状況データを分析し、提案する
- Cloud Management Tools
  - 他社の管理ツール
- レコメンデーション

## 次世代のクラウドマネジメント

### Machine Learningの有効活用
- Capacity Planning
  - AutoScaling
- 異常検知
  - Kinesis Data Analytics
- ノイズキャンセリング

### 障害に備えるメカニズム
- Resiliency
  - 回復力
  - Design for Failure
- Error Ingestion
  - 意図的に障害を起こす
  - Aurora DB Cluster Fault Injection Queries

### Chaos Engineering
Netflix使用するライブラリ[Chaos Monkey](https://github.com/netflix/chaosmonkey)、OSSで公開中

- Netflix本番運用中
- 検証環境で使用するお客様が多い
【Ruby on Rails】初心者でもメソッドの単体テストがしたい！！

# 前置き
- リクエスト単位でのテストより、メソッドレベルでのテストがやりたい！

Railsで「モジュールのメソッドレベルのテストがやりたいな〜」と思いながら、今まで保留していました。
テストを書きながら実装していこうとしている、この機会に調べてまとめてみました。

# 任意のモジュールを作成 

```ruby:sample_mod.rb
module SampleMod
  def add(a, b)
    a + b
  end
end
```

# /spec 配下にテストファイルを格納

```ruby:sample_mod_spec.rb
require 'rails_helper'

describe SampleMod do
  let(:test_class) { Struct.new(:sample_mod) { include SampleMod } }
  let(:hoge) { test_class.new } # hoge.メソッド名 でモジュール内の各メソッドを呼び出せる

  it 'サンプルテスト' do
    expect(hoge.add(1, 2)).to eq 3
  end
end
```

# テスト実行
## 特定のファイルのみ実行

```bash
bundle exec rspec spec/path/to/sample_mod_spec.rb
```

## タグ付け
タグ付けして特定のテストのみを実行することも出来る

```ruby:sample_mod_spec.rb
require 'rails_helper'

describe SampleMod do
  let(:test_class) { Struct.new(:sample_mod) { include SampleMod } }
  let(:hoge) { test_class.new } 

  it 'サンプルテスト' do
   expect(hoge.add(1, 2)).to eq 3
  end

  it 'タグ1', fruits: :apple do
    expect('a').to eq 'a'
  end

  it 'タグ2', fruits: :orange do
    expect('a').to eq 'a'
  end
end
```

タグ1のみ実行

```bash
 bundle exec rspec --tag fruits:'apple' spec/path/to/sample_mod_spec.rb
```

タグ1、タグ2を実行

```bash
 bundle exec rspec --tag fruits spec/path/to/sample_mod_spec.rb
```

describeの部分にタグ付けしてグループ化することもできる

```ruby:sample_mod_spec.rb
describe SampleMod do
  let(:test_class) { Struct.new(:sample_mod) { include SampleMod } }
  let(:hoge) { test_class.new } 

  describe 'テストだよ' fruits: 'apple' do
    it 'テスト1' do
      expect(hoge.add(1, 2)).to eq 3
    end

    it 'テスト2'
      expect('a').to eq 'a'
    end

    it 'テスト3'
      expect('a').to eq 'a'
    end
  end
end
```

テスト1〜3を全て実行

```bash
bundle exec rspec --tag fruits spec/path/to/sample_mod_spec.rb
```

## タグの複数指定
`--tag` を指定する分だけ繰り返す。いずれかに合致するものがテストされる

```bash
bundle exec rspec --tag fruits --tag bird:crow spec/path/to/sample_mod_spec.rb
```

# 参考文献
- [Rspecで始めるユニットテスト(単体テスト)](http://qs.nndo.jp/unit-test-by-ruby-with-rspec)
- [RSpecで特定のテストケースのみを実行する方法](http://labs.timedia.co.jp/2013/03/rspec.html)
- [一部のテストのみ実行 -タグ付け-](http://samurai.ataglance.jp/rspec_tag/)
- [Rails4のconcernsなmoduleのテストをrspecで書く方法](https://qiita.com/tq_jappy/items/ed56b0f4a20500252461)
AWSソリューションアーキテクト-アソシエイト受験体験談(2019年5月)

2019年5月末にAWSソリューションアーキテクト-アソシエイトに合格したので所感を記載します。

## 目次
1. 受験のメリット
1. 試験概要
1. 勉強方法
1. AWSの構成
1. 主要サービス紹介
1. 試験当日の注意事項

## 受験のメリット
- AWSのサービスについてある程度知ることができる
- AWSのサービスを利用したユースケースについて知ることができる
- AWS認定試験合格者しか手に入らないグッズを購入できる

## 試験概要
下の資格に「クラウドプラクティショナー」、
上の資格に「ソリューションアーキテクト-プロフェッショナル」があります。
アソシエイトはその中間に位置し、アプリケーションの構築・デプロイ面が中心です。
同列の資格として「デベロッパーアソシエイト」などもあり、こちらは開発向けです。

### 受験料
2019年6月時点で15,000円(税別)

## 勉強方法
- 書籍紹介
- Black Belt(公式動画)
- 公式サンプル問題
- 公式模擬試験
- Udemy

### 書籍紹介

自分は以下を使用
![AWS教科書](https://images-na.ssl-images-amazon.com/images/I/51ODtT%2BwepL._SX350_BO1,204,203,200_.jpg)

1周目は熟読してサービス概要を知り、
2周目は問題を解く
3周目は苦手分野を中心に復習  
→とにかく電車移動中などの隙間時間はずっと読む！！！

### Udemy

こちらの講座を受講しました。
手を動かす時間がなかったため、解説と模擬試験を利用しました。
構築の知識を身につけたいので後で再実施します。
https://www.udemy.com/aws-associate/

### Black Belt(公式動画)

正直見る時間が取れませんでしたが、公式で提供してくれているので余裕があれば見てください。  
[AWS Black Belt](https://aws.amazon.com/jp/aws-jp-introduction/)

### 公式サンプル・模擬試験

模擬試験は2000円で受験できます。
ある程度自信がついてからの受験をおススメ！
※間違えた問題を振り返ることができないです
※間違えた分野のパーセンテージは確認できます

公式サンプル問題は少ないですが問題と解答があります。
[公式サンプル問題](https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/)

## AWSの構成 ※重要

以下の用語はしっかりと理解してください。
冗長化やスケーリングする際に分かっていないと問題も解けないし、構築もできません。

![リージョンとAW](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/images/aws_regions.png)
(公式より拝借)

### リージョン(Region)

バージニア北部、東京など、実際のデータセンターがある場所を指します。
リージョンの中に複数のアベイラビリティーゾーン(AZ)が存在します。

■主な利用例(リージョンサービス)

- VPC
- DynamoDB
- Lambda

### アベイラビリティーゾーン(AZ)

リージョン内に複数存在し、単一のAZ内にEC2を立ち上げたり、
複数のAZにEC2を立ち上げて冗長化や負荷分散することができます。
※厳密にはELB使ったりしますが

AZはあくまでも同一リージョン内のため、仮にリージョンがある地域に災害等が発生した場合は
サービスの継続に影響が出る場合があります。
※複数AZが同時に使用できなくなることはそんなに無いと思いますが

■主な利用例(アベイラビリティーゾーンサービス)

- EC2
- RDS

## 主要サービス紹介

## IAM

AWSの各サービスを使用するユーザーやグループの権限を制御するサービス。  

### EC2

仮想サーバーを提供するサービス。
LinuxやWindowsのOSを指定し、仮想サーバーを立ち上げることができる。

### ストレージサービス

- S3
    - Glacier
- EBS
    - ストレージの種類も把握しよう！
- EFS

各ストレージサービスの違いはしっかりと覚えてください。

### データベースサービス
- RDS, Aurora
- Dynamo
- Red Shift
- Elasti Cache

各DBのユースケースをしっかり覚えてください。
特にAuroraとDynamoはAWSの製品なので覚えましょう！

### スケーリング・冗長化
- Auto Scaling
- Cloud Front

スケーリング・冗長化の実現方法については重要です。

## 試験当日の注意事項

★本人確認証

- 運転免許証
- クレジットカード

私の場合は上記の2点を使用しました。
2点必要なので、事前にしっかり確認してください。
運転免許証は目の前のスキャナー？カメラ？にかざし、
クレジットカードは目の前のWEBカメラにかざしました。

★持ち物
本人確認証以外は持ち込み禁止でした。  

★試験中の姿勢
私は平気でしたが、顔を覆ったりすると注意を受けるらしいです。
試験中はカメラで見られているらしく、チャットで指示が来ます。  


以上です。

私はAWSの業務経験は有りませんが、これを期に少しずつ実務でも経験していきたいです。
受験される皆様を応援しています！
なぜアジャイルな計画づくりがうまくいくのか ?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257568/37d61c35-e123-b1ff-7526-6489328aa884.png)

こちらを読んで

## 目的
上手くいく要因を理解するため


## アジャイルな計画づくりの目的
プロダクト開発全体への問いに対する最適な回答を見つけ出すこと

アジャイルな見積もりと計画づくりはプロジェクトの最適解を見つけられるからこそ成功する
全てはプロダクト開発全体への問いに答えるため


## アジャイルな見積もりと計画づくりのための12のガイドライン
1. チーム全体を巻き込む
2. 複数レベルの計画を立てる
3. 規模の見積もりと期間の見積もりに別々の単位を使う
4. 不確実性はフィーチャーか日付の幅のいずれかで表現する
5. 頻繁に計画を見直す
6. 進捗をトラッキングして情報を共有する
7. 学習の大切さを受け入れる
8. フィーチャーは適切な大きさで計画する
9. フィーチャーを優先順位づけする
10. 現実に即した見積もりと計画を立てる
11. ゆとりを残す
12. 複数チームの連携にはフィーチャーが移動する事を先読みして計画する

## 1. チーム全体を巻き込む
要求の優先順位づけを最終的に判断するのはプロダクトオーナー

- チーム全体で行うべきもの
 - 見積もり

- 見積もったストーリーやタスク
  - 1人か2人

チームで共有できる責任が増えれば増えるほど、チームで共有できる成功は大きくなる

## 2. 複数レベルの計画を立てる
大きく3つ

- リリース計画
- イテレーション計画
- 1日の計画

期間も制度も独自の狙いがある

## 3. 規模の見積もりと期間の見積もりに別々の単位を使う
- 規模 
  - ストーリーポイント
- 期間 
  - ベロシティ

## 4. 不確実性はフィーチャーか日付の幅のいずれかで表現する
- リリースプランニング
  - 必ず不確実性を反映させる

- 不確実性
  - 日付の幅で表現する
  - 「~ の間に完了します。」「○イテレーションから△イテレーションの間に完了します。」など。

- フィーチャーは交渉できるが期日は固定されている場合
  - 提供予定のフィーチャーに幅をもたせて不確実性を表現する
  - 「○月○日に完了させるがその時点で確実に約束できるのはこれとこれ。余裕があったらこれもできるがそれ以上は無理」

- 不確実性が高い場合
  - 不確実性に合った計画単位で話す。(リリース、イテレーション、1日)

## 5. 頻繁に計画を見直す
- 新しいイテレーションを始めるタイミング
  - リリース計画を見直すタイミング

- 更新対象
  - 既に古くなっている情報
  - 間違っている見込みが高い仮定

計画の見直しはプロジェクトが最大限の価値を提供できるように進んでいるかどうかを評価するチャンス

## 6. 進捗をトラッキングして情報を共有する
- チームの進捗は一目で理解できるようにする
- 常に公開しておく
- バーンダウンチャートなどがベスト
- 個人ではなく、チームを計測する

## 7. 学習の大切さを受け入れる
- プロジェクトとは、プロダクトにフィーチャーを追加するのと同じくらい新しい知識も生み出す。
  - それを必ず計画に反映する

- 新しいフィーチャーは顧客がなにを求めているのかを学ぶ事で追加できる
- 技術理解、チーム作業における新しい発見から進捗速度や開発の進め方の調整ができる

## 8. フィーチャーは適切な大きさで計画する
- 数イテレーション以内の直近開発予定のもの
  - 小さなユーザーストーリーに分割する

- 一般的なユーザーストーリーのサイズ
  - 1、2 ~ 10日くらい

- プロジェクトの期間とストーリーの大きさ、量のバランス 
  - ストーリーの大きさを計画の対象期間に合わせる

- 計画の対象期間が長い場合
  - ストーリーをエピックとしてくくる(大きな塊)

- 期間が先のストーリーを小さく分割する必要はない

## 9. フィーチャーを優先順位づけする
- フィーチャーの優先順位
  - プロジェクト全体の価値を高められるかが判断基準

- 他の判断基準
  - フィーチャーから得られる知識
  - フィーチャーを開発する事で軽減できるリスク
  - フィーチャーを実装することで重大なリスクを先行して解消できる
  - フィーチャーに取り組むことでプロダクトやプロジェクトのナレッジを深められる事が明白など 

## 10. 現実に即した見積もりと計画を立てる
- 根拠とない実績がほとんどない状態でのベロシティの見積もり
  - ベロシティ見積もりのテクニックを押さえる

- できる限り測定した実績値を根拠とする

## 11. ゆとりを残す
- チームメンバー全員の時間を100%使い切るような計画を立てない
  - 車の密度が100%の高速道路は渋滞で進めない
  - 限界まで時間を使うと進みは遅くなる

## 12. 複数チームの連携にフィーチャーが移動する事を先読みして計画する
- チーム間で依存関係が生じるフィーチャーについては、ある程度先を見越して実装するイテレーションを決めておく
  - 依存関係を計画に組み入れる 

## 書籍情報
Mike Cohn (著), 安井 力  (翻訳), 角谷 信太郎  (翻訳)
アジャイルな見積りと計画づくり ～価値あるソフトウェアを育てる概念と技法
https://amzn.to/30CRfjU

## 雑感
「ゆとりを残す」が印象的
バッファをもたせることは大事だけれど、何も考えずにこれに合わせると、結局はパーキンソンの法則で時間いっぱいになる

逆に決めた時間いっぱいになるのなら、自分の中でのスケジュール期限は少し短めに設定して、それに合わせて本気で進める事が大事そう

セーフティな期限で短く粒度を決めて、締め切り効果で常に取り組むのが良さそう
Tomcatを起動したらPort 8080 required by Tomcat v6.0 Server at localhost is already in use.となったときの対応方法

- 環境
  - Dockerコンテナの環境
  - CentOS Linux release 7.6.1810 (Core)
  - Eclipse2019-03
  - java6
  - Tomcat v6.0

# 事象:EclipseでTomcatを起動したらダイアログが表示されて怒られた

```text:ダイアログのメッセージ
Port 8080 required by Tomcat v6.0 Server at localhost is already in use. The server may already be running in another process, or a system process may be using the port. To start this server you will need to stop the other process or change the port number(s).
```

# 原因:Tomcatがデフォルトで使うポートが既に使われているから
今回はDockerホストからつなぐDockerコンテナのポートで`8080`を使っていた。
この時`ss -t4`を実行するとサービス名は`webcash`だったので最初「？」となった。

## CentOS7ではnetstatが非推奨だったのでssを使って使われているポート番号を調べる
[CentOS 7 では ifconfig、route、arp、netstat が非推奨 - eTuts+ Server Tutorial](https://server.etutsplus.com/centos-7-net-tools-vs-iproute2/)

```bash
# CentOS7ではデフォルトでnetstatはない
$ netstat -n | grep 8080
bash: netstat: command not found

# だからssを使う
$ ss --version
ss utility, iproute2-ss170501

# 本当に使われているか確認する
$ ss -nt4 | grep 8080
ESTAB       0      0             172.19.0.2:8080           123.50.201.40:60003

# 誰が使っているか見てみる
$ cat /etc/services | grep 8080
webcache        8080/tcp        http-alt
webcache        8080/udp        http-alt
```

## 対応:Tomcatのポート番号を変える
1. EclipseのServerビューでTomcatをダブルクリック
2. Portsエリアにある「HTTP/1.1」のポート番号を使われていない番号に変える
"bundle exec rspec" RSpecのテストコードを利用したテストを実行する際にエラーが出てしまう

##$ bundle exec rspecが実行できない

下記コマンドを実行する際に、

```
$ bundle exec rspec
```

```
Book:pictweet_exam2 YUKI$ bundle exec rspec
Unsupported rails environment for compass

An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error:
  class User < ActiveRecord::Base
    # Include default devise modules. Others available are:
    # :confirmable, :lockable, :timeoutable and :omniauthable
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :trackable, :validatable
    has_many :tweets
  
    validates :nickname, presence: true, length: { maximum: 6 }

ActiveRecord::AdapterNotSpecified:
  'test' database is not configured. Available: ["default", "development"]
# ./app/models/user.rb:1:in `<top (required)>'
# ./config/routes.rb:2:in `block in <top (required)>'
# ./config/routes.rb:1:in `<top (required)>'
# ./config/environment.rb:5:in `<top (required)>'
# ./spec/rails_helper.rb:4:in `require'
# ./spec/rails_helper.rb:4:in `<top (required)>'
# ./spec/models/user_spec.rb:1:in `require'
# ./spec/models/user_spec.rb:1:in `<top (required)>'
No examples found.

Finished in 0.00046 seconds (files took 5.97 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples
```

とのエラーが出てしまう。

もちろんgemはしっかり記述、、bundle installはできている

<img width="659" alt="スクリーンショット 2019-06-12 20.15.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432050/4f77a23b-63e2-5a05-4ac5-cca620e6e540.png">

エラーメッセージの中の、

```
'test' database is not configured. Available: ["default", "development"]
```

のが怪しそうと検索、testdatabaseを確認するも、、、

<img width="608" alt="スクリーンショット 2019-06-12 20.17.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432050/272c6741-4f43-8db4-d169-86c751f2b948.png">

ちゃんとある、、

##database.ymlの確認

<img width="689" alt="スクリーンショット 2019-06-12 20.19.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432050/2ef211c2-bd7d-ad47-449e-fd163b3fe0b1.png">

testの記述のインデントが問題でした、、

<img width="680" alt="スクリーンショット 2019-06-12 20.21.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432050/b1c954e3-798f-3168-51c1-ca1584365143.png">

##解決

なんて単純なの、、
でもエラーメッセージにヒントが出てこないので、随分と時間がかかってしまった。

```
Book:pictweet_exam2 YUKI$ bundle exec rspec
Unsupported rails environment for compass

User
  #create
    is invalid without a nickname

Finished in 0.00736 seconds (files took 7.8 seconds to load)
1 example, 0 failures
```
HTTP通信におけるGETメソッドとPOSTメソッドの違いについて

#HTTPとは

HyperText Transfer Protocolの略。
HTMLなどのテキストファイルや、画像、コンテンツ等をやりとりする時のプロトコル（規約。分かりやすく言うと「決まりごと」）のことを意味します。


#HTTPの基本動作
「クライアントからの要求（HTTPリクエスト）」と「サーバーからの応答（HTTPレスポンス）」が基本的な流れになります。
言葉よりも図を見てイメージして頂いた方が分かりやすいと思います。

<img width="776" alt="スクリーンショット 2019-06-12 20.09.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353565/5fe38250-6a9c-0996-7a34-cffb2fbbcffc.png">





# GETメソッドとPOSTメソッドの違い
## GETメソッド
- サーバーからデータを取得する。

- データベースに処理を加えない。
イメージとしてはSQLのSELECTに近いです。

ex. 検索するとき
（ググる時はデータの書き換えではなくデータの取得をしている）

- 閲覧履歴が残る  
GETメソッドを使ってデータを送信する場合は「URL?パラメーター名=値」の形で送信されます。

試しにGETメソッドでフォームを送信してみます。

```
<h1>New User</h1>

<%= form_for @user ,method: :get do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  
  <%= f.label :email %>
  <%= f.email_field :email %>
  
  <%= f.submit '送信' %>
<% end %>

```

nameフォームにtest1
emailフォームにtest1@test.comと入力してみます。


```
https://xxx.com/users?utf8=%E2%9C%93&user%5Bname%5D=test1&user%5Bemail%5D=test1%40test.com&commit=%E9%80%81%E4%BF%A1
```

URLにtest1とtest1@test.com(@は%で表記)が表示されていることが確認できます。

- ブックマーク（URLを登録すること）できる      
「URL?パラメーター名=値」の形でURLをブックマークすることができるので、そのURLで検索した時の結果を表示させることができます。

## POSTメソッド 
- サーバーにデータを登録する

- データベースに処理を加える。
イメージとしてはSQLのINSERTやUPDATEに近いです。

- 閲覧履歴が残らない
POSTメソッドで送る場合はメッセージボディに情報が含まれていてURLには情報が表示されない。

POSTメソッドで先ほどGETメソッドで送ったのと同じデータを送信してみます。

```
https://xxx.com/users/5
```

URLに入力したデータではなくパスが表示されていることが確認できます。

- ブックマークできない  
パラメーターがbodyに含まれており、URLに明記されていないためブックマークに登録することができない。



# 最後に
誤っている箇所や追記した方が良い点等ございましたら
編集リクエストやコメントの方でご指摘していただけると幸いです。

参考文献
[イラスト図解式 この一冊で全部わかるWeb技術の基本](https://www.amazon.co.jp/%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E5%9B%B3%E8%A7%A3%E5%BC%8F-%E3%81%93%E3%81%AE%E4%B8%80%E5%86%8A%E3%81%A7%E5%85%A8%E9%83%A8%E3%82%8F%E3%81%8B%E3%82%8BWeb%E6%8A%80%E8%A1%93%E3%81%AE%E5%9F%BA%E6%9C%AC-%E5%B0%8F%E6%9E%97-%E6%81%AD%E5%B9%B3/dp/4797388811/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=F0GDZ0PRNM4NYNGYF2EW)
【npm】brewでパッケージをインストールするときにnode_modules関係でPermission deniedされた時の対処法

#概要
Homebrewでパッケージをインストールする時に，node_modules関係でPermission deniedされて悲しい思いをしている人に向けた記事

#現象
node_modules以下のファイルを変更する権限が無いと言われて，パッケージのインストールができない．
<img width="1552" alt="スクリーンショット 2019-06-12 20.00.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390000/af4c55d4-5608-c860-4a8f-987f008f6c6c.png">

#対策
Permission deniedされたnode_modulesの権限を775(所有者とグループに読み込み+書き込み+実行権限を付与)に変更する．

```shell
chmod 775 /usr/local/lib/node_modules
```

#Happy Hacking :sunglasses: !
Dockerで作った環境のMavenで/home/path/.m2/repository/x.pom.part.lock (Permission denied)となった時の対応方法

- 環境
    - Dockerホスト側:AWSのEC2インスタンスにあるCentOS
    - Dockerコンテナ
        - CentOS Linux release 7.6.1810 (Core)
        - java 6
        - Eclipse 2019-03

# 事象 : EclipseでMavenプロジェクトの更新をしたら怒られた
```bash
Failed to read artifact descriptor for jsonic:jsonic:jar:1.2.0

org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for jsonic:jsonic:jar:1.2.0
    at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:276)
# 省略
Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Could not transfer artifact jsonic:jsonic:pom:1.2.0 from/to local (file:/home/ponsuke/path/to/../repo): /home/ponsuke/.m2/repository/jsonic/jsonic/1.2.0/jsonic-1.2.0.pom.part.lock (Permission denied)
    at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:422)
# 省略
    ... 36 more
Caused by: org.eclipse.aether.transfer.ArtifactTransferException: Could not transfer artifact jsonic:jsonic:pom:1.2.0 from/to local (file:/home/ponsuke/path/to/../repo): /home/ponsuke/.m2/repository/jsonic/jsonic/1.2.0/jsonic-1.2.0.pom.part.lock (Permission denied)
    at org.eclipse.aether.connector.basic.ArtifactTransportListener.transferFailed(ArtifactTransportListener.java:52)
# 省略
    ... 39 more
Caused by: java.io.FileNotFoundException: /home/ponsuke/.m2/repository/jsonic/jsonic/1.2.0/jsonic-1.2.0.pom.part.lock (Permission denied)
    at java.io.RandomAccessFile.open0(Native Method)
# 省略
    ... 44 more
```

## 原因 : `.m2`ディレクトリの持ち主が自分じゃないから
Dockerで`.m2`ディレクトリを作成したので持ち主がrootさんになっている

```bash
$ sudo ls -la .m2/repository/
total 4
drwxr-xr-x. 1 root root 4096  6月 11 16:00 .
drwxr-xr-x. 1 root root   24  6月 11 16:00 ..
drwxrwxr-x. 1 root root   19  6月 11 15:38 antlr
drwxrwxr-x. 1 root root   25  6月 11 15:38 aopalliance
drwxrwxr-x. 1 root root   39  6月 11 15:38 args4j
drwxrwxr-x. 1 root root   70  6月 11 15:38 asm
# 省略
```

### 対応方法 : 持ち主を自分に変更する
1.. 持ち主を自分に変更する

```bash
$ sudo chown -R ponsuke .m2/

$ sudo ls -la .m2/repository/
total 4
drwxr-xr-x. 1 ponsuke root 4096  6月 11 16:00 .
drwxr-xr-x. 1 ponsuke root   24  6月 11 16:00 ..
drwxrwxr-x. 1 ponsuke root   19  6月 11 15:38 antlr
drwxrwxr-x. 1 ponsuke root   25  6月 11 15:38 aopalliance
drwxrwxr-x. 1 ponsuke root   39  6月 11 15:38 args4j
drwxrwxr-x. 1 ponsuke root   70  6月 11 15:38 asm
# 省略
```

2..再びEclipseでMavenプロジェクトの更新をする

## ほかの原因
[プロジェクトをMavenでビルドできない時の対応方法 - Qiita](https://qiita.com/ponsuke0531/items/bfa1998d4ac24788529d)
Burp Suiteでのセキュリティテスト[傍聴編]

実際に通信内容を可視化し、通信内容の確認までをこの記事で行なっていきます。
Proxyタブを開いてください。
Optionsタブを開き 傍受する　プロキシアドレスを確認してください。
前回プロキシの設定で行ったlocalhost(127.0.0.1) Port 8080であることを確認してください。

<img width="683" alt="burp0620-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/10d8d3a0-bd40-eef6-d3fe-4b9994dc613f.png">


次にInterceptタブを開きます。
Intercept is OFFとなっているのでこれをクリックしONにします。
<img width="467" alt="burp0612-c.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/05d256bd-736a-2a22-eaf1-b3ee89b5d272.png">




Webブラウザを操作し適当なWebサイトにアクセスしてください。


ページがロード中のまま　ブラウザがフリーズしたような状態になります。
BurpSuiteの画面をみると　Intercepの下のRawタブの中に大量の文字が入ってることがわかります。
これがBurpSuiteがキャッチした通信内容です。

<img width="699" alt="burp0612.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/9b7a2d20-3999-e49d-9c0d-eaa81a92039f.png">


Forwardを押すとそのままの状態で通信を進めます。（Dropは通信の遮断を行います）
このとき、Rawはテキストフォームになっており内容を書き換えることもできます。（変えた状態でForwardすること通信内容の改ざんができます）



次に通信内容をみてみたいと思います。
データベース管理ツールであるPhpMyAdminのログイン時を入力した値をBurpでみてみたいとおもいます。
便宜上HTTP通信で行います。

ユーザー名にqiita , パスワードにabcde と入力します。
<img width="244" alt="burp14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/f79fe069-0800-6cec-3047-f6fbd3138f1a.png">

そしてこちらがBurpSuiteの傍受画面です。
入力フォームの内容がキャッチされていることが確認できます。

<img width="646" alt="burp0612-d.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/d1587a73-b7a5-00ae-8f44-407a1e106400.png">
誕生日から生後[Y]歳[D]日の表示に変換するクラスを作った

タイトルのままです。

普段Laravelを使っていて、最近のバージョンではデフォルトでCarbonが入っているので、`diffForHumans()`を使うとある程度時間差を見やすく変換してくれますが、今回具体的に年齢と日数を取得する必要があったのでクラスを作ってみました。

- ◯年◯ヶ月◯日
- ◯◯◯日

とかなら比較的簡単だったけど、年と日数に分ける必要があったのでひと手間。
レアケースなので、再利用性は低いと思いますが、折角なので上げておきます。

# 仕様

誕生日と現在日から、生後◯歳と◯日を出力。うるう日もカウント。

```text
誕生日 '2015-02-12'
現在日 '2016-03-12'
出力 '生後1歳と29日'
```


# クラス

```php

class ConvertBirthday
{
    private $birthday;

    public function __construct($birthday)
    {
        $this->birthday = new DateTime($birthday);
    }

    public function yearDays($today_param = null)
    {
        $today = new DateTime($today_param);
        if(!$today){
            return "";
        }

        //現在の歳を計算
        $years = $this->calcYears($today);

        //最後の誕生日から今日までの日数を計算
        $days = $this->calcDays($today);

        //文字列に整える
        $year_days = $this->genString($years, $days);

        return $year_days;
    }

    private function calcYears($today)
    {
        $years = $today->diff($this->birthday)->y;
        return $years;
    }

    private function calcDays($today)
    {
        $temp_birthday = $this->birthday->format(date('Y')."-m-d");
        $temp_today = $today->format(date('Y')."-m-d");

        //誕生日が1/1～今日の間の場合、今年の誕生日を取得
        if($temp_birthday <= $temp_today){
            $last_birthday_year = $today->format("Y");
        }
        //誕生日が明日～12/31の間の場合、昨年の誕生日を取得
        else {
            $last_birthday_year = $today->format("Y") - 1;
        }
        $last_birthday = new DateTime($this->birthday->format( $last_birthday_year."-m-d" ));

        $days = $last_birthday->diff($today)->format('%a');

        return $days;
    }

    private function genString($years, $days)
    {
        $year_str = "";
        if(!empty($years)){
            $year_str = $years."歳";
        }

        $day_str = "0日";
        if(!empty($days)){
            $day_str = $days."日";
        }

        $separater = "";
        if(!empty($year_str)){
            $separater = "と";
        }

        return "生後{$year_str}{$separater}{$day_str}";
    }

}
```

# 使い方

```php
$birthday = "2010-01-01";
$convert = new ConvertBirthday($birthday);

//現在日までの日数
echo $convert->yearDays();

//指定した日までの日数
$date = '2019-06-12';
echo $convert->yearDays($date); //生後9歳と162日

```

# 作ってみて

オブジェクト指向がまだいまいち掴みきれなくて、これでいいのかよくわかってません。
バグもあるかもしれません。
C# error: A field initializer cannot reference the nonstatic field, method, or property

# 解消方法
今回でたエラー文は以下のもの,  
**A field initializer cannot reference the nonstatic field, method, or property \`原因部分\`**

Google先生に翻訳していただくと,  
`フィールド初期化子は、非静的フィールド、メソッド、またはプロパティを参照できません`
との意味だそうです.
エラーがでたコードはこちらになります, 

```c#:エラーコード

using System;

public class Product
{
	public int Code { get; private set; }
	public string Name { get; private set; }
	public int Price { get; private set; }

	public Product(int code, string name, int price)
	{
		this.Code = code;
		this.Name = name;
		this.Price = price;
	}

	public int GetTax()
	{
		return (int)(Price * 0.08);
	}

	public int GetPriceIncludingTax()
	{
		return Price + GetTax();
	}

	Product karinto = new Product(123, "かりんとう", 180);
	Product daihuku = new Product(235, "大福餅", 160);
	int karintoTax = karinto.GetTax();
	int daihukuTax = daihuku.GetTax();
	static void Main(string[] args)
	{
		//code
	}
}
```

コンパイル結果は以下のものです,

```言語:結果
$ mcs Chapter1.cs
Chapter1.cs(28,19): error CS0236: A field initializer cannot reference the nonstatic field, method, or property `Product.karinto'
Chapter1.cs(29,19): error CS0236: A field initializer cannot reference the nonstatic field, method, or property `Product.daihuku'
Compilation failed: 2 error(s), 0 warnings
```

エラー文からわかる通り, `Product.karinto` と `Product.daihuku`に問題があるようです.
では, どのように解決するのか.
karinto と daihuku の二つの変数を**static**にするだけです.
以下は, 修正コードです.

```c#:修正コード
using System;

public class Product
{
	public int Code { get; private set; }
	public string Name { get; private set; }
	public int Price { get; private set; }

	public Product(int code, string name, int price)
	{
		this.Code = code;
		this.Name = name;
		this.Price = price;
	}

	public int GetTax()
	{
		return (int)(Price * 0.08);
	}

	public int GetPriceIncludingTax()
	{
		return Price + GetTax();
	}

	//ここを変更した↓
	static Product karinto = new Product(123, "かりんとう", 180);
	static Product daihuku = new Product(235, "大福餅", 160);
	int karintoTax = karinto.GetTax();
	int daihukuTax = daihuku.GetTax();
	static void Main(string[] args)
	{
		//code
	}
}
```

すると, 先ほどまでのエラー文はなくなります.
staticフィールド内で宣言する場合はそもそもstatic演算子は不必要になります.
以下に例を示します.

```c#:例
using System;

namespace SampleApp
{
	class Product
	{
		public int Code { get; private set; }
		public string Name { get; private set; }
		public int Price { get; private set; }

		public Product(int code, string name, int price)
		{
			this.Code = code;
			this.Name = name;
			this.Price = price;
		}

		public int GetTax()
		{
			return (int)(Price * 0.08);
		}

		public int GetPriceIncludingTax()
		{
			return Price + GetTax();
		}
	}
	class Program
	{
		static void Main(string[] args)
		{
			Product karinto = new Product(123, "かりんとう", 180);
			int taxIncluded = karinto.GetPriceIncludingTax();
			Console.WriteLine(taxIncluded);
		}
	}
}
```

このコードでもエラーは出ません.

# 最後に
未来の自分用に書いたので雑だったら申し訳ありません.

# 参考文献
[実戦で役立つ C#プログラミングのイディオム/定石&パターン](http://gihyo.jp/book/2017/978-4-7741-8758-7)　(最終閲覧:2019/6/11)
[万年素人からGeekへの道 ](http://d.hatena.ne.jp/shinriyo/20111110)　(最終閲覧:2019/6/11)
【Laravel5.8】bladeを使ってCSVを作れなかった

いやースマブラに勇者参戦ですよ、これは楽しみすぎますね。
さらにゼルダ続編も出るとか、また寝る時間がなくなってしまいますね。


さて、それとは全く関係ないのですが、LaravelでCSVダウンロードしたいという要件があったのですよ。
目的は達成したのですが、解決は特にしていません。

単に1テーブルとか、1対1のテーブルを結合して出力するだけであれば、withで拾ってコントローラあたりで適当に整形するのが楽だし早いでしょう。
しかし世の中そう単純にはいかないもので、1対多のテーブルを複数まとめてひとつのCSVに出力しろだとか、そもそも関係ないテーブルを並べてひとつのCSVに出力しろだとか言われるわけです。

そんなときはどうするのが最も適切でしょうか。ちゃぶ台を返す？

```php:fooController.php
	$csv = [];

	$csv[] = ['テーブル1'];
	foreach($model1 as $v){
		$csv[] = [$v->id, $v->data1, $v->data2];
	}

	$csv[] = ['テーブル2'];
	foreach($model2 as $v){
		$csv[] = [$v->id, $v->data1, $v->data2, $v->data3];
	}
```

コントローラに延々こんなことを書いていく？
なんか嫌なのでどこか外に出したい。

ならテンプレートでいいんじゃないか？
bladeの機能とか使えるしな。

ということで持っていってみたわけですが、実際普通にテンプレート書くのとほとんど同じなので、むしろタグがないだけ簡単です。

```php:csv.blade.php
テーブル1
@foreach ($model1 as $v)
{{ $v->id }},{{ $v->data1 }},{{ $v->data2 }}
@endforeach
テーブル2
@foreach ($model2 as $v)
{{ $v->id }},{{ $v->data1 }},{{ $v->data2 }},{{ $v->data3 }}
@endforeach
```

やったぜ。

…これCSVエスケープできてないやん。
ダメやんプレイやん。

テンプレート上で各出力項目にCSVエスケープ関数を通さないといけないわけですが、実はPHPには文字列に素で使えるCSVエスケープ関数がありません。
[fputcsv](https://www.php.net/manual/ja/function.fputcsv.php)も[SplFileObject](https://www.php.net/manual/ja/class.splfileobject.php)も、リソースやオブジェクトに対してどうにかするものであって、`{!! csvspecialchars($v->id) !!}`みたいなことはできません。

まあ探せばユーザサイドのCSVエスケープ実装なんていくらでも出てくるので、[Blade::directive](https://readouble.com/laravel/5.8/ja/blade.html#extending-blade)に突っ込んでテンプレから呼べばいいといえばいいのですが、どうにも美しくない気がして微妙なところ。
何より`Blade::directive`が、[create_function](https://www.php.net/manual/ja/function.create-function.php)ばりに実装を文字列で渡さないといけないのが生理的に受け付けない。
関数返すようにできないんですかね？

ていうか、調べてみたらディレクティブって[めっちゃevalしてる](https://github.com/laravel/framework/blob/5.8/src/Illuminate/View/Compilers/Concerns/CompilesConditionals.php)んですね。
特に調べたわけじゃないけど、なんとなくちょっと怖くない？


ということで、結局どこに持っていくのが一番適切なんだろう。

なお、冒頭のやつは最終的にコントローラにだばだば書いた。
【WordPress】ハッキング被害に遭った「他人の」サイトを見つけた時の連絡先

WordPress で、「自分の」サイトが、改ざんなどのハッキング（クラッキング）[^1]被害に遭った際の対処法を説明しているサイトは、[こんな検索ワード](https://www.google.co.jp/search?q=WordPress+%E3%82%A6%E3%82%A4%E3%83%AB%E3%82%B9&ie=utf-8&newwindow=1) であっても、簡単に見つかります。[^2]

[^1]: WordPress の利用者層を考慮して、タイトルにはあえて「ハッキング」という表現を使用しています。

[^2]: さすがは Google 大先生！と言いたいところですが、それだけ被害者が多いという事でもあります。

しかし、もし「他人の」サイトを見つけた場合は、どこに連絡するのが良いのでしょうか。初心者の方向けに、まとめてみました。

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

## 連絡先の候補

1. ドメイン管理元
2. サーバー管理元
3. ~~サイト運営者~~
4. ~~ドメイン所有者~~

オススメは 2 です。1 は、予備の候補という扱いです。

3 と 4 は、連絡手段があったとしても、時間の無駄になってしまう事も多いので、候補から除外します。

- 放置している。
- 情報を更新してない・設定ミス・改ざん被害などにより、連絡手段が機能していない。
- 連絡が取れても、該当者のスキルの問題で対応できない、時間がかかる。
- 「個」ではなく、根元部分の「管理組織」に連絡した方が良い。

などが理由です。

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

## 【1】連絡先候補を簡単に調べる

ウェブブラウザからでも簡単に調べられますので、`whois` のようなコマンドが分からない方でも大丈夫です。

[whois ツール](https://www.google.co.jp/search?q=whois+%E3%83%84%E3%83%BC%E3%83%AB&ie=utf-8&newwindow=1) でググったところ、得られる情報量では↓が優れていると感じました。

- https://whois.domaintools.com/

このサイトを利用して、実際に、改ざん被害を受けているドメイン `○e○ry○k○.com`（一部伏せ字）を[^3]、検索窓に入力してみる事にします。

[^3]: この記事を書くに先立ち、私が管理するサーバーのログを元に見つけました。もし心当たりがあるようでしたら、改ざん被害のご対応を…。

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

## 【2】見る箇所

訳の分からない英語の情報が出てくるかもしれませんが、見るところを 2 箇所だけに絞ります。

1. Registrar
    - ドメイン管理元です[^4]
2. ASN
    - サーバー管理元です[^5]

[^4]: 日本語では「レジストラ」と呼ばれます。興味がある方はググってみてください。

[^5]: **A**utonomous **S**ystem **N**umber の略です。日本語では「AS 番号」とも呼ばれます。興味がある方はググってみてください。

※`IP Address` が、検索したドメインが割り当てられている「サーバーの IP アドレス」です。

※↑の文章の意味が理解できる方は、後々の連絡のために控えておくと良いですが、文章の意味が理解できない方は、とりあえず無視で良いです。

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

## 【3】組織名が書かれていないか確認

1. Registrar
    - 「Onamae.com（お名前.com）」「VALUE-DOMAIN（バリュードメイン）」など
2. ASN
    - 「SAKURA（さくらインターネット）」「KAGOYA（カゴヤ）」など
    - 見覚えがなくとも、日本の国旗が付いていたら日本の組織である可能性が高いですので、ASN に書かれている情報でググれば、何か分かるかもしれません。

今回は、ASN に `SAKURA Internet Inc.` と出てきましたので、「さくらインターネット」が管理しているサーバーで動いている事が分かりました。

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

### 【3-1】組織名が分からない時は

もし、組織名が分からない時は、`abuse` という単語がどこかに書かれていないか、探してみてください。

迷惑行為や不正行為などの、連絡受けつけ窓口として使われる単語です。

※`abuse@example.com` のようにメールアドレスが書かれていたら、直接メールで連絡を取ってみるのも良いですが、相手が日本語を読めない可能性もある点には注意してください。

`@` 以降のドメイン名（上記なら `example.com`）で、【1】に戻って検索すると、何か分かる事があるかもしれません。

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

## 【4】「abuse」を付けてググる

`abuse` の意味は、【3-1】の通りです。

Google 検索は賢いので、この単語を使用すると、かなり的確に連絡先を見つけてくれます。

[さくらインターネット abuse](https://www.google.co.jp/search?q=%E3%81%95%E3%81%8F%E3%82%89%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%8D%E3%83%83%E3%83%88+abuse&ie=utf-8&newwindow=1) でググると、すぐに連絡先が見つかりました。

- https://secure.sakura.ad.jp/abuse/form/select

あとは、案内に従いながら、内容を入力し送信すれば終わりです。

もし余裕があれば、他の連絡先候補 1,3,4 にも連絡すると良いでしょう。

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

WordPress の利用者層を考慮して、かなり初心者よりの内容になっています。

専門知識のある方からすると、「その表現はおかしいのでは？」と感じる部分があるかもしれませんが、説明を簡略化するための意図的なものですので、ご了承ください。
gem install cocoapodsしたのにcocoapodsがない

MacBook Proに買い換えて、いろいろセットアップしている中でcocoapodsを入れようと思ったんですが、podコマンドがないと言われ続けてしまってました。

```
$ sudo gem install cocoapods
$ pod setup
bash: pod: command not found
```
原因の特定には至っていないのですが、同じように困っている人の参考になればと思います。

## 前提条件
* MacOS Mojave 10.14.5
* rbenvを使ってる （version: ruby 2.6.3）

## 解決方法
以下のコマンドで解決できました。

```
sudo gem install -n /usr/local/bin cocoapods
```
普段rubyを使うことがあまりないので、gem installの引数については調べていませんが、もし同じように困っていたら、試してみてください。
merge --squashで適度にまとめたコミットを作成する


こんにちは、@na4daです。


作業用に切ったブランチで作業している時に、「ここで細かくコミットすると粒度が小さすぎるので、ある程度纏めてコミットしたい」という状況が時々あると思います。今回は、そういった場合に`merge --squash`を用いて、複数のコミットをまとめてコミットする手順をまとめた忘備録的な内容です。

## 手順

リモートからcheckoutしてきた(もしくはローカルで作成した)ブランチ(例：`foo`)から、さらにブランチを切り、チェックアウトします。ブランチ名は`foo-dev`とします。この`foo-dev`ブランチはリモートへpushはせず、ローカルだけで用います。

```bash
git checkout -b foo-dev
```

その後、`foo-dev`ブランチでコミットします。後で一つにまとまるため、粒度が小さくなっても気にせずコミットしていきます。

ある程度切りの良いところまでコミットができたら、`foo`ブランチに移動し、`merge --squash`を行います。

```bash
git checkout foo
git merge --squash foo-dev
```

すると、ブランチを分けた時点から`foo-dev`ブランチの最新コミットまでの差分が一つにまとまり、`foo`ブランチにマージされます。

その後、`foo`ブランチのコミットを行います。

```bash
git commit
```

squashを行なっているため、`git commit`でエディタを開くと、`foo-dev`ブランチでコミットした情報が付加されていることが確認できます。そこで、コミットメッセージの追記とともに、あとで確認できるよう`foo-dev`ブランチで行なったコミットログの今回分を残して編集を終えます。

その後、`foo`ブランチのコミットをリモートへpushします。

```bash
git checkout foo
git push origin foo
```

`foo`ブランチにsquashしたコミットを行なった後は`foo-dev`ブランチに戻り、`foo`ブランチのコミットをmergeします。

これは、次回`merge --squash`を行う際に、前回のcommitと同一の箇所を編集することで発生するconflictを防ぐために行います。ちなみに、この手順のタイミングとしては、リモートにpushする前と後、どちらでも構いません。

```bash
git checkout foo-dev
git merge foo
```

ここまでの一連の流れをシーケンス図で表すと以下のようになります。

<img width="600" alt="スクリーンショット 2019-06-11 16.35.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323578/4efde50c-aa83-9999-1088-8539b96e7dd0.png">

この図のように、必ず`foo-bar`を介さずに直接`foo`ブランチにコミットし、pushすることもあると思います。その際も、`foo-dev`にそのコミットを反映させることでconflictを防ぐことができます。

また、一つにまとめたコミットを`foo`ブランチから`git log`で確認すると、以下のような構成になっています(内容は一例です)。

```
commit f333c7c62f0a949014346c43fa9dd387367e9056
Author: test user <test@example.jp>
Date:   Fri Jun 7 17:13:43 2019 +0900

    Fix config files

    Squashed commit of the following:

    commit 1638a43820192c9a1e73ba630559a37fd38f3832
    Author: test user <test@example.jp>
    Date:   Fri Jun 7 17:12:37 2019 +0900

        Fix foo-config.txt

    commit 5abe5741b8ab5cf87283b3ff400002015a743855
    Author: test user <test@example.jp>
    Date:   Fri Jun 7 16:34:48 2019 +0900

        Fix bar-config.txt

    ...

    commit f1ec251f4eee1c42bce0abaed60da3195ebb2df2
    Merge: d53b4ec e1e4093
    Author: test user <test@example.jp>
    Date:   Fri Jun 7 15:34:28 2019 +0900

        Merge branch 'foo' into foo-dev
```

少し手順が多いですが、以上の方法で複数のコミットをまとめて一つのコミットとしてpushすることができます。

### 終わりに

例えば、masterブランチの変更を反映させるために`foo`に`git merge master`を行なった場合は、`foo-dev`にも同じような操作(`git merge foo`)が必要になるため、多少作業量が増えてしまうなどの欠点はあります(confilictが発生する場合は特に)。

個人で作業しているのであればともかく、チームで作業ブランチを共有している場合は他の人が混乱しないように、そして、なるべく内容がまとまっている範囲内でこの方法を使うと良いかなと思います。

少しでも参考になれば幸いです。

## 参考

- [git merge --squash でブランチでの変更を1コミットにまとめてマージ](https://qiita.com/ttskch/items/3f9b798fe3fb5e129489)
メールの開封を確認する

# メールの開封を確認する
結論から言うと、メールの開封確認は原理上できません。
SMTPの仕様上、自分から分かるのは自分の送りつけたSMTPサーバーの時点で問題が起こっていないかまでであり、
その先の問題についてはきちんとバウンスメールを送ってくれるかどうかにかかっています。
よって、すべてのメールの到達確認や開封確認を正規の手法で行うことはできません。

## それでも確認したい場合の方法
とはいえ、メールの開封確認の需要は昔からあったので、様々な方法が考案されてきました。

### 開封確認ヘッダ
メールヘッダに ``Disposition-Notification-To``を足すことで、
受信側に開封確認通知を要求します。

ただ、スパムメール等で多用されたため、
現在はこれに対して真面目に送り返してくれるメーラーやWebメールはほとんどありません。

### HTMLメールで<img>タグを仕込む
HTMLメールを利用して、<img>タグのsrc部分に開封確認を行うサーバーのurlとメールを識別するパラメータを仕込みます。
画像自体はごくごく小さい人の目では確認できないものを利用するのが通常です。
かなり幅広く用いられている方法です。

ただし、そもそも画像を表示しない設定にしている場合には対応できませんし、
セキュリティ的な理由から外部アクセスを拒否している場合もあります。
ここで検知できなかったからと言って開封していないとは言い切れないのがつらいところです。

### メール文中のリンクに仕込む
メール本文中のリンクをクリックした際に記録用のサーバーを経由させることで、
ユーザーが開封したことを確認する方式です。
SendGridなどの一部のメール系サービスで利用できます。

ユーザーに能動的にリンクをクリックしてもらうため、セキュリティ上の問題で弾かれることはありません。

しかしながら、当然メールを開いたら当然リンクをクリックしてくれるような類のメールではないと意味がありません。
(資料をWeb上においてリンクだけメールで送る場合など)


## まとめ
* 仕様上、メールの100%の開封確認はできない
* 開封したことの検知だけはある程度可能
* 100%の受信確認が必要な場合はメール以外を利用することも検討するべき