OmniAICreator/Qiita-1.07M huggingface dataset:
「SFDC Expiring Certificate Notification/証明書の有効期限のお知らせ」とは何なのか？

:::note info
**2023/11/09追記**
この件についての公式ヘルプができたようです。
[エラー「Salesforce 組織の 1 つ以上の証明書の有効期限が近づいています」](https://help.salesforce.com/s/articleView?id=000385781&type=1)
:::

Salesforceの組織をたくさん持っていると以下のようなメールがよく届くと思います。この通知なんなのか知ってますか？
※ちゃんと証明書を作成した覚えのある方はこの記事の対象者ではありません。

英語

>件名: SFDC Expiring Certificate Notification
>
>本文:
>You have one or more certificates in your Salesforce org <組織名> <組織ID> that will expire soon. Review the list below and visit Certificate and Key Management from Setup to make an update.
>
>   - <証明書名>, Self-Signed, expires on <日付>. Warning: This certificate will expire in <日数> day(s).

日本語

>件名: 証明書の有効期限のお知らせ
>
>本文:
>Salesforce 組織 <組織名> <組織ID> の 1 つ以上の証明書の有効期限が近づいています。下記のリストを確認して、[設定] から [証明書と鍵の管理] にアクセスし、更新を行ってください。
>
>   - <証明書名>、自己署名 は、<日付> に有効期限が切れます。警告: この証明書は、あと <日数> 日で有効期限が切れます。

# ヘルプには何と書いてあるか？

ヘルプには以下の説明があります。

[証明書の有効期限についての通知「Salesforce 組織の 1 つ以上の証明書の有効期限が近づいています。」](https://help.salesforce.com/articleView?siteLang=ja&id=000329338&type=1&mode=1)

>自己署名証明書は、シングルサインオン設定 ([証明書の署名要求] または [SAML アサーション検証] 項目) または外部サイトへのコールアウト (クライアント認証のため) によく使用されます。
>（中略）
>Salesforce の ID プロバイダとしての機能が有効になっているため、自己署名証明書が自動的に作成された可能性があります。

しかし、これを読んでも、「**自己署名証明書なんて作ってないよ**」「**IDプロバイダも有効にしてないし**」「**昔はこんなメール届いたことないのにな**」と思ったのは僕だけではないはず。

# 身に覚えのない人に何が起きているのか？

Salesforceの[私のドメイン](https://help.salesforce.com/articleView?id=domain_name_overview.htm&type=5)（My Domain）のヘルプに以下の記述がありました。

>[私のドメイン] を使用してサブドメインを作成すると、Salesforce は ID プロバイダとして有効になります。

犯人は**私のドメインの有効化**でした。私のドメインを有効化するとIDプロバイダが有効化されて、自己署名証明書が自動的に作成されます。

>[私のドメイン] には、次のような多くの Salesforce 機能を使用する必要があります。
>
>（中略）
> ・ Lightning コンポーネントタブ、Lightning ページ、Lightning アプリケーションビルダー、またはスタンドアロンアプリケーションの Lightning コンポーネント

日本語訳がおかしい[^1]ですが、要は**Lightningコンポーネントを使うには私のドメインの有効化が必要**ってことです。つまり、今はほぼみんな私のドメインを有効化しますよね。なので、Lightningコンポーネント（Aura含む）登場以降、開発組織でもみんな私のドメインを有効化したので、めっちゃこのメール来るようになったということのようです。

[^1]: 元の英文はこうです。My Domain is required to use many Salesforce features, including:

# 結局どうすれば？

結局どうすればよいかと言うと、SalesforceでSSOやAPIの代理認証とかの機能を使っていない人は、**IDプロバイダの機能を無効にする**か、**通知を無視すれば**よいと思います。
dataGridViewのチェックボックス型セルの値は、次に他のセルを選択した時に確定される。

dataGridViewのチェックボックス型セルをクリックして、trueになった瞬間(チェックマークが表示された瞬間)に他の動作を起こしたかったが、どうしても実装できなかった。

ネットで教えてもらったのは…
「チェックマークが表示されていも内部の値は変らない、次のセルを選択した際にそれが確定される。チェックマークが表示/非表示された瞬間に値(true/false)を確定させるには、DataGridView.CommitEdit を呼ぶ必要がある」ということだった。
あのチェックマークは見えてるだけなんや～
そんなん判らんでぇ…(TT)

サンプルは以下の通り。

```ruby:C#
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    // 0番目がチェックボックス型の列として、これだけを処理対象とする
    if (dataGridView1.CurrentCell.ColumnIndex != 0) return;

    // 値を確定する!!!
    dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    //ここに、すぐさま起こしたいコードを書く
}



```
【CSS】CSS で作ったハンコのようなもの

世が令和になっても歴史は繰り返されます。
[[CSS] CSSで作る印影（印鑑、ハンコ、判子）っぽいもの](https://qiita.com/sengoku/items/0ba632a146a706f01633) と同じような経緯から CSS ハンコを作成することになりました。

やはり要望は多くはないと思いますが上記記事とは別パターンでの CSS ハンコの作成方法ということで記しておきます。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="ari3der" data-slug-hash="eYNyrEd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="eYNyrEd">
  <span>See the Pen <a href="https://codepen.io/ari3der/pen/eYNyrEd">
  eYNyrEd</a> by ari3der (<a href="https://codepen.io/ari3der">@ari3der</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

特に特殊なことはしていないと思いますがせめてハンコの大きさに応じて font-size がいい感じになるようにだけ調整しました。

```html:hanko.html
<div class="hanko">
  <span>営業1課</span>
  <hr noshade>
  <span>2020/03/01</span>
  <hr noshade>
  <span>たかし</span>
</div>
```

```scss:hanko.scss
$hanko-size: 100px;
.hanko {
  font-size: 16px * ($hanko-size / 100px);
  border:3px * ($hanko-size / 100px) double #f00;
  border-radius: 50%;
  color: #f00;
  width: $hanko-size;
  height: $hanko-size;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  hr {
    width: 100%;
    margin: 0;
    border-color: #f00;
  }
}
```
行数の指定、その行数を超えた場合に非表示にする(cssのみ)



cssだけで行数の指定をするやり方
①2行以上の時は文字を非表示にする
②2行以上の時は二行目の最後に三点リーダーをつける
③◯行以上で非表示

※jsでもできます。
②のやり方だとIE11非対応です。(多分)

```index.html
<p class="line_wrap">テキストの文章を2行を超えたら表示したく無いです。且つ、レスポンシブ対応にしたいです。</p>
```

#①2行以上の時は文字を非表示にする
``` style.css
.line_wrap {
  overflow: hidden;
  height: 3.6em /* 2em（行）x line-heightの1.8 */
  font-size: 16px;
  line-height: 1.8;
}
```
表示したい行数 * line-height
をheight（単位em）にしてoverflow:hiddenするだけ。
3点リーダーはありませんが、2行以上の場合は文字が表示されません。
<img width="414" alt="スクリーンショット 2020-03-10 17.42.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518907/33c67f91-6761-8917-a311-9f8566267c40.png">

#②2行以上の時は二行目の最後に三点リーダーをつける

```style.css
.line_wrap {
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}
```
2行以上の場合はちゃんと三点リーダーが表示されます。(レスポンシブ対応)
<img width="487" alt="スクリーンショット 2020-03-10 17.40.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518907/a90e3c16-bbff-f2db-5e69-71bbc768e45d.png">
どちらがいいかはお好みで。

##◯行以上で非表示

表示する行数をカスタマイズするときはこれが便利。

行数を指定したい(3行、4行,,,)時に以下のコードを使用できます。
△が指定する行数。

````style.css
.line_wrap{
 line-height: ■;
  font-size: ◎rem;
　height:calc(■ * ◎rem *△);
　overflow:hidden;
}
```


##参考サイト
https://coliss.com/articles/build-websites/operation/css/css-line-clamp-property.html
CSSの％とvh、vwの違い

##viewport
ブラウザで表示されている領域です。vhとvmがあります。
vhは高さでvwは幅です。
##vh
1vhと指定した場合は、ブラウザで表示される高さは1%です。つまり、ブラウザで表示されている高さが、1000pxなら 10vhだったら100pxとなります。
##vw
1vwと指定した場合は、ブラウザで表示される幅は1%です。つまりブラウザで表示されている高さが、1200pxなら 10vwだったら120pxとなります。
##%とViewportの違い
%（パーセント）は、親要素に依存します。
viewport（vh、vm)は、ブラウザの表示されている領域に依存します。
要素をページの高さいっぱいに配置する場合は、100vhを使い、要素のページの横幅いっぱいに配置する場合は、100%を使うのが多いです。
##calc関数
CSSで四則演算（+,-,*,/）を行うことができます。
【例】

````
.top {
    width: calc(100vh - 300px);
}
```
全体の高さが、1000pxだった場合.topのwidthは700pxとなります。使うメリットとしては異なる単位同士での計算が可能になります。
【例】

```
/* wrapperは親要素とします */
.wrapper {
  background-color: #d9ead3;
  height: 700px;
  width: 100%;
  display: flex;
}

div {
    width: calc(100% / 3);
}
/* 以下のセレクタは.wrapperの子要素です */
.left {
    background-color: #c9daf8;
    height: 200px;
}

.center {
    background-color: #ea9999;
    height: 200px;
}

.right {
    background-color: #fff2cc;
    height: 200px;
} 
```
上記のように記述するとひとつひとつの子要素に記述しなくても親要素に記述するだけで均等にしてくれます。
Flutter SDK セットアップで Android sdkmanager tool not found

エラーの原因は、Android Studio3.6 (2020.3.10現在)でSDK ToolsがGUIからなくなったようです。

``` 
% flutter doctor --android-licenses
Android sdkmanager tool not found
(/Users/xxxxxxx/Library/Android/sdk/tools/bin/sdkmanager).
Try re-installing or updating your Android SDK,
visit https://flutter.dev/setup/#android-setup for detailed instructions.
```
githubでもイシューが上がっているのでそのうち解決されると思います。
https://github.com/flutter/flutter/issues/51712#issuecomment-593375678

Comand line Toolに変わったようでパスを変更するとうまくいくらしいですが面倒なので
Hide Obsolete Packagesのチェックを外してSDK Toolsにチェックを入れるとうまくいきました。
<img width="996" alt="スクリーンショット 2020-03-10 17.01.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25104/5769d886-5f8b-9482-8aee-98085a4aa8f4.png">

```
% flutter doctor --android-licenses
Warning: File /Users/xxxxxx/.android/repositories.cfg could not be loaded. 
5 of 7 SDK package licenses not accepted. 100% Computing updates...             
```
【Udemy Python3入門+応用】　49. 関数の引用と返り値の宣言

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**


##■関数の引用と返り値の宣言
```python:function
# a,b,返り値をint型とし、a+bを返す
def add_num(a: int, b: int) -> int:
    return a + b

r = add_num(10, 20)
print(r)
```
```:result
30
```
Python3.6以降では、
`num: int = 10`
とすると、
「`num = 10`で、かつ`num`はint型にする」、
というように記述できた。

`-> int`としているのは、
「返り値もint型にする」
という意味。

こういう風にする場合は多くはないが、
このような記述をする場合もあるため、頭の片隅に入れておこう。
#Ruby で自前クラスのインスタンス同士で同値性チェックを実装するには == ===イコールメソッドを定義すれば良いかね？

- 強制的に同値の評価を真にしてしまう場合。別のクラスのインスタンスだろうとなんだろうと常に「同値だ！」と言い張るクラスを作ってみる。実用的には特に意味なし。
- Aのインスタンスが同値評価をしようとすると、常に true を返す。(特に意味はありません)
- 有意な同値評価を実装してみてください。

```rb
class A
  def ==(instance)
    true
  end

  def ===(instance)
    true
  end
end

class B
end



A.new == B.new
# => true

A.new === B.new
# => true

B.new == A.new
# => false

B.new === A.new
# => false
```


== と === だけ再定義可能みたいだ

[Rubyにおける==，===，eql?，equal?の違い - ぬいぐるみライフ？](https://mickey24.hatenablog.com/entry/20100910/1284052782)


# Original by Github issue

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








<!-- Update From Qiita API -->

# チャットメンバー募集


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

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





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
FirebaseのTimeStampあれこれ[忘備録]

#FirebaseのTimeStampをswiftであれこれ

```swift
func chancgeFirebaseTime(date:Date){
      let formatter = DateFormatter()
    
      formatter.dateStyle = .medium
      formatter.timeStyle = .short
      formatter.locale = Locale(identifier: "ja_JP")
      print(formatter.string(from: date))//2020/00/00 00:00
  }
```


```swift
let serverTime = "firebaseで取得してきたTimeStamp型を配置".dateValue()//To date type
chancgeFirebaseTime(date: serverTime)
```

##ちなみにカスタム出来ます↓
- .dateStyle
- .timeStyle
- .locale

編集途中です、、
【kaggleユーザー向けのAzure 入門】Azure Notebooks と Azure Notebooks VM の始め方や使いどころ比較


## 狙い
手元の計算資源では限界を感じている、けれどクラウドリソースは使い方がよくわからないという方向けに Azure Notebooks と Azure Notebooks VM の使い方をまとめる。詳細はAzure公式チュートリアルを紹介するとして、ぶっちゃけ何をどれだけやれば計算資源として利用できるのかというあたりをフォローしたい。

## 対象者
* kaggle などのコンペ参加ユーザーで、GPU利用やメモリ増量などの環境をクラウドで利用したい方
* 業務などで手元の計算環境では不足を感じている方

## 前提条件
* (プライベートで利用する場合は) `hotmail.com` や `outlook.com` などのメールアカウント
* Azure サブスクリプション（無料Verでも可）
* [Azure ML ワークスペースの作成(リンク先マニュアルに沿って作れます)](https://docs.microsoft.com/ja-jp/azure/machine-learning/service/how-to-manage-workspace)

## 利用方法
1. Azure Notebooks Free compute
1. Azure Notebooks VM
1. 上記２つの複合利用

## 1. Azure Notebooks Free compute の利用
### 仕様

* 利用金額：無料
* マシンスペック：CPU 1 core, 4GB RAM, 7GB Storage
* 言語：R, Python

### 長所
* 無料で Azure 関連パッケージ（もちろん tensorflow や Keras 、Pytorch も）がインストール済みの Jupyternote 環境が利用できる点
* 起動が早く、すぐに始められる

### 短所
* 最近の平均的なPCよりスペック低い環境
* たまにフリーズしたり、Runを連打したりするとセキュリティでシステムから何時間か出禁になったりするので、ファイルはこまめにローカルにダウンロードするか、コードの編集はローカルで実施などがよさそう
* R ユーザーも Jupyter 環境での作業になり、使い慣れた R-Studio では利用できない

## 始め方
* [公式リンク](https://notebooks.azure.com/)でサインイン
* 初回ログイン時は AzureML に関するチュートリアルの .ipynb ファイルを Github からコピーしてプロジェクトを始めるか聞かれる（チュートリアルは Python Ver のみ）
* Projectという聞きなれない単位が出てくるが単なるディレクトリと考えて、最初は問題ない
* +ボタン → Notebook → [R]か[Python]を選択してスタート

## 2. Azure Notebooks VM の利用
### 仕様

* 利用金額：https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/windows/
* マシンスペック：上のリンクから自由に選択
* 言語：R, Python

### 長所
* Azure 関連パッケージ（もちろん tensorflow や Keras 、Pytorch も）がインストール済みの Jupyternote 環境が利用できる
* GPU利用や大容量RAMなど、スペックを自由に選べる
* R ユーザーは R-Studio 環境も選べる

### 短所
* たまにフリーズしたり、Runを連打したりするとセキュリティでシステムから何時間か出禁になったりするので、ファイルはこまめにローカルにダウンロードするか、コードの編集はローカルで実施などがよさそう

## 始め方
* [Azure Portal](https://ms.portal.azure.com/#home)にログインして、前提条件で作成したワークスペースの画面に移動 → 左側ツールバーの[計算]を選択 → [Notebook VMs]のNewから名前とスペックを選んで作成

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F490864%2F0e03744e-3951-3de4-44db-9aaf130f10b5.png?ixlib=rb-1.2.2&auto=compress%2Cformat&gif-q=60&s=2b173c47038cd033aad84250aab338c2)

* 作成には 10 min 程度かかる
* Status : Running になったら、隣の Application URL から JupyterLab, Jupyter, R-Studio から選択して実行

## 3. Free compute と VM の複合利用
### 仕様

* 利用金額：https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/windows/
* マシンスペック：上のリンクから自由に選択
* 言語：R, Python

### 長所
* データ前処理は Free compute で、ディープラーニングの学習などは GPU 環境を使うなど、スペックが必要な部分だけ必要な VM を使うことでコストカットできる 
* Azure AutoML が利用できるので、いろいろな手法のモデリングやそれらのアンサンブル（平均、スタッキング）で最良のモデルを自動選択できる

### 短所
* たまにフリーズしたり、Runを連打したりするとセキュリティでシステムから何時間か出禁になったりするので、ファイルはこまめにローカルにダウンロードするか、コードの編集はローカルで実施などがよさそう
* 専用のコーディングがあり、覚えるのが手間
* 前処理は時間的にも大して費用に効かないことも多いので終始 VM でやれば十分という気もする

## 始め方
* Python : https://docs.microsoft.com/ja-jp/azure/machine-learning/service/tutorial-1st-experiment-sdk-setup
* R : https://docs.microsoft.com/ja-jp/azure/machine-learning/service/tutorial-1st-r-experiment
【Udemy Python3入門+応用】　48. 関数定義

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**


##■関数を定義して呼び出す
#####◆基本
```python:function
def say_something():
    print('hi')

# 定義したsay_something()を呼び出す
say_something()
```
```:result
hi
```
このとき、`say_something`の後ろに`()`をつけないといけない。

```python:function
def say_something():
    print('hi')

f = say_something
f()
```
```:result
hi
```
このように別の変数に代入してから呼び出すことももちろん可能。

#####◆typeはどうなる？
```python:function
def say_something():
    print('hi')

print(type(say_something))
```
```:result
<class 'function'>
```
typeは 'function' になっている。

#####◆返り値
```python:function
def say_something():
    s = 'hi'
    return s

result = say_something()
print(result)
```
```:result
hi
```
ここでの`say_something()`の中身は、
`'hi'`を`ｓ`に代入し、
その`s`を返す
というものになっている。

#####◆引数
```python:result
def what_is_this(color):
    print(color)

what_is_this('red')
```
```:result
red
```

#####◆応用
```python:function
# 引数に応じたオブジェクトをprintする
def what_is_this(color):
    if color == 'red':
        return 'tomato'
    elif color == 'green':
        return 'green pepper'
    else:
        return "I don't know."

result = what_is_this('red')
print(result)

result = what_is_this('green')
print(result)

result = what_is_this('blue')
print(result)
```
```:result
tomato
green pepper
I don't know.
```
このように、なんども同じ処理をする場合は、共通の部分を関数として定義し、
その関数を呼び出すようにするとよい。
Laravelのインストール後の設定

##概要
[前回記事](https://qiita.com/r24desgin/items/b6805c06c20024ccb388)でmacOSにインストールしたLarvelの設定を行う

##設定
####ローカリゼーション
　タイムゾーン（時刻設定）とロケール（地域、言語設定）、フェイカー（ダミーデータ言語）を日本に変更。
　作成したプロジェクトフォルダ配下のconfigフォルダに、設定ファイル app.php があります。

　**timezone**で検索（70行目）、値を **Asia/Tokyo** へ、続くブロックにある **local** の値を **ja** へ、 **faker_locale** は **ja_JP** へ変更。

```ruby:config/app.php
'timezone' => 'Asia/Tokyo',

'locale' => 'ja',

'faker_locale' => 'ja_JP',
```

#データベース
データベースを使わない場合は不要ですが、使う場合はローカルにインストールするか仮想サーバをインストールします。Laravelの開発環境向けに、前者はMac用のValet、後者はHomesteadがあります。今回は軽量で動作するValet（ベレット）を使います。

####Valetインストール

公式サイト
https://readouble.com/laravel/6.x/ja/valet.html

Laravelをインストールしたディレクトリにて、Composerを使って実行
（artisanでローカルサーバを起動している場合は停止しておきます）

```
$ composer global require laravel/valet
$ valet install
```

メッセージ確認
`Valet installed successfully!`

ping確認
任意のxxx.testにpingを打つと127.0.0.1から応答確認

その他
>他のドメインの使用
>>デフォルトでValetは.test TLDをプロジェクトのドメインとして処理します。他のドメインを使いたい場合、valet tld tld-nameコマンドを使ってください。
たとえば、.testの代わりに.appを使用したければ、valet tld appと実行します。Valetは *.appをプロジェクトのために自動的に使い始めます。


####MySQLインストール

Homebrewを用いてインストール

```
$ brew install mysql@5.7
```

インストール後、起動

```
 $ brew services start mysql@5.7
==> Successfully started `mysql@5.7` (label: homebrew.mxcl.mysql@5.7)
```

その他
>PHPバージョン
>>Valetではvalet use php@versionコマンドにより、PHPバージョンを変更できます。指定されたPHPバージョンがインストールされていない場合、ValetはBrewによりインストールします。
valet use php@7.2
valet use php
Note: 複数のPHPバージョンをインストールしている場合でも、Valetは一度に一つのPHPバージョンのみを提供します。

>インストレーションのリセット
>>Valetインストレーションが正しく動作せずに問題が起きた時は、composer global updateの後に、valet installを実行してください。これによりインストール済みのValetがリセットされ、さまざまな問題が解決されます。稀にValetを「ハードリセット」する必要がある場合もあり、その場合はvalet installの前にvalet uninstall --forceを実行してください。

> アップグレード
>>Valetインストールをアップデートするには、ターミナルでcomposer global updateコマンドを実行します。アップグレードできたら、valet installコマンドを実行し、必要な設定ファイルの追加アップグレードを行うのは、グッドプラクティスです。


サイト動作確認

ディレクトリを作成し、parkコマンド実行

```
$ mkdir ~/DIR
$ cd DIR/
$ valet park
```

Laravelサイトを作成

```
$ laravel new blog
```

ブラウザから http://blog.test を開く


>必要なのはこれだけです。 これで"parked"ディレクトリ内に作成されたLaravelプロジェクトは、http://フォルダ名.test規約に従い、自動的に動作します。

前回と同様サイトが表示されればOKです。

<img width="867" alt="blog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599693/3bee228e-db13-64e6-a724-d4de08d2c626.png">


###Valetの停止

起動したサービスを停止する場合

```
$ valet stop
```


```ruby:MySQL
$ /usr/local/opt/mysql@5.7/bin/mysql.server stop
```
C#のLINQでよく見かけるメソッドについて復習だ。

# はじめに
こんにちは。

突然ですが、C#のLINQでよく見かけると思うメソッドを3つ、頭に思い浮かべてみてください。

筆者は**Selectメソッド, Whereメソッド, FirstOrDefaultメソッド**だと思うのです。
ということで本記事ではこれら3つのメソッドについて簡単なサンプルをご紹介。
# Selectメソッド
やっぱり最初はSelectメソッドじゃない？
Selectメソッドの効果は**加工や変形**です。あえて数学っぽい言葉で言うなら**射影**か。

```C#
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] a = { 1, 2, 3, 4, 5 };
        var result = a.Select(c => c + 3);

        foreach (var item in result) Console.WriteLine(item);

        Console.Read();
    }
}
```
この例では、配列のアイテムひとつひとつに対して`c => c + 3`してます。
ちなみに`Console.Read();`とあるけど、これは結果画面を閉じずに待機したかっただけなので気にしないで。
実行結果は…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600213/2b78f344-7d19-df20-478b-c0d8204936f9.png)
ちゃんと全部 3 が足されてます。
# Whereメソッド
Whereメソッドは条件に合うやつだけを絞り込む、つまり**フィルタリング**ですね。

```C#
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] a = { 1, 2, 3, 4, 5 };
        var result = a.Where(c => c >= 3);

        foreach (var item in result) Console.WriteLine(item);

        Console.Read();
    }
}
```
配列のアイテムから`c => c >= 3`という条件を満たすものだけを取り出しています。
なんか形が似てるけど、`=>`はラムダ式の記号で、`>=`は不等号（≧）よね。
実行結果は…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600213/cf032437-605a-bf48-3a36-33c7682b239b.png)
3以上のアイテムだけが取り出されました。
# FirstOrDefaultメソッド
これも個人的にはよく見かけるメソッドだと思うのです。
ファーストまたはデフォルト。つまり**最初のアイテムを返すけど該当アイテムがなかったら既定値を返すよ**という、とても賢い子。
該当アイテムがなかったら例外をぶん投げてしまうFirstメソッドよりたぶん便利。

```C#
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] a = { 1, 2, 3, 4, 5 };
        var result = a.FirstOrDefault(c => c >= 3);

        Console.WriteLine(result);

        Console.Read();
    }
}
```
実行結果は…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600213/e51fb088-5308-ba13-3fa0-5ae3546262f1.png)
たしかに`c => c >= 3`という条件を満たすアイテムのうち、最初に見つかるのは 3 。
ちなみにもし見つからなかったら、今回はint型なので既定値 0 が出力されます。
# まとめ
Selectメソッドは加工、変形、射影。
Whereメソッドはフィルタリング。
FirstOrDefaultメソッドは最初のアイテムを取り出すけど見つからなかったら既定値。

LINQのメソッドはたくさんあって全ては覚えられないけど、有名なところだけでもしっかり使い方をおさえておきたいですな。

今回紹介したメソッドのほかにはCountメソッド、OrderByメソッドあたりも頻出かな？

ちなみに本記事は筆者のQiita初投稿なので、見出しとか段落とかフォントとかでミスがあっても大目に見ていただきたい…！
MST(mobx-state-tree) でコンパイル(tsc)が激重になったり、出力 *.d.ts が激デカになったりしたら


## TL;DR

MSTのモデル定義を下記のように書き換えましょう。

```typescript:書き換え前
export const ModelA = types.model("ModelA", {/**/})
``` 

```typescript:書き換え後
const model = types.model("ModelA", {/**/})

type ModelA_InferredType = typeof model
export interface ModelA_ModelType extends ModelA_InferredType {}
export const ModelA: ModelA_ModelType = model
``` 

モデルの型を型推論そのままをexportせずに、きちんと名前がついた型としてexportするのがポイントです。

(参考: https://github.com/mobxjs/mobx-state-tree/issues/1425)

## なぜ上記のように書き換えるのか

TypeScriptは型を推論できます。

そして、その型推論の上にmobx-state-treeのようなライブラリは成り立っているわけですが、推論された型は基本的に名前を持ちません。

たとえば、下記の `ModelA` の型はなんでしょうか？

```typescript
export const ModelA = types.model("ModelA", {
  name: types.string
})

// 答え
IModelType<{ name: ISimpleType<string>, {}, _NotCustomized, _NotCustomized }>
``` 

では、下記の `ModelB` の型はどうなっているでしょうか？

```typescript
export const ModelB = types.model("ModelB", { a: ModelA })

// 答え
IModelType<{
  a: IModelType<{ name: ISimpleType<string>, {}, _NotCustomized, _NotCustomized }>
}, {}, _NotCustomized, _NotCustomized >
```

では、下記の `ModelC` の型はどうなっているでしょうか？

```typescript
export const ModelC = types.model("ModelB", { a: ModelA, b: ModelB })

// 答え
IModelType<{
  a: IModelType<{ name: ISimpleType<string>, {}, _NotCustomized, _NotCustomized }>;
  b: IModelType<{
    a: IModelType<{ name: ISimpleType<string>, {}, _NotCustomized, _NotCustomized }>
  }, {}, _NotCustomized, _NotCustomized >
}, {}, _NotCustomized, _NotCustomized >
```

なんか辛くなってきましたね。でも、なんで辛いのでしょうか？

それは、モデルの型に名前がついていないからです。たとえば、 `ModelA` の型について、いちいち `a: IModelType<{ name: ISimpleType<string>, {}, _NotCustomized, _NotCustomized }>` という型が出てきて非常に煩雑になっています。

仮にこれが `ModelA_ModelType` という名前だったなら、 `ModelC` の型は、下記のように非常にすっきりします。

```typescript
IModelType<{
  a: ModelA_ModelType;
  b: IModelType<{
    a: ModelA_ModelType
  }, {}, _NotCustomized, _NotCustomized >
}, {}, _NotCustomized, _NotCustomized >
```

同じことが tsc のコンパイルにも言えます。

今の tsc (`3.8.3`時点) はコンパイル途中経過およびコンパイル出力結果において、 **型推論結果に名前がない場合、型推論の大きな型を持ち回り、いちいちその型を展開して利用します。**

MSTの場合、単純なモデルではこれは大きな問題になりませんが、複雑なネストを持つモデルを持ち始めた瞬間、上記の巨大な型をtscが用いるようになるので、コンパイル時間が簡単に膨れ上がります。

私のケースでは、ビルド時間が 1分程度 → 1時間弱にまで成長しました。また、複雑なモデルを参照しているだけの20行程度のファイルが、2000行の型ファイルにコンパイルされていました。

## まとめ

MSTのようにメソッドチェーン＋型推論で型を成長させていくライブラリの場合、型推論結果に名前をつけずにそのまま使っていると、いつかTypeScriptコンパイラが死にます。

（コンパイラ側で適度なところで名前をつけるなり、何とかしてほしいですが…。）

気をつけましょう。
Docker Composeにおける各種ファイルの変更時の反映

# はじめに
- ここでは、docker-composeによるコンテナ構成時の、各種ファイル更新時の反映方法についてまとめる。
- 以下のファイルが対象。
    - docker-compose.yaml
    - Dockerfile
    - ソースコード等

# docker-compose.yaml変更を反映
- イメージ：再構築の必要なし
- コンテナ：再構築の必要**あり**：```docker-compose up -d```

# Dockerfileの変更を反映
- イメージ：再構築の必要**あり**：```docker-compose build```
- コンテナ：再構築の必要**あり**：```docker-compose up -d```

# ソースコード等の変更を反映
- イメージ：再構築の必要**あり**：```docker-compose build```
- コンテナ：再構築の必要**あり**：```docker-compose up -d```
- 以下、補足
    - ローカルのボリュームとコンテナのボリュームをマウントしてる場合はこれに限らず。
    （コンテナに対してローカル領域をマウントしているのでイメージを作り直す必要はない）
    - Dockerfileの中でローカルのソースコードを読み込んでいる処理がある場合は、イメージの再構築が必要。
    （イメージ内でソースコードを抱えているため、ローカル側のソースコードの修正が反映しないので、作り直す必要がある）


# 補足
- Dockerfileに関する別記事を作成しましたので、よかったら参考にしてみてください。  
    - [DockerfileにおけるCMDとRUNの挙動](https://qiita.com/subretu/items/31a387510f84daaa4c1c)
エクセルでソートやマクロを使わずに、連番IDかつ同じ商品には同じIDを振りたい場合の関数

「マクロを使えばできます。」みたいなものは求めていなくて
関数だけで、できる限りのことはすませたい。
コピペもできるし、Googleスプレッドシートにも使えるし。
という人向けです。

# やりたいこと
![renban.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533019/057036fa-f173-afe5-a0ee-24b08ac0571f.png)

同じ商品には同じ番号(ID)を割り振りたい。
番号は連番にしたい。
あとから商品を追加しても大丈夫なようにしたい。

# 考え方
一番最初の商品は 1
それ以降、はじめて出現する商品は +1していく
同じ商品の場合は同じ番号を使う

# 関数
B2 に利用している関数は下記になります。

`=IF(ISNUMBER(B1),IF(MATCH($A2,$A:$A,FALSE)=ROW(),MAX(OFFSET(B:B,1,0,ROW()-2,1))+1,INDEX(B:B,MATCH($A2,$A:$A,FALSE))),1)`

B3 以降はスマートフィルとかCtrl+Dとか、コピペしてください。
おしまい。

以下、解説になります。

----

# 関数の解説
## 最初のIF
```
001:	=IF(
002:	  ISNUMBER(B1),
003:	  IF(MATCH($A2,$A:$A,FALSE)=ROW(),MAX(OFFSET(B:B,1,0,ROW()-2,1))+1,INDEX(B:B,MATCH($A2,$A:$A,FALSE))),
016:	  1
017:	)
```

002 条件式：ひとつ上が数字かどうか
003 真：数字の場合は色々やる（後述）
004 偽：最初のIDは、1にする

## 次のIF
```
003:	IF(
004:	  MATCH($A2,$A:$A,FALSE)=ROW(),
005:	  MAX(OFFSET(B:B,1,0,ROW()-2,1))+1,
014:	  INDEX(B:B,MATCH($A2,$A:$A,FALSE))
015:	),
```

002 条件式：自分の行の商品が自分と同じか＝新出の商品
003 真：いままでの最大値に +1 する
004 偽：最初に出現した商品と同じ番号にする

## いままでの最大値
```
005:	MAX(
006:	  OFFSET(
007:	    B:B,
008:	    1,
009:	    0,
010:        ROW()-2,
011:        1
012:      )
013:    )+1,
```

005 最大値：()内の最も大きな数を取得
006 指定の位置からズラした範囲を取得
007 OFFSET参照：対象の範囲
008 OFFSET行数：開始位置を1にして見出し(連番)を除外
009 OFFSET列数：開始位置は0のまま
010 OFFSET高さ：自分の位置よりもひとつ上まで（見出しと合わせて-2）
011 OFFSET幅：1列のまま
013 最大値に +1

## 既にある商品と同じID
```
014:	INDEX(
015:	  B:B,
016:	  MATCH($A2,$A:$A,FALSE)
017:	)
```

014 指定の範囲：この範囲の中の値を返す
015 行数：最初にマッチした商品の行（列数は省略）

## 整形まとめ

```
001:	=IF(
002:	  ISNUMBER(B1),
003:	  IF(
004:	    MATCH($A2,$A:$A,FALSE)=ROW(),
005:	    MAX(
006:	      OFFSET(
007:	        B:B,
008:	        1,
009:	        0,
010:            ROW()-2,
011:            1
012:          )
013:        )+1,
014:	    INDEX(
015:	      B:B,
016:	      MATCH($A2,$A:$A,FALSE)
017:	    )
015:	  ),
016:	  1
017:	)
```
Windows FormのDataGridViewに設置したチェックボックスをカチカチしたらがあべこべになった解決策

結論から言うと、ダブルクリックイベントも考慮する必要があった。

## どんな処理をしようとしていたか
DataGridViewには下記のようにデータは表示される。

|  |No  |Data  |
|---|---|---|
|□  |1  |AAA  |
|□  |1-1  |BBB  |
|□  |1-2  |CCC  |
|□  |1-3  |DDD  |
|□  |2  |AAA  |
|□  |2-1  |BBB  |

例として、No列の1のチェックボックスをクリックすると
1-1～1-3のチェックボックスにもチェックが付き、2～2-1にはチェックが付かないようにする。
こんな感じ。

|  |No  |Data  |
|---|---|---|
|■  |1  |AAA  |
|■  |1-1  |BBB  |
|■  |1-2  |CCC  |
|■  |1-3  |DDD  |
|□  |2  |AAA  |
|□  |2-1  |BBB  |

## カチカチするとあべこべに

普通にポチッとクリックするだけなら問題なく動いたが、ダブルクリックみたいに間髪入れずにクリックするとこんな感じになる。

|  |No  |Data  |
|---|---|---|
|■  |1  |AAA  |
|□  |1-1  |BBB  |
|□  |1-2  |CCC  |
|□  |1-3  |DDD  |

とか

|  |No  |Data  |
|---|---|---|
|□  |1  |AAA  |
|■  |1-1  |BBB  |
|■  |1-2  |CCC  |
|■  |1-3  |DDD  |

になる。
こうなるともう無言で画面再表示。

## どう修正したか

CellContentDoubleClickイベントを追加して同じ処理を行うようにした。

```
private void DgvCellContentClick(object sender, DataGridViewCellEventArgs e)
{
	// 大本のチェックボックスが選択されたか判定する
	if (dgv["1", e.RowIndex].Value.ToString() == "1")
	{
	    // チェックボックスの状態を反映する
	    dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
	    
	    // 関連するデータすべてのチェックボックスの状態を変更する
	    if ((bool)dgv[e.ColumnIndex, e.RowIndex].EditedFormattedValue)
	    {
	        // チェックが付いた場合はチェックを付ける
	    	// 省略
	    }
	    else
	    {
	        // チェックが外れた場合はチェックを外す
	        // 省略
	    }
	}
}

private void DgvCellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
	// 全く同じ内容なので省略
}
```

データの種別を判定したり何なりは省略。

カチカチッ(コンマ秒)だけではなく、カチ‥カチ(1秒くらい)でもあべこべになっていたのがこれで解決。
おわり。
KotlinでHelloWorldプラグインを書こう

みなさんこんにちは
いつも記事を見ていただきありがとうございます。
そういや私はこの間githubとQiitaの名前を本名にしちゃいました。
やっぱりgithubで仕事が来る時代ですからね～
最初は迷いましたが思い切ってやってみましたとさ。
今回はKotlinでHelloWorldを書いていこうと思います。

##まずKotlinとはなんなの
つい最近プログラム業界（特にAndroid）とかではkotlinで開発を始めるなどと言われていますがまずkotlinとはなんなのかについて話していこうと思います。
まぁ一番いいのはこの方の記事をみてほしいのですがhttps://qiita.com/farman0629/items/f959a4ff791f26b33fc3 今すぐ知りたいって人はここで超大まかに説明していきますね。
こういうのって前置き長くてもあれなのでサクッと解説していきます。
**kotlinとはズバリJavaと完全互換性を持ったプログラム言語です。**
まぁkotlinを使う利点としては自分的にはJavaでは定番のセミコロン(;)がいらなくなることや便利な関数がたくさんあります。
では本題に入っていきましょう。

##KotlinでHelloWorldを書く
さあ、書いていこうにもまずは環境を整えましょう。
今回からPaperMCのプラグインを作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545700/6592c0b5-cb76-7d6b-59c8-e01a8a04ae51.png)
PaperMCにチェック
そこいがいはお好みでどうぞ
**ただしMavenをGradleにするのをお忘れなく。**
これを忘れるとここから説明が進みません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545700/76feb282-5283-c4c2-f49c-ab445525a010.png)
このようにできましたか？
できたらhttps://github.com/Chiharu-Hagihara/PaperMCKotlinTemplate ここから「build.gradle」「gradlew」「gradlew.bat」を持ってきましょう。
持ってきたらjavaのファイルを開いた状態で Ctrl+alt+shift+KでKotlinに書き換えてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545700/76469f81-6504-859c-38e5-8cba5b79cec7.png)
そしたらあとは簡単。
わからない人はこのSSを参考に書き写しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545700/37c83079-f4f8-27de-15ef-7ad7c02a77f0.png)

~~~kotlin:コピペ用
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        sender.sendMessage("Hello Kotlin!")
        return true
    }
~~~
そしてplugin.ymlを編集しましょう。

~~~yaml:plugin.yml
commands:
  hello:
~~~

そしたらもうお分かりの通り再生ボタンを押してJarを作りましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545700/73cee29d-8d62-ed76-65ef-bc7ff7cf139c.png)
そしてできたやつをpluginsのファイルに入れてサバ内で/helloとして「Hello Kotlin!」と出てきたら完成です。

さて、今回はここまで！
ぜひ良ければフォローとグッドよろしくお願いします。
【Udemy Python3入門+応用】　47. 辞書をfor文で処理する

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**



##■辞書をfor文で処理する
#####◆例
```python:dict_for
d = {'x': 100, 'y': 200}

for v in d:
    print(v)
```
```:result
x
y
```
このようなコードだと、keyのみがprintされる。
keyだけでなく、valueもprintしたい。

#####◆`.items`メソッド
```python:dict_for
d = {'x': 100, 'y': 200}

for k, v in d.items():
    print(k,':', v)
```
```:result
x : 100
y : 200
```
`.items()`を使うことで、変数を2つ用意すると、それぞれにkeyとvalueが代入されるようになる。

どういう仕組みになっているのか調べるために、`d.items()`をprintしてみる。

```python:dict_for
d = {'x': 100, 'y': 200}

print(d.items())

# for k, v in d.items():
#     print(k,':', v)
```
```:result
dict_items([('x', 100), ('y', 200)])
```
`d.items()`の部分に、タプルがリストになったものが返ってきていることがわかる。
これにより、このタプルがアンパッキングされて、2つの変数に代入されていることがわかる。
Wordpress自作テーマ　ヘッダーの画像とテキストを設定 + 変更可能にする

#はじめに
この記事ではwordpressの自作テーマでヘッダー画像をセットしたい場合、どうるれば良いかを紹介。

#functions.php
結論、`functions.php`を編集します。
これを追加することで、`管理画面＞外観＞ヘッダー`が選択できるようになる。

```php:functions.php
//ヘッダーテキストの色を変更可能にする
function wphead_cb() {
  echo '<style type="text/css">';
  echo '.topimg-pc h1, .topimg-pc h6 { color: #'.get_header_textcolor().' }';
  echo '</style>';
}

// カスタムヘッダー
$custom_header = array(
'random-default' => false,
'width' => 1000,
'height' => 300,
'flex-height' => true,
'flex-width' => true,
'default-text-color' => '',
'header-text' => true,
'uploads' => true,
// ヘッダーテキストのデフォルトの色
  'default-text-color' => '000',
  'wp-head-callback' => 'wphead_cb',
                         // デフォルト画像へのパス
'default-image' => get_bloginfo('template_url').'/src/img/top_img.jpg',
'admin-head-callback'    => '',      // 管理画面で、[外観 - カスタマイズ]をプレビューするためのコールバック
    'admin-preview-callback' => '',
);
add_theme_support( 'custom-header', $custom_header );
```

<img width="314" alt="スクリーンショット 2020-03-04 20.46.28.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/20085312-5324-9732-d21a-421f08dd4a25.jpeg">

#ヘッダー画像を出力
どこにヘッダー画像を出力するかは人によるけど、私は共通化してある`header.php`にした。

```html:header.php
 <div class="topimg-pc">

 <!-- もしヘッダー画像が設定されていたら -->
       <?php if ( get_header_image() ) : ?>
          <img src="<?php header_image(); ?>" height="<?php echo get_custom_header()->height; ?>" width="<?php echo get_custom_header()->width; ?>" alt="" />

 <!-- もしヘッダー画像が設定されていないなら -->
          <?php else: ?>
           <img src="<?php echo get_template_directory_uri(); ?>/src/img/top_img.jpg">
        <?php endif; ?>

        <h1><?php bloginfo( 'name' ); ?></h1>
        <h6><?php bloginfo('description'); ?></h6>

  </div>
```

#ヘッダーテキストの色を変更

`echo`で何を吐き出すかは、HTMLタグとCSSによって変わる。

```php:functions.php
//ヘッダーテキストの色を変更可能にする
function wphead_cb() {
  echo '<style type="text/css">';
  echo '.topimg-pc h1, .topimg-pc h6 { color: #'.get_header_textcolor().' }';
  echo '</style>';
}
```

<img width="305" alt="スクリーンショット 2020-03-10 20.28.28.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/3a782c18-0585-7642-5362-1cf76417a9fe.jpeg">


これで`管理画面＞外観＞ヘッダー＞色`からヘッダーテキストの色を変更可能になった！
erlang 22.2 のインストールで発生した問題と対応のメモ

# やりたかったこと
erlang 22.0.5 がインストールされている環境に erlang 22.2 をインストールする。

## 発生した問題

```
$ asdf current
erlang 22.2

$ erl
asdf: No version set for command erl
```

## 試したけど改善しなかった対応

### バージョンを指定

```
$ asdf local erlang 22.2
```

### shims 再作成

```
$ asdf reshim erlang 22.2
```

### 再インストール

```
$ asdf uninstall erlang
$ asdf install erlang 22.2
```

## 解決した対応

erlang のパッケージごと再インストールで解決しました（原因は不明のまま）。

```
$ asdf plugin-remove erlang
$ asdf plugin-add erlang
$ asdf install erlang 22.2
```

パッケージごと削除したので、`erlang 22.0.5` のインストールもしなければなりませんでした。
各チャットツールでの改行・送信のキーまとめ

仕事やプライベートで色々なチャットツールを使っているとどのキーが改行でどのキーが送信か、
間違うことが多いのでまとめておきます。
(今日も元気に改行しようと思い送信しました。)

| ツール名 | 改行 | 送信 |
| ------- | ---- | ---- |
|Slack    |Enter, Shift + Enter, Alt + Enter|Ctrl + Enter|
|ハングアウト|Shift + Enter|Enter|
|Microsoft Teams|Shift + Enter|Enter|
|Skype|Shift + Enter|Enter|
|Discord|Shift + Enter|Enter|
|LINE|Shift + Enter|Enter|
|Chatwork|Enter|Shift + Enter|

だいたい改行はShift + Enterで設定されているみたいです。
他に追加しておいた方がいいツールや別のキーでも出来る等あったらコメントでお教えください。
【Udemy Python3入門+応用】　46. zip関数

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**

##■zip関数
#####◆for文で記述した場合
```python:for
days = ['Mon', 'Tue', 'Wed']
fruits = ['apple', 'banana', 'orange']
drinks = ['coffee', 'tea', 'bear']

for i in range(len(days)):
    print(days[i], fruits[i], drinks[i])
```
```:result
Mon apple coffee
Tue banana tea
Wed orange bear
```

#####◆zip関数を使って記述した場合
```python:zip
days = ['Mon', 'Tue', 'Wed']
fruits = ['apple', 'banana', 'orange']
drinks = ['coffee', 'tea', 'bear']

for day, fruit, drink in zip(days, fruits, drinks):
    print(day, fruit, drink)
```
```:result
Mon apple coffee
Tue banana tea
Wed orange bear
```
【Udemy Python3入門+応用】　45. enumerate関数

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**


##■enumerate関数
#####◆for文で記述した場合
```python:for
i = 0
for fruit in ['apple', 'banana', 'orange']:
    print(i, fruit)
    i += 1
```
```:result
0 apple
1 banana
2 orange
```

#####◆enumerate関数を使って記述した場合
```python:enumerate
for i, fruit in enumerate(['apple', 'banana', 'orange']):
    print(i, fruit)
```
```:result
0 apple
1 banana
2 orange
```
gonを使ってJavaScriptへの直書き環境変数を防ぐ

## まず環境変数を設定する
Dotenvを用いて環境変数を設定していきます。

**1 gemのインストール**

```ruby:Gemfile
gem 'dotenv-rails'
```

```
bundle install
```

**2 envファイルの作成**
appファイルやcofigファイル、Gemfileがあるルートディレクトリに**「.env」**というファイルを作成して下さい。

<img width="167" alt="スクリーンショット 2020-03-10 15.36.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/5e695632-7147-c884-be81-4cdcffb28a22.png">

私は一瞬迷ったので画像載せておきます。
歯車マークが目印です。

**3 環境変数の設定**

```|.env|
ACCESS_KEY='*******'
```

**4 .gitignoreの編集**
環境変数をGitのトラッキングの対象外にする為記述します。

```|.gitignore|
.env
```

これでgonを使うための下準備は完了です。

## gonのインストール
Gemfileに追記して下さい。

```ruby:Gemfile
gem 'gon'
```

```
bundle install
```

## JSファイルでRails環境変数を扱う

**１ コントローラーの編集**
JavaScriptを呼び出しているViewに対応させてコントローラーのメソッド内に以下を記述します。
例えば、new.html.hamlでjsを呼び出しているのであればnewメソッド内に追記します。

```ruby:gons_controller.rb
def new
  gon.xxx_access_key = ENV['ACCESS_KEY']
end
```

**2 renderメソッド**
application.html.haml内のheadタグに以下を記述して下さい。

```html:application.html.haml
= Gon::Base.render_data
```

**3 JSファイルに記述**
jsファイル内にコントローラーで設定した変数を記述します。

```gon.js
gon.xxx_access_key
```
これでjavascriptとrailsの連携が出来ました。
Windows FormでDataGridViewの描画処理が無限ループしたときの解決策

結論から言うと、別のイベントで処理するようにしたら解決した。

## どういう画面で起きたのか
みんな大好きDataGridViewを使って、List内に格納されているデータを表示する画面。

DataGridViewに表示する際に、値によって罫線を描画したりしなかったりがあってちょっとややこしい表。
俗に言うセルを結合というやつ。見かけだけ。

この罫線描画が今回の話の中心。

## なぜ無限ループが起きた
<dl>
<dt>原因</dt>
<dd>CellPaintingのメソッド内で、罫線を描画しようとして無限ループが発生していた</dd>
</dl>

やらかしたのはこういう感じのソース(※記憶とコピペ頼り)

```
private void DgvCellPainting(object sender,
	DataGridViewCellPaintingEventArgs e)
{
	//セルの値が0ではないか判定
	if (dgv.Rows[e.RowsIndex,e.ColumnIndex].Value != 0)
	{
		// 罫線を描画する
		int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0;
		int startY = e.RowBounds.Top + e.RowBounds.Height - 1;
		int endX = startX + dgv.Columns.GetColumnsWidth(
			DataGridViewElementStates.Visible) -
			dgv.HorizontalScrollingOffset;
		//線を引く
		e.Graphics.DrawLine(linePen,
		startX, startY, endX, startY);
	}
	else
	{
		// ～罫線は描画しない、代わりに別の処理略～
	}
	// キーイベントを完了する
	e.Handled = true;
}
```

これだと無限ループが発生して画面の動きがもっさりしだした。
コンソール出力したら延々垂れ流しで焦った。

## 調べてみた
マイクロソフトさん家では下記のように解説している
> セルが描画されなければならないときに発生します。
参照元：https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview.cellpainting?view=netframework-4.8

簡素。

11時間労働が続く日の中で調べ上げたのがこちら。

CellPaintingイベントは、DataGridViewに1mmでも触れれば発生して描画しにかかるとっても繊細なイベント(イベントメソッド内にコンソール仕込んで、触ってみるとわかると思う)

描画イベントが発生するタイミングは大体こんな感じ。

* 画面表示時
* 表示する値が1コでも変更(DataGridViewが保持している内部の値が書き換わったのを検知したとき、バインド未完了の状態)
* DataGridViewの見た目が変わる(セルの幅や高さが変わったときなど)
* ヘッダーにマウスカーソルが触れる(ちょっと曖昧、発火した気がする)
* セルをクリックしたり方向キーで選択する(見た目が変わるとかぶる)
* 再描画メソッド呼び出したあと

さらにCellPaintingメソッドは、引数のeからセルの行列数が取得できる。

これは左上(-1,0)から右下へ(XX,XX)とセル一つずつ描画していくため。
ちなみに-1行目はヘッダーで、0行目からデータ表示部になる。

上記の内容から見ると・・・

1. CellPaintingイベント発生
2. 罫線の描画を行う
3. 描画発生を検知する
4. 1に戻る

こうなってしまうっぽい。
コンソール出力するとこういう感じで無限ループになる。

```コンソール出力例.
// 描画開始
(-1,0)
(-1,1)
～～～
(99,99)
// ここですべてのセルを描画し終わった・・・
// と思いきや描画開始
(-1,0)
(-1,1)
～～～
(99,99)
// ここですべてのセルを描画し終わった・・・
// と思いきや無限ループ
```

すべてのセルに対して描画処理を行ったあとにまた1から描画し直すということになる。


## どう修正したか

RowPrePaintイベントを使用して、CellPaintingイベントから切り離した。

例によってマイクロソフトさん家の解説が簡素なので省略、DOBON.NETさん家の解説を引用
> RowPrePaintイベントは行が描画される前に発生
> 参照元：https://dobon.net/vb/dotnet/datagridview/rowpostpaint.html

CellPaintingイベントはセル単位なのに対して、RowPostPaintイベントは行単位になるため引数のeでは行列は取得できない。

下記コードは特定の列まで行罫線なし、特定列以降最後まで行罫線を描画する

```
private void DgvRowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
	// 線描画位置を計算
	Pen linePen = Pens.DarkGray;
	int lineAllStartX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0;
	int lineStartX = 1;
	int lineStartY = e.RowBounds.Top - 1;
	int lineEndX = lineAllStartX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset;
	foreach (DataGridViewColumn col in dgv.Columns)
	{
		// セル合計幅を取得
		if (col.Index < 6)
		{
			lineStartX += col.Width;
		}
	}

	// 罫線を描画する
	if (dgv.Rows[e.RowIndex].Cells["XXX"].Value.ToString() == "XXX")
	{
		e.Graphics.DrawLine(linePen, lineAllStartX, lineStartY, lineEndX, lineStartY);
	}
}
```

これで無限ループはしなくなった。

## 余談

完全な解決ではなかったことを実装後すぐに気づいて泣きそうになった。
クリックバチバチしまくると罫線が消えたり出たりする。
CellPaintingイベント内でデフォルトで罫線を一旦消す処理をしていて、そっちが勝っちゃうことがあった。
クリックイベントとダブルクリックイベントに再描画メソッド付けて無理くり直した。
おわり。
【Android】Snackbarを任意の位置から表示させる

#概要
Snackbarを画面の下部から出すのではなく、任意の位置から表示させる

#レイアウトファイルでCoordinatorLayoutを入れる
```xml:fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_marginTop="200dp"
            android:gravity="center" />

     </androidx.coordinatorlayout.widget.CoordinatorLayout>

</LinearLayout>
```
`Snackbar`を表示させたいViewを`CoordinatorLayout`でラップする

#Snackbarの実装

```gradle:build.gradle
dependencies {
    implementation 'com.google.android.material:material:1.1.0'
}

```
`Snackbar`を使うために`build.gradle`に`com.google.android.material`を`implementation`する
※バージョンはよしなに変えてください

```java:MainFragment.java

import com.google.android.material.snackbar.Snackbar;

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    Button button = view.findViewById(R.id.button);
    button.setText("button");
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            showSnackbar("test");
        }
    });
    super.onViewCreated(view, savedInstanceState);
}

private void showSnackbar(String message) {
    View view = getView();
    if (view == null) return;

    Snackbar snackbar = Snackbar.make(view.findViewById(R.id.button), message, Snackbar.LENGTH_SHORT);
    snackbar.show();
}
```
`Snackbar`表示用のメソッドを作成
`Snackbar.make`の第一引数を表示させたい位置のViewのidにする（今回は`Button`）
ボタンを押した時にボタンの`View`の下から`Snackbar`を出すようにする

余分な記載が多いし、もうちょっと良い方法があると思うけど備忘録なので一旦これでいいかな。。
eccube4のキーワード検索で商品を管理している人を取得する

## やりたかったこと
「北村 山田」で検索した時は以下のものを取得するようにする。

- 北村が管理している商品
- 山田が管理している商品

単独のキーワードで良ければ参考にあるあずみさんのやりかたでOK。
今回は複数単語がある場合も対応する必要がある。

## 結論
正しい書き方かはわからないので、適時修正する必要があるかも。

以下は商品を管理している人を検索対象にするカスタマイズをした際に記載したもの。
※Creator(登録者)ではない。

```QueryCustomizer.php
<?php

public function customize(QueryBuilder $builder, $params, $queryKey)
    {
        if ($params['name']) {
            $results = [];
            $keywords = preg_split('/[\s　]+/u', str_replace(['%', '_'], ['\\%', '\\_'], $params['name']), -1,
                PREG_SPLIT_NO_EMPTY);

            foreach ($keywords as $index => $keyword) {
                $key = sprintf('member%s', $index);
                $Members = $this->memberRepository->createQueryBuilder('m')
                    ->where(sprintf('NORMALIZE(m.name) LIKE NORMALIZE(:%s)', $key))
                    ->andWhere('m.Status = '.BusinessStatus::Public)
                    ->setParameter($key, '%'.$keyword.'%')
                    ->andWhere('m.Authority = '.Authority::Owner)
                    ->getQuery()
                    ->getResult();

                // メンバーのテーブルから検索キーワードに部分一致するメンバーを取得
                /** @var Member $Member */
                foreach ($Members as $Member => $Member) {
                    $results[] = $Member->getId();
                }

            }
            $builder->innerJoin('p.Owner', 'po');
            $builder->orWhere($builder->expr()->in('po.id', array_unique($results)));
        }
    }
```
参考
eccubeの解説がわかりやすいあずみさん
[EC-CUBE4のフロントの検索ボックスで商品タグ検索できるようにする方法](https://a-zumi.net/eccube4-tag-search/)
【Udemy Python3入門+応用】　44. range関数

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**


##■range関数
#####◆listとfor文のみで記述した場合
```python:list_for
num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in num_list:
    print(i)
```
```:result
1
2
3
4
5
6
7
8
9
```
listを書くのがめんどくさい。

#####◆range関数を使って記述した場合
```python:range
for i in range(10):
    print(i)
```
```:result
0
1
2
3
4
5
6
7
8
9
```
このままだと0からスタートしているので、1から始まるようにしてやる。

```python:range
for i in range(1, 10):
    print(i)
```
```:result
1
2
3
4
5
6
7
8
9
```
1つ目の引数が始まりを指定し、2つ目の引数が終わりを指定する。

```python:range
for i in range(1, 10, 2):
    print(i)
```
```:result
1
3
5
7
9
```
3つ目の引数で、「何個とばしか」を指定できる。
2と入れた場合は、値を2つずつ(1個とばし)で処理される。

#####◆インデックスと`_`
```python:range
for i in range(10):
    print(i, 'hello')
```
```:result
0 hello
1 hello
2 hello
3 hello
4 hello
5 hello
6 hello
7 hello
8 hello
9 hello
```
このとき、`i`も一緒にprintしているが、
`i`を使わずにただ`hello`を10回printするだけの場合、

```python:range
for i in range(10):
    print('hello')
```
と書いてしまうと、他の人が読んだときに
「この`i`は何だ？」
という疑問が生じてしまいやすい。
その場合は、よく`_`が使われる。

```python:range
for _ in range(10):
    print('hello')
```
```:result
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
```
`_`を使えば、
「このループ内ではインデックスは特に使わないんだな」
といったことが伝わる。
最初にChrome拡張機能を作るときに参考にしたもの

- [OXY NOTES のカテゴリー「Google」の記事一覧](https://oxynotes.com/?cat=8)
- [Chrome拡張の作り方](https://www.amazon.co.jp/Chrome%E6%8B%A1%E5%BC%B5%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9-%E5%B1%B1%E6%9C%AC%E6%99%BA%E7%94%B7-ebook/dp/B07ZK2JJQH/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=Chrome%E6%8B%A1%E5%BC%B5%E6%A9%9F%E8%83%BD%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9&qid=1583823403&sr=8-1)
message.content内のサーバーカスタム絵文字を取り除く方法

# 不具合と対処
[Discordのチャットを読み上げるbotの作成](https://qiita.com/ToraRoot/items/d04bd91d373d31d9c3f3)の記事を見てbotを作成したところ、サーバーのカスタム絵文字の読み上げの際に**emojiのIDを読み上げてしまう不具合**が発生したので以下のように対処しました。

```py
import re

def remove_custom_emoji(text):
    pattern = r'<:[a-zA-Z0-9_]+:[0-9]+>'    # カスタム絵文字のパターン
    return re.sub(pattern,'',text)   # 置換処理
```
# 解説

カスタム絵文字は`message.content`で`<:emoji_name:emoji_id>`という形式になっています。
それを取り除くパターンを作成し`re.sub()`に渡し、マッチした部分を空白に置換することでカスタム絵文字を除去しています。


もっと正確なパターンがありましたらコメントのほうによろしくお願いします:bow_tone1:


# 参考
> [Pythonで複数の文字列を削除したい ー re.subを使う](https://qiita.com/kenTee/items/dcf63aa2895122bb3fd1)
[Discord Bot Portal JPのみなさん(Twitter)](https://twitter.com/discordbot_jp)
AWS NAT Gatewayの費用を抑えようと思ったら高くなった話

# 概要

NAT Gatewayの費用を抑えようと思ってVPCエンドポイントを使ったら、逆に高くなった話です

# 結論

* 前提条件
  * 外部APIへのリクエストなどすでにNAT Gatewayを使っている
  * セキュリティ(インターネットに出てはいけない)のことは考慮不要
  * インターネットに出てアクセスするレイテンシは考慮不要

NAT Gatewayを普段使っているのであれば、VPCエンドポイントはS3とDynamoDB（ゲートウェイエンドポイント）だけでいい
と思ってます（個人的見解です）


# 考察

### 現状
* NAT Gatewayの費用が高くなっていった
* NAT Gatewayを利用しているリクエストは以下
  * S3とかECRとかDynamoDBとかCloudWatchLogsとかAWSのサービスにアクセスしているもの
  * 外部APIにアクセスしているもの

AWSのサービスにはVPCエンドポイントを利用することでNAT Gatewayを通さずにアクセスできる

### VPCエンドポイント

* 概要：https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-endpoints.html
  * インターフェイスエンドポイント
  * ゲートウェイエンドポイント

インターフェイスエンドポイントは各AZごとにENIが作成され、そこ経由でアクセスする
ゲートウェイエンドポイントはルートテーブルのみ

**インターフェイスエンドポイントはENIを作成する関係で使っていなくても時間課金される(AZ単位で)**

インターフェイスエンドポイントを作れば作るほど、起動時間で課金されてしまう
https://aws.amazon.com/jp/privatelink/pricing/


### 損益分岐点

* 東京リージョン、3AZ利用
* VPCエンドポイント費用：($0.014/h*24h*31day*3AZ)*{VPCエンドポイント数}+$0.01/GB*通信量
* NAT Gateway費用：$0.062/GB*通信量
  * 現状NAT Gatewayを使っているという前提なので起動時間課金については考慮しない

| 通信量\VPCエンドポイント数 | 1 | 2 | 3 | 4 | 5 | NATGW増加費用 |
|:-----------------------|:---:|:---:|:---:|:---:|:---:|:---:|
| 100GB | 32.248 | 63.496 | 94.744 | 125.992 | 157.24 | 6.2 |
| 200GB | 33.248 | 64.496 | 95.744 | 126.992 | 158.24 | 12.4 |
| 300GB | 34.248 | 65.496 | 96.744 | 127.992 | 159.24 | 18.6 |
| 400GB | 35.248 | 66.496 | 97.744 | 128.992 | 160.24 | 24.8 |
| 500GB | 36.248 | 67.496 | 98.744 | 129.992 | 161.24 | 31 |
| 600GB | 37.248 | 68.496 | 99.744 | 130.992 | 162.24 | 37.2 |
| 1000GB | 41.248 | 72.496 | 103.744 | 134.992 | 166.24 | 62 |

結果、**VPCエンドポイント1つでかつ600GB以上の通信をその1つのVPCエンドポイントでする場合**に限り、NAT Gatewayを通すよりも安くなる

600GBの通信、CloudWatchLogsとかKinesisとかであれば可能性ありそうでしょうか・・・？
VPCエンドポイントの起動時間課金が安くなるorなくなること、複数AWSサービスに対して1つのVPCエンドポイントでまかなえるようになることを期待しています。


ちなみに、VPCエンドポイントを9個作ったので、費用跳ね上がりました・・・とさ。
Windowsで環境変数を設定する

#はじめに
新しくOpenSSLというものをダウンロードした時に環境変数というものを設定したので、その時の手順を備忘していきます

#環境変数の設定
『コントロールパネル』>『システムとセキュリティ』>『システム』>『システムの詳細設定』の順で開いていきます

『システムのプロパティ』が開くので、下部にある【環境変数(N)】をクリック

（windowsキー＋sで”環境変数”と検索しても表示できます！というかこっちの方が早いです）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508418/d5ccf33c-8209-7558-ba26-c0f4477a4efc.png)

『環境変数』というウィザードが表示され、上側に『**ユーザー環境変数**』・下側に『**システム環境変数**
』があるかと思います

ユーザーがシステムにサインインすると、この２つが結合され、コマンドプロンプトやアプリケーションなどに渡される（システム環境変数が設定された後にユーザー環境変数が設定される為、同じ名前の変数を定義した場合、ユーザーの方が優先される）

今回は、システム環境変数に追加します

『環境変数』のウィザードの下側のシステム環境変数(S)の一覧の中の変数：Path を選択し、【編集(I)】をクリックすると『環境変数名の編集』というウィザードが表示されます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508418/67446193-7862-fc87-dcf0-883a45d8b641.png)

【新規(N)】をクリックまたは下側の空いてる箇所をダブルクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508418/189305c5-0151-5c58-1eaf-ef13acd13a34.png)

設定したいフォルダのPathの値を入力する

表示されている『環境変数名の編集』と『環境変数』のウィザードを【OK】をクリックして閉じると設定が反映されます

コマンドラインで下記のコマンドを実行
今回はPowerShellを使いました
```openssl version```

実行結果
```OpenSSL 1.1.1d  10 Sep 2019```

バージョンが表示されれば、Pathが設定できています
【Udemy Python3入門+応用】　43. for else文

**※この記事はUdemyの
「[現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル](https://www.udemy.com/course/python-beginner/ "現役シリコンバレーエンジニアが教えるPython3入門+応用+アメリカのシリコンバレー流コードスタイル")」
の講座を受講した上での、自分用の授業ノートです。
講師の[酒井潤](https://twitter.com/sakaijun "酒井潤")さんから許可をいただいた上で公開しています。**


##■for else文
#####◆基本の使い方
```python:for_else
for fruit in ['apple', 'banana', 'orange']:
    print(fruit)
else:
    print('I ate all!')
```
```:result
apple
banana
orange
I ate all!
```
while else文と同様に、`for`によるループから`break`なしに抜けると、`else`内のものが実行される。


#####◆`break`がある場合
```python:for_else_break
for fruit in ['apple', 'banana', 'orange']:
    if fruit == 'banana':
        print('stop eating')
        break
    print(fruit)
else:
    print('I ate all!')
```
```:result
apple
stop eating
```
`break`により`for`のループを抜けたので、`else`内のものは実行されない。
kindle本の発行情報をSlackに連携したい


### 動機

 - 最新の技術本の発行情報を知りたい。
 - でも自分は毎日kindleの発行情報をあんまり見ない。
 - slackで自動通知、技術本が発行された時だけ来るのが一番ベスト。


### 対策
 - 最初はProduct Advertising APIを使いたいと考えたけど、このAPIはアカウント作成が必要で、しかもアカウント作成には審査がいる。この審査はウェブサイト或いはモバイルアプリが必要になるので、slack通知だけのために使うのは無理かも。

 - そこでProduct Advertising APIを使っているサービス [キンセリ](https://yapi.ta2o.net/kndlsl/) から発行情報を取得することにした。

### 実装方法

 - golang + [agouti] (https://github.com/sclevine/agouti) でクローラを作る。
 - [Incoming Webhook](https://slack.dev/node-slack-sdk/webhook) を利用してslackに通知。

### 結果

実際の結果はこんな感じ：
<img width="892" alt="スクリーンショット 2020-03-10 15.32.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153285/0ea44912-ca8e-b1c0-3ec3-f9618b3dda52.png">