OmniAICreator/Qiita-1.07M huggingface dataset:
Swiftの無限ループをforEachで実現する

## はじめに
なんで余計なことを考えるのか。`while( true ) {}`を使いましょう。
（Swift触り始めて数日の初心者なので、間違いなどがある可能性があります）

## きっかけ
ネットのどこかでみかけた以下のやりとり。


>cソースからswift5に移行してるのですが、ループ処理の代替で詰まりました。
>for (;;) {}
>上記for文をswift5で書くとするとどう書くでしょうか？
>while (true) {}
>が無難ですかね...  

<div/>
>それ以外ないような

<div/>
>やはりwhile文使うしか無さそうですね。ありがとうございました

<div/>
>`foreachは？` 

<div/>
>無限ループforeachサンプルお願いします

<div/>
>ごめん勘違いしてたわ


## 作ってみたもの
cancelメソッドを実行するまで無限にnextできるイテレーター。

```swift:無限forEach
class Loop:Sequence,IteratorProtocol{
    typealias Element = Loop
    var _loopVal :Loop?
    
    init() { _loopVal = self }    
    func next() -> Loop? {return _loopVal }
    func cancel() { self._loopVal = nil }
}
```

```swift:forEachでの使い方
var cnt = 0
Loop().forEach { loop in 
    cnt += 1
    print(cnt)
    
    if cnt > 5 { loop.cancel() }
}
```

```swift:for..inでの使い方(breakで抜けるパターン)
var cnt = 0
for _ in Loop(){
    cnt += 1
    print(cnt)
    
    if cnt > 5 { break }
}
```

（使用例の条件が簡易すぎて用途が不明瞭すぎる…）


## 以下雑感（作ってる間に考えてたこととか）
- 最初は単なる無限のジェネレーター（イテレーター、シーケンス）を作ればそれで完了だと思っていた。
 - for..inはいいけど、forEach{}(クロージャー)で使うと無限ループ終了できないね。うん。
 - Cancelの仕組みつくろか。 `cancel:bool`を条件値に持てばええな。
 - ……。ループするたびに判定処理とか実行コストアカンでしょ……。
 - `self`を変数にいれて管理すれば判定いらんくなるな。（これでやっと↑のコードに）

```swift:ループ毎に判定してたやつ
class Loop:Sequence,IteratorProtocol{
    typealias Element = Loop
    var _cancel = false
    
    func next() -> Loop? {return self._cancel ? nil : self }
    func cancel() { self._cancel = true }
}
```
<br/>


- Cancelの仕組みはスレッドセーフの観点からみたらどうなる？？
 - breakで抜ける分には、breakの条件になる値をスレッドセーフにして読み書きしてれば大丈夫だろうけど、Cancelでは次ループに入る可能性があるのでは？
 - ……残念なことに問題がでそうなコードを書いてみたけれど、おかしな値を出すことができなかった。<br/>(並列処理の書き方がよくない？？）

```swift:スレッドセーフ検証
class Counter{
    var count: Int = 0;
    init(){}
}

func doAsyncs() {
    let group = DispatchGroup()
    let queue = DispatchQueue(label: "queue", attributes: .concurrent)

    let loop = Loop();
    let cnt = Counter();

    // 並列処理させるタスク
    // 共用のカウンタをインクリメントする
    // (微妙な動きさせたいので、一旦共用カウンタは排他制御なし）
    // 共用のカウンタが規定値超えたところでループを抜ける。
    let task = { (taskNo: Int) in
        var i = 0
        loop.forEach { l in
            if cnt.count > 100000 {
                l.cancel()
                // ループ抜けた時のタスクNoとタスク内カウンタを表示
                print("taskNo:\(taskNo)   i:\(i)")
                return
            }
            cnt.count += 1
            i += 1
        }
    }

    // タスク10個作って実行
    for i in 1 ... 10{
        let work =  DispatchWorkItem {
                task(i)
                group.leave()
            }
        group.enter()
        queue.async(execute: work)
    }

    // 全タスク終了で共用カウンタの値出力
    group.notify(queue: .main) { print("result=\(cnt.count)") }
}

doAsyncs()
```

```:出力結果
taskNo:7   i:10589
taskNo:2   i:10333
taskNo:10   i:10380
taskNo:4   i:10383
taskNo:1   i:10516
taskNo:3   i:10508
taskNo:6   i:10419
taskNo:8   i:10400
taskNo:9   i:10479
taskNo:5   i:10557
result=100001
```
- `iの合計値＞ループ終了条件値`なのは共用カウンタのインクリメントをアトミック操作してないため……。
- Loopのインスタンスはタスクごとに別にしても、共用にしても結果変わらず。
- そもそも終了条件をみたしているときはループを抜るので、問題にならないのか。
- そうなると`_loopVal = nil`の操作がアトミックかを考えればいいだけかな？
- Swiftがそのへんどうなのかは、また今度調べよう。
SwiftPMでQuickを入れたら"No such module 'Quick'" というエラーが出た話

[iOSテスト全書](https://peaks.cc/books/iOS_testing)を読んでいると、第５章に「BDDによるアプリ開発」というQuick/Nimbleを利用したチュートリアルがあったので、早速やろうとしたらライブラリ導入の時点でつまづきました。

初歩的すぎて呆れる方もいるかもしれませんが、初心者の自分はこれで3時間以上溶かしてしまったので、同じ様な初心者の方にお役に立てれば幸いです。Qiita初投稿のため、内容に誤り等あればコメントでご指摘いただけると助かります。

ちなみに、簡単にSwiftPMの話もしています。

# 環境
MacOS Mojave 10.14.6
Xcode11.2.1
Swift5

# まずライブラリを入れる
「iOSテスト全書」内ではCocoaPodsとCarthageでの導入手順が記載されていますが、私はSwiftPMで入れてみました。Xcode11からはGUIでパッケージ管理できるので便利ですね。
まずはXcodeのメニューから[File]→[Swift Packages]→[Add Package Dependency...]を選びます。
![スクリーンショット 2020-02-08 17.22.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/d66982a9-4b5f-0afe-46d1-484cda4ecfc7.png)

すると、githubのリポジトリを検索するウインドウが出てくるので、お目当の「Quick」を検索します。ちなみに、github以外にあるものでもURLを指定すればパッケージを入れることができるそうです（私は未検証）。
![スクリーンショット 2020-02-08 17.16.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/a4694ce4-c108-4268-672e-cc85c7bc213a.png)

バージョンやブランチを聞かれます。今回は特にこだわらないのでそのままNextを押します。
![スクリーンショット 2020-02-08 17.17.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/e9a6761f-86c8-90ad-af38-c96cef846165.png)

次は、ライブラリをどのターゲットに対して追加するかを聞かれます。デフォルトでは実際のソース（今回はqiitasample）が選ばれていますが、Quickはテストコードで使いたいライブラリなので、忘れずにテストコードの方（qiitasampleTests）を選びましょう。
![スクリーンショット 2020-02-08 17.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/4c7b56af-0d67-ed20-9248-695a21b71e23.png)

すると、Xcodeの左側に追加したパッケージと、その依存関係にあるパッケージが追加されます。Quickを入れただけでSwiftPMがNimbleも落としてきてくれたみたいですね。
![スクリーンショット 2020-02-08 17.29.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/02f98160-43fb-05f3-44e9-91cda516cb51.png)


# いきなりエラー
早速Quick/Nimbleを使ってテストコードを書いていきましょう！まずはQuickをimportして...
![スクリーンショット 2020-02-08 17.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/8fd377d0-6087-b624-46fd-146da89a3a00.png)
それらしいものがサジェストされません。もう不穏ですね。
とりあえずQuickと手打ちしてビルド（Controll＋B）してみると...
![スクリーンショット 2020-02-08 17.36.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/1c7b717c-5a02-8f2d-dac0-7f8cc822c4d1.png)
`No such module Quick`
と言われてしまいました。いや、あるじゃん。
この後プロジェクトをクリーンしてみたり、エラー名でググってみたりしましたが、それらしい解決法に出会えずハマってしまいました...

# 解決法
どうにかできないかと思いXcodeのメニューを見ていると、[Product]の中に[Build for...]という項目を見つけました。よく見ると...
![スクリーンショット 2020-02-08 17.41.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439025/3e60bf90-71aa-5da0-c74d-cfaec074270a.png)
Build For Testingがある！ここでようやく、Controll＋Bではテストコードに対するビルドが行われていないことに気付きました。確かにXcode上部のバーにQuickがビルドされているような表示は出ていなかった気がする。
Build For Testingを実施すると、QuickやNimbleにもビルドがかかり、無事エラーも解消されました。


# まとめ
- テストコードに対してパッケージを追加した時は、Build For Testingを実行する。
- ググってもわからないからといって諦めず、メニューをよく見てIDEの機能をよく確認する。
kintone 計算項目のしくみ

kintone の計算項目にIF関数が導入されて、だいぶ標準機能だけでも出来ることが増えてきました。
この機会に計算項目について、これまで分かったことをまとめてみます。

# 計算式を書ける項目タイプ

計算式を書ける項目は、文字列(1行)と計算項目があります。

- 文字列(1行)：　計算結果は、文字列
- 計算：　計算結果は、指定した書式で表示

<img width="1108" alt="2020-02-08_16h42_29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/4986acba-e31d-22e2-64a2-2715417e8ceb.png">

# 計算式の設定

計算式には、フィールドコードを指定して式を設定します。

- 計算式の例： 数値1 * 数値2

<img width="1048" alt="2020-02-08_16h46_08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/3677881b-40a5-2f2d-3e98-3e5761c60ef1.png">

- 計算結果

計算項目は、右詰め。文字列項目は、左詰めで表示。

<img width="628" alt="2020-02-08_16h50_41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/1b136d48-5810-571e-7c26-e8ae4ac81168.png">

# API による計算

計算式で使われている項目の値が変わると、API が実行され、画面に反映されます。
たぶん、ブラウザによるレコード変更と REST API によるレコード変更を共通のしくみで計算するために、API が使われているのではないかと思われます。

ただし、API なので当然タイムラグが多少発生します。
計算項目に値変更時のイベントが無いのも、この辺が影響していると思われます。
イベント処理で、計算結果を JavaScript で使おうとしても、更新前の古い値だったりするので注意が必要です。


<img width="1127" alt="2020-02-08_16h54_12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/69439d4c-1063-7726-5805-39c3bf4cb5fc.png">


# 計算項目を編集可にできるか？

なんとなく、編集可能にできるような気がして、試してみました。

```.js
(function() {
  'use strict';
  kintone.events.on(['app.record.create.show','app.record.edit.show'], function(event) {
    var record = event.record;
    record['計算1']['disabled'] = false;
    record['文字列1']['disabled'] = false;
    return event;
  });
})();
```

試した結果、計算項目は、編集不可。文字列項目は、編集可能になる。
ただし、文字列項目の計算結果を変更すると、API が実行され、再計算される。
まあ、そうなんだといったところですね。

<img width="1127" alt="2020-02-08_17h26_39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/01895db8-6d0c-5d9f-fd8d-ebf91443c221.png">
pandasのdataframeでグルーピングしてランク付けし、グループ項目、ランキングで並べ替える。

# サンプルデータとコードです。　groupby とrank の操作例です。

## サンプルデータ
```python
data=[
{"氏名":"山田","回数":1, "国語":10 ,"算数":20 },
{"氏名":"山田","回数":2, "国語":20 ,"算数":30 },
{"氏名":"山田","回数":3, "国語":30 ,"算数":40 },
{"氏名":"山田","回数":4, "国語":40 ,"算数":50 },

{"氏名":"鈴木","回数":1, "国語":10 ,"算数":50 },
{"氏名":"鈴木","回数":2, "国語":20 ,"算数":40 },
{"氏名":"鈴木","回数":3, "国語":30 ,"算数":30 },
{"氏名":"鈴木","回数":4, "国語":40 ,"算数":20 },
    
{"氏名":"野田","回数":1, "国語":10 ,"算数":30 },
{"氏名":"野田","回数":2, "国語":20 ,"算数":40 },
{"氏名":"野田","回数":3, "国語":30 ,"算数":20 },
{"氏名":"野田","回数":4, "国語":40 ,"算数":50 },
]

import pandas as pd
df=pd.DataFrame(data)
print(df.to_markdown()) 
```

|    | 氏名   |   回数 |   国語 |   算数 |
|---:|:-------|-------:|-------:|-------:|
|  0 | 山田   |      1 |     10 |     20 |
|  1 | 山田   |      2 |     20 |     30 |
|  2 | 山田   |      3 |     30 |     40 |
|  3 | 山田   |      4 |     40 |     50 |
|  4 | 鈴木   |      1 |     10 |     50 |
|  5 | 鈴木   |      2 |     20 |     40 |
|  6 | 鈴木   |      3 |     30 |     30 |
|  7 | 鈴木   |      4 |     40 |     20 |
|  8 | 野田   |      1 |     10 |     30 |
|  9 | 野田   |      2 |     20 |     40 |
| 10 | 野田   |      3 |     30 |     20 |
| 11 | 野田   |      4 |     40 |     50 |




## 回数別の算数上位者を求める。(groupby とrank のサンプル)
```python
import pandas as pd
df=pd.DataFrame(data)
#print(df.to_markdown()) 

#回数別の算数上位者
df["回数別算数順位"]=df.groupby(["回数"])["算数"].rank(ascending=False)

#見やすいように表示順、カラム位置を修正
df.sort_values(["回数","回数別算数順位"],inplace=True)
df=df[[ '回数' , '回数別算数順位' , '算数' , '氏名'  ]]

print(df.to_markdown()) 

```
## 結果
|    |   回数 |   回数別算数順位 |   算数 | 氏名   |
|---:|-------:|-----------------:|-------:|:-------|
|  4 |      1 |              1   |     50 | 鈴木   |
|  8 |      1 |              2   |     30 | 野田   |
|  0 |      1 |              3   |     20 | 山田   |
|  5 |      2 |              1.5 |     40 | 鈴木   |
|  9 |      2 |              1.5 |     40 | 野田   |
|  1 |      2 |              3   |     30 | 山田   |
|  2 |      3 |              1   |     40 | 山田   |
|  6 |      3 |              2   |     30 | 鈴木   |
| 10 |      3 |              3   |     20 | 野田   |
|  3 |      4 |              1.5 |     50 | 山田   |
| 11 |      4 |              1.5 |     50 | 野田   |
|  7 |      4 |              3   |     20 | 鈴木   |


#参考
[pandas.core.groupby.DataFrameGroupBy.rank](https://pandas.pydata.org/pandas-docs/version/1.0.0/reference/api/pandas.core.groupby.DataFrameGroupBy.rank.html)
テーブルのデータをCSV形式で高速に出力する小ネタ

# 記事の内容
以前、同僚からテーブルのデータをCSV出力するのに時間が掛かるという話をされたので、ちょっとした内容ですがアドバイスしました。

時間の計測まではしていなかったので、どのくらい早くなるのか簡単な検証も踏まえてメモを残します。

# 前提
・テーブルの内容は全データそのまま出力する
・PostgreSQL 12.1

# 用意データ
準備したデータは以下のidだけ違うデータを1000万件用意しました。

```
-[ RECORD 1 ]--------
id     | 1
data1  | aiueo
data2  | kakikukeko
data3  | sasshisuseso
data4  | tachitsuteto
data5  | naninuneno
data6  | hahihuheho
data7  | mamimumemo
data8  | yayuyo
data9  | rarirurero
data10 | wawon
```

# 出力方法１

出力方法１は全データ取得して、プログラムの中でCSV形式に変換するという内容です。

SQL

```
select * from demo
```

出力コード

``` csv_out.py
import psycopg2
import time

def get_connection():
    return psycopg2.connect("host=localhost port=5432 dbname=sampledb user=postgres password=postgres")
    
path = "csv1.csv"
with get_connection() as conn:
    with conn.cursor() as cur:
        start = time.time()
        cur.execute("select * from demo")
        exec_time = time.time() - start
        print(exec_time)
        start = time.time()
        with open(path, "w") as f:
            i = 1
            for row in cur:
                csv = ''
                for data in row:
                    csv = csv + str(data) + ','
                f.write(csv[:-1] + '\n')
                
                if i % 1000 == 0:
                    f.flush()
                i += 1
        exec_time = time.time() - start
        print(exec_time)                
```

# 出力方法２
SQLでCSV形式に変換しておく方法です。

SQL

```
select 
    id | ',' | 
    data1 | ',' | 
    data2 | ',' | 
    data3 | ',' | 
    data4 | ',' | 
    data5 | ',' | 
    data6 | ',' | 
    data7 | ',' | 
    data8 | ',' | 
    data9 | ',' | 
    data10 as data
from demo 
```

出力コード

``` csv_out2.py
import psycopg2
import time
def get_connection():
    return psycopg2.connect("host=localhost port=5432 dbname=sampledb user=postgres password=postgres")
    
path = "csv2.csv"
with get_connection() as conn:
    with conn.cursor() as cur:
        start = time.time()
        cur.execute("select id || ',' || data1 || ',' || data2 || ',' || data3 || ',' || data4 || ',' || data5 || ',' || data6 || ',' || data7 || ',' || data8 || ',' || data9 || ',' || data10 as data from demo")
        exec_time = time.time() - start
        print(exec_time)
        start = time.time()

        with open(path, "w") as f:
            i = 1
            for row in cur:
                for data in row:
                    f.write(data + '\n')
                if i % 1000 == 0:
                    f.flush()
                i += 1
        exec_time = time.time() - start
        print(exec_time)
```

# 出力結果
結果は以下の通りです。

|処理  |出力方法１  |出力方法２  |
|---|---|---|
|SQL  |11.68s  |13.35s  |
|ファイル出力  |56.95s  |15.05s  |

出力方法２の方が早く出力できました。

# 感想
個人的にはもう少し早くなるかなと思ってましたが、出力カラム数が少ないのでこんなものかもしれません。
出力するカラム数が増えるほど効果が出てくるやり方です。

DBサーバに接続してCSV出力をするのであれば、ExportコマンドやCOPY TOコマンドなどの方が早く出力が出来ると思います。
ただ、業務システムの保守開発ではテーブルのデータをそのまま出力するという処理は割とあるので、そういった場合にこのやり方を使っています。
AWS S3のCSVファイルをシンプルに読み込むPythonコード

# 概要
- AWSのS3バケットに保存されたCSVファイルをパッと読み込んで使うコードです
- 難しいことはしません
- 自分用スニペットです
- 一旦ローカルファイルに保存しないので、巨大なファイルはメモリ注意ですね
    - 巨大ファイルの場合は素直にローカルにダウンロードするのが良いと思われます
    - 巨大な場合は全列必要でない可能性も考慮するとS3 Selectも検討範囲になるかもしれません

# コード

```python:csv_from_s3.py
import io
import csv
import boto3


def get_s3file(bucket_name, key):
    s3 = boto3.resource('s3')
    s3obj = s3.Object(bucket_name, key).get()

    return io.TextIOWrapper(io.BytesIO(s3obj['Body'].read()))


for rec in csv.DictReader(get_s3file('my-bucket-name', 'test.csv')):
    print(rec)
```

# 確認
- awscli使います

## テスト用CSV作成
```bash
$ cat << EOF > test.csv
id,name,price
1,apple,100
2,orange,200
3,grape,300
EOF
```

## S3バケット作成とアップロード

```bash
$ aws s3 mb s3://my-bucket-name
$ aws s3 cp test.csv s3://my-bucket-name/
```

## コード実行
- サンプルとしてDictReaderを使っているので、dict型になっています

```bash
$ python csv_from_s3.py
{'id': '1', 'name': 'apple', 'price': '100'}
{'id': '2', 'name': 'orange', 'price': '200'}
{'id': '3', 'name': 'grape', 'price': '300'}
```
Python初心者です。どこがエラーか教えていただけないでしょうか？

user_reply=input("ロボットは好きですか?(yes,no,maybe,laserで答えてください)")
if user_reply=="yes":
    print("ピー　ブー!")
elif user_reply=="maybe":
    print("はっきりしろよ人間!")
elif user_reply=="laser":
    print("ビーーーーム!")
elif user_reply=="no":
    print("いいもん。ロボットも君が好きじゃないから。")
elif:
    print("君たち人間のくだらなさにはあきれるよ。")
Rails new で造られるフォルダの意味

ディレクトリ	用途
app/	モデル、ビュー、コントローラ、ヘルパーなどを含む主要なアプリケーションコード
app/assets	アプリケーションで使うCSS (Cascading Style Sheet)、JavaScriptファイル、画像などのアセット
bin/	バイナリ実行可能ファイル
config/	アプリケーションの設定
db/	データベース関連のファイル
doc/	マニュアルなど、アプリケーションのドキュメント
lib/	ライブラリモジュール
lib/assets	ライブラリで使うCSS (Cascading Style Sheet)、JavaScriptファイル、画像などのアセット
log/	アプリケーションのログファイル
public/	エラーページなど、一般(Webブラウザなど)に直接公開するデータ
bin/rails	コード生成、コンソールの起動、ローカルのWebサーバの立ち上げなどで使うRailsスクリプト
test/	アプリケーションのテスト
tmp/	一時ファイル
vendor/	サードパーティのプラグインやgemなど
vendor/assets	サードパーティのプラグインやgemで使うCSS (Cascading Style Sheet)、JavaScriptファイル、画像などのアセット
README.md	アプリケーションの簡単な説明
Rakefile	rakeコマンドで使えるタスク
Gemfile	このアプリケーションに必要なGemの定義ファイル
Gemfile.lock	アプリケーションで使われるgemのバージョンを確認するためのリスト
config.ru	Rackミドルウェア用の設定ファイル
.gitignore	Gitに取り込みたくないファイルを指定するためのパターン
表 1.2: デフォルトのRailsフォルダ構造まとめ
Railsチュートリアルの主なリナックスコマンドまとめ

説明	コマンド	コマンド例
ディレクトリ内容の表示	ls	$ ls -l
ディレクトリの作成	mkdir <ディレクトリ名>	$ mkdir environment
ディレクトリの移動	cd <ディレクトリ名>	$ cd environment/
上のディレクトリに移動		$ cd ..
ホームディレクトリに移動		$ cd ~ もしくは $ cd
ホームディレクトリ直下のenvironmentに移動		$ cd ~/environment/

ファイルの移動やリネーム	mv <移動元> <移動先>	
$ mv foo bar　mv <現在の名前> <変更後の名前>
	
ファイルのコピー	cp <コピー元> <コピー先>
	　　　　　$ cp foo bar

ファイルの削除	rm <ファイル名>	$ rm foo
空のディレクトリの削除	rmdir <ディレクトリ名>	$ rmdir environment/
中身のあるディレクトリの削除	rm -rf <ディレクトリ名>	$ rm -rf tmp/
ファイルの内容の結合と表示	cat <ファイル名>	$ cat ~/.ssh/id_rsa.pub
表 1.1: 主要なUnixコマンド
【Katalon Studio】デフォルトのブラウザを変更する方法

# 動作確認環境

- macOS Catalina バージョン10.15.3
- Katalon Studio Enterprise 7.2.1

※ ダークテーマにしているため、デフォルトの見た目とは異なります。

# 設定方法

1. 「Project」 → 「Settings」を選択します。すると、Project Settings が開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233011/d8fc091b-1fc7-4efc-7593-0509e829f12b.png)

2. サイドバーから「Execution」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233011/36ba0406-0e55-454d-d086-e8767d6e4959.png)

3. 「Default execution」を任意のブラウザに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233011/9d85e2e2-61df-1596-6bfa-56b0eacee04d.png)

4. 変更したら 「Apply」ボタン → 「OK」ボタンで完了です。

# 終わりに

日本語の検索で出てこなかったので…

# 参考文献

[How to change the default browser in Katalon Studio - Web Testing - Katalon Community](https://forum.katalon.com/t/how-to-change-the-default-browser-in-katalon-studio/6642)
CloudWatchのLogGroupをpecoで選択してcliで見る、awslogsの拡張シェル書いた


# TL;DR

AWSのCloudWatchのログをPECOで絞り込んで参照できます。

![e6506d60438a230b7c7f8fffce0c6fdb.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96286/215524c7-0d87-cf16-2171-aa418d1ad03a.gif)


# どうなっているか？

[awslogs](https://github.com/jorgebastida/awslogs)のラッパーになっています。

```sh
# 抜粋
awslogs get $(awslogs groups $AWS_PROFILE_OPTION | peco) $AWSLOGS_PURE_OPTIONS
```

業務で使うことを想定しているので、 `--profile`オプションにも対応しています。
（もともと `awslogs` が対応している、というだけの話ですが）

ソースはこちら。スターください！
https://github.com/hiroga-cc/cli/blob/master/awslogx/awslogx
ラズベリーパイのOSのRaspbian導入エラー

#はじめに#
ラズベリーパイ４Bの初心者セットを購入しました。今思えば技適マークがついてないもの買ってしまったなと少し後悔。
私が購入したものは以下のラズパイ入門セット。
https://www.amazon.co.jp/gp/product/B07ZRF5BC1/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

#やってしまったこと#
ネットで調べながら組み立て、Raspbianを別パソコンに入れてSDカードにコピー…。コピーには以下のDDWinというフリーソフトを使用しました。
https://www.si-linux.co.jp/techinfo/index.php?DD%20for%20Windows

普通ならば…正常にコピーできるですが何らかのエラーが発生した模様。SDカード移行中に”フォーマットしますか？”みたいなメッセージがでたときにYESを押しちゃったのが悪かったのかと思います。ま、まぁうまくいっているだろうと、気になりながらもラズパイにSDカード入れてスイッチオン！ラズベリーマークが出てきてテンションマックスになるも以下の画面から動かない…。

![unnamed.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481632/3040e8cf-c91e-68d2-131e-117baa5f02b6.jpeg)

**”end kernel panic”とかいうのが出てしまっている!!。**

#対策#
色々調べてみましたが、結局SDカードを完全フォーマットしました。ただ、普通にwindowsでフォーマットしようとしてもうまくいかない…。MiniTool Partition Wizardというフリーソフトでフォーマットしました。
https://forest.watch.impress.co.jp/library/software/partwizhome/
以下はフォーマット後に再度OSを入れた後の画面ですけど、こんな感じです。
![mini tool partition wizard.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481632/c0f9f3f1-676b-294f-1d14-fd6398d602f4.png)

#結果#
フォーマット&Raspbianを再度コピーし、無事に導入成功。
![IMG_3037.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481632/1d5b3b5e-3d77-dd85-9e51-92b4ae6b29f5.jpeg)

#さいごに#
新しくSDカードを購入して対応したりしている方もいるようなのですが…僕みたいに夜中に一人でコツコツやってる人にとっては新しいSDカードを買いに行く時間すら節約したいし、Amazonで注文して翌日まで待つのも惜しい。僕みたいな初心者で一人コツコツ派の助けになれば幸いです。
Laravelをインストールしたときにまずやること(ログ設定変更)

# TL;DR

`config/logging.php`の設定を確認すること。

# stackのchannelsをdailyに変更

Laravelをインストールして、私がまずやるのはログの設定を変更することです。
ログの設定は`config/logging.php`にまとめられています。
`single`や`daily`、`slack`などがあり、デフォルトは`stack`で、`stack`は複数のログを`channels`でまとめることができます。

最新のLaravel 6.12.0では、`stack`の`channels`は`['single']`となっていました。
私はこの`single`を`daily`に変更します。

```config/logging.php
...

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['daily'],
            'ignore_exceptions' => false,
        ],

...
```

古の昔(バージョン4時代)も今と同じく`single`だったのですが、最近はデフォルトで`daily`の記憶があります。
調べたら、5.8.35では`stack`のデフォルトは`daily`、
6.5.2まで`daily`、6.8.0で`single`に戻っていました。
何故に戻った。。。:sweat_smile:
`single`のままだと、単一のログファイルにログが溜まっていき、巨大なログファイルを生成してしまいます。
しかも削除されません。
これが`daily`だとログローテーションで一定期間後には削除されます。
膨大なログを想定しているなら、それ相応のロギングシステムを外部サービス含めて検討すべきですが、
とりあえずは`daily`にしておくのが良策かなと思います:thumbsup:

# php_sapi_nameでログファイルを分ける

さらにですが、Webアクセスとコンソールコマンドで出力先ログファイルを分けます。

```config/logging.php
...
        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel-'.php_sapi_name().'.log'),
            'level' => 'debug',
            'days' => 14,
        ],
...
```

例えば`artisan`コマンドでエラーがログファイルに出力された後に、Webアクセスでエラーが発生した場合、ログファイルに書き込み権限がなくて書き込めないエラーが発生します:cry:
それが上記のように変更することで、`artisan`コマンドでのエラーは`laravel-cli-YYYY-MM-DD.log`、
Webアクセスからのエラーは`laravel-fpm-fcgi-YYYY-MM-DD.log`に出力されます(Apacheだと`apache2handler`になります)。
しかもログファイルによってエラー発生元が切り分けられて調査も楽になるので、皆さんも是非！！
BCD符号について

#B.C.D符号について
__B.C.D符号 (Binary Coded decimal code)は*符号体系のひとつで、二進化十進符号とも呼ばれる。__

*符号体系:人間界の言葉(10進法)とからコンピュータが理解できる言語(2進法)へと変化する符号化(エンコード)とその逆の操作となる復号化(でコード)のことである。

#B.C.D符号の例
まず。記述方法を234(10)==>10進法の234とする。

十進数の各桁を2進数の4ビットで記述。
例えば、

__234(10) = 0010 0011 0100 (B.C.D.)__　と記述できる。(下記に詳細記述)


・2 (10) = 0010 (2)
・3 (10) = 0011 (2)
・4 (10) = 0100 (2)

このとき、__1010~1111 (10~15)は不適切表現となる__。各桁1桁しか存在しないので…

#B.C.D符号の加算アルゴリズム
##普通の計算(人間界)
例）684＋277 = 961について

　　684 (10, B.C.D)
　+ 277 (10, B.C.D)
　--------------------
　　__961 (10, B.C.D)__　　←Answer!

このひっ算は単なる10進数の人間の数学界における常識的な算術方法である。しかし2進法のコンピュータには理解ができない。では、B.C.Dを用いることによりどのように計算できるか解説する。

##コンピュータでの計算方式
10進数の数をそれぞれB.C.Dで書きなおしてみる。

__684 (10) = 0110 1000 0100 (B.C.D)__
__277 (10) = 0010 0111 0111 (B.C.D)__

下位4ビット同士の計算から始める。

__0100 + 0111__

　＊0100 : 684(10)の4(1の位)を2進数で表したもの
　＊0111 : 277(10)の7(1の位)を2進数で表したもの


　　0100
　+ 0111 
　---------
　　1011

これは、2進法計算によりできる。このとき、__1011 (2) = 11 (10) となってしまい、不適切表現となってしまう__ので、この場合にはさらに、
<br>
__+6(10) = +0110 (2)__

　　1011
　+ 0110 
　---------
　  __10001  =  1(10)__

<br>
の加算を行う。
どうしてこの加算を行うかというと、
10~15(1010~1111)に対して、+6(10) = +0110(2)を行うことで、必ず、2進数では4桁から5桁に桁上りがわかる状態となることが明白となる。あくまで、桁上りがするかどうかを見たいので、数字に対しては、影響は持たない。
<br>
これを次の桁においても同様のことをする。

__1000 + 0111__

　＊1000 : 684(10)の8(10の位)を2進数で表したもの
　＊0111 : 277(10)の7(10の位)を2進数で表したもの


　　1000
　+ 0111 
　+ 0001   ←桁上り分
　---------
　  10000

この数字においても10以上の数字を表すので、0110を加算。

　　10000
　+　0110 
　---------
　  __10110  =  '6' (10)__
<br>
さらにもう一回。
__0110 + 0010__

　＊0110 : 684(10)の6(1の位)を2進数で表したもの
　＊0010 : 277(10)の2(1の位)を2進数で表したもの

　　0110
　+ 0010
　+ 0001 
　---------
　　__1001  =  '9' (10)__
桁上りなしなので、この桁は、終了。

よって、今までの各桁で出てきた数字を1の位から並べると、
__9 6 1__となるので、人間界と同じ数が算出できる。

#さいごに
符号体系の一つのB.C.Dについてちょっとだけまとめた。桁上りの部分だったりちょっと難しいけど、理解さえしてしまえばそんなに難しいことはしてない。わからなかったら、違うページでググってくれ。(笑)
[Oracle Cloud] Oracle DB のマネージドサービス (Database Cloud Service) に、令和パッチを手動適用してみた

Oracle Cloud Infrastructure(以下OCI)には、Oracle DB のマネージドサービスがいくつかあります。

今までオンプレミスで使ってきた Oracle DB と同じような操作感で扱うことのできる Database Cloud Service や、パフォーマンスチューニングやアップグレードなど様々な自動機能が備わっている Autonomous DB があります。今回は、Database Cloud Service に令和パッチを適用する方法を紹介します。

令和パッチを適用することで、Oracle DB 側で次のような SQL を実行したときに、令和XX年と表示されます。
DI（Dependency Injection）に使われる「依存性」とは？

#DI（Dependency Injection）とは

- 依存性の注入 or オブジェクトの注入
- spring frameworkの「コア」となっている
- クラス間での依存関係を減らし、外部からいれる(注入する)ようにするデザインパターンの一つ


##メリット
- 依存性が少ないため、コードの書き換えがしやすい( == 変化に強い)
- 依存性が少ないため、単体テストがしやすい
- クラス別でやり方を決めるため、変える場所がわかりやすい


#依存性って何？(疑問)
- 相手が変更されたときにうける影響
- クラスとクラスの関係性・結びつき

##依存性が強い

```kotlin:qiita.kt

class AttackMethod(){
    fun attackhand(): String{ 
        return "素手で攻撃しました" // 返り値が「文字列」の型で返す
    }
}

class Player(){ 
    fun playerAttack(): String{ // 
        val a = AttackMethod() // AttackMethodクラスをa変数に代入
        return a.attackhand() // attackhand関数を呼び出す
    }
}


fun main(args:Array<String>){
    println("プレイヤーが攻撃します")
    val player = Player() // Playerクラスをplayer変数に代入
    println(player.playerAttack()) //playerAttack関数を呼ぶ
    
}
```

**「依存性が強い」**と呼ばれる箇所は

Playerクラス　と　AttackMethodクラス


##理由：　目的のクラスを変更するときに、他のクラスから修正しないといけない！！

```kotlin:qiita.kt

class AttackMethod(){
    fun attackhand(): String{ 
        return "素手で攻撃しました" // 返り値が「文字列」の型で返す
    }
}

class Player(){ 
    fun playerAttack(): String{ // 
        val a = AttackMethod() // AttackMethodクラスをa変数に代入
        return a.attackhand() // attackhand関数を呼び出す
    }
}
```

例えば・・・・

1. 「やっぱり、Playerクラスでは、"素手"と"ダメージ量"も返したい。
2. そうすると、playerAttack関数の返り値を変更しないといけないな。

3. けど、AttackMethodクラス内のattackhand関数を変更しないと作業ができないぞ・・・・ 

Playerクラス内で、AttackMethodクラスのインスタンスを使いたい場合は、AttackMethodクラスが使えるような状態にしておく必要がある。
つまり、PlayerクラスはAttackMehodクラスに**依存している**と言える!!（かな・・・・)


#まとめ
「DI」の日本語での意味は、「依存性の注入 or オブジェクトの注入」。
「依存性」とは、クラスとクラス間の関係性のこと。

「依存性が強い」とは、あるAクラスでBクラスのインスタンスを使用したいときに、Bクラスの実装が終えないとAクラスが使えない状態・Bクラスを修正しないといけないということ

「依存性の注入」の「依存性」とは、処理を分散・使いやすくするために、クラス間の依存関係をなるべく減らすようにしようという意味


※「依存性」についての理解をコードで表してみました。間違い・疑問点があれば、ご指摘ください。


#参考リンク(ありがとうございます!!）
- https://www.slideshare.net/yuiito94/di-56742600
- https://teratail.com/questions/125025
- https://morizyun.github.io/blog/dependency-injection-java-basic/index.html
- https://www.eeb.co.jp/wordpress/?p=105
- https://engineering.linecorp.com/ja/blog/spring-boot-job-report/
- https://qiita.com/ritukiii/items/de30b2d944109521298f#didependency-injection%E3%82%92%E6%AD%A3%E3%81%97%E3%81%8F%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B
- https://www.techscore.com/tech/Java/Others/Spring/1-2/
gulpfile.babel.jsの例

# gulpについて

gulはタスクランナーである


## gulpの下準備
gulpはnpmで動くので[パッケー管理ツールnpmについて](https://qiita.com/KZ-taran/items/29e642307c8e1d75a834)も

gulpを使用するために`npm`で`gulp`とその他必要なタスクをインストールしておく

```sh
$ npm i -D gulp

# 他、必要なタスクもインストールする(例)
$ npm i -D gulp-plumber  # エラーが原因でタスク(watchなど)が強制停止することを防止するモジュール
$ npm i -D gulp-notify  # エラー時デスクトップ通知が行えるモジュール
$ npm i -D browser-sync  # css更新時リロード

```


## タスク作成

- `gulp`のインストールができたらタスクの処理を書いていく`gulpfile.js`を手動で作成する
- `babel`を使う場合は`gulpfile.babel.js`とする
- タスクはversionが変わるとタスク名が変わったりするため必ずnpmで検索引用する

gulpfile.babel.js例

```javascript:gulpfile.babel.js
// gulp version4.0 からcallbackが必要となり明示的にタスクの終了を示す必要がある
// 関数の引数に done を入れ、関数最後に done() を追記する必要がある

import gulp from 'gulp';
import cleanCSS from 'gulp-clean-css';
import sass from 'gulp-sass';
import changed  from 'gulp-changed';
import imagemin from "gulp-imagemin";
import notify from 'gulp-notify';
import plumber from 'gulp-plumber';

// gulpタスクの作成
// gulp.task()を使う
// 第一引数に任意のタスク名、第二引数に実行したい処理をfunction関数で渡してあげる
// 関数内ではpipe()で処理を繋ぐ（jQueryのメソッドチェーンと同じ）
// 基本的には次のような流れで返り値を与える
// -> return gulp.src(実行元のパス).pipe(パッケージ).pipe(gulp.dest(出力先のパス));

//CSS圧縮  cssファイルが複数ある場合は*で全て指定できる 圧縮なのでファイルの数だけ出力
gulp.task('minify-css', (done) => {
  return gulp.src('src/css/*.css')
    .pipe(cleanCSS({compatibility: 'ie8'}))  //ie8に互換性を持たせるオプション
    .pipe(gulp.dest('dist/css/'));
  done();
});

// defaultで動かすタスクを指定
// defaultに設定しておくとgulpコマンドだけでタスクが実行される
// 書き方は第二引数に配列でタスクを指定する
// gulp.task(‘default’,[‘タスク名’,’タスク名’,’タスク名’,…]);
gulp.task('default', gulp.task('minify-css'));


// sassをコンパイル
sass.compiler = require('node-sass');
gulp.task('sass', function (done) {
  return gulp.src('./src/scss/*.scss')
    .pipe(plumber({errorHandler: notify.onError("Error: <%= error.message %>")}))
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./dist'));
  done();
});


// jpg,png,gif画像の圧縮タスク
// jpg,png画像を圧縮して保存する
gulp.task('imagemin', () =>
gulp.src('src/img/*')
  .pipe(imagemin())
  .pipe(gulp.dest('dist/img'))
  .pipe(changed('./dist/img'))  // 保存先と指定したディレクトリと比較して異なるものだけ圧縮する
  .pipe(imagemin([
    imagemin.gifsicle({interlaced: true}),
    imagemin.jpegtran({progressive: true}),
    imagemin.optipng({optimizationLevel: 5}),
    imagemin.svgo({
      plugins: [
        {removeViewBox: true},
        {cleanupIDs: false}
      ]
    })
  ]))
);

// Gulpを使ったファイルの監視
// watch()を使う
// 第一引数は監視したいディレクトリ配下
// 第二引数に監視しているディレクトリに変更があった場合に実行するタスクを
gulp.task('sass:watch', function (done) {
  gulp.watch('./src/scss/*.scss', gulp.task('sass'));
  done();
});
```

## タスク実行
実行可能なタスクの確認

```ｓｈ
$ gulp --tasks
```

タスクの実行の際はタスク名をターミナルに吐き出す
タスク名は`gulpfile.js`に記述した関数の第一引数
プログラミング講座1

##講座内容
・プログラミングで大事なこと
・解読する

##はじめに
今回から少しずつ講座みたいなものを挙げていきます
言語はJavaを使うものの、言語によらない部分をメインにしていくつもりです

##プログラミングで大事なこと
それは言語化することです

現実問題、一人ですべてのソースコードを書くことはありません
誰かに引き継いで、または誰かの作業を引き継いで、どんどん改修していくものです
なので、第三者が見てわかりやすいこと
改修しやすいことが、とても大切です
すなわち、コードの説明文や設計書などのドキュメントが良ければ良いほど
クオリティの高い開発になっているといえるでしょう


とあるソースコードを書いて、それがどう動くのかを
「道端のおっさん」に説明してみてください
何も知識のない人に急に説明して、理解させるためには

何をしたら、どうしてくれるもの

なのかを、簡潔に伝える必要があると思います
例えば「エアコン」を説明してみましょう

```wikipediaより引用.
エア・コンディショナー（英: air conditioner）とは、
空調設備の一つで、室内の空気の温度や湿度などを調整する機械である。
日本での通称はエアコン（以下「エアコン」と表記）。 
狭義では、冷媒による蒸気圧縮冷凍サイクルの蒸気圧縮冷凍機の
パッケージ・エア・コンディショナーや
家庭用のルーム・エア・コンディショナーのうち、
水以外の熱媒体で熱を搬送する装置、
つまりヒートポンプを指す。 なお「エアコン」は(以下省略)
```
無駄が多すぎで分かりにくくないですか？
いくら正確な情報であっても、理解できなければ意味がありません
(もちろん辞書は正確な意味を載せる必要があるので仕方ありませんが...)

```これでよくない?.
部屋の温度調節できるやつ
```
理解するのに必要なことって、結局それが「なにしてくれんの？」ってことですよね

ただこのままだと、使ったらどうなるのかが分かったけれど
使い方や注意点などが分からないですね
なので

```簡潔な説明.
<エアコンのメイン機能>
リモコンのボタンを押して操作すると、部屋の温度を調節できる

<その他便利機能>
・湿度調節
・入タイマー
・切タイマー

<使用上の注意>
長時間使っていると、エアコン内部のフィルターに汚れがたまります
定期的に掃除してね
```
・どうしたら、何をしてくれるのか
・その他詳細機能
・使用上の注意

を書いてみました

もし使用上の注意点として、「3分使うと爆発する」とかあったら
間違いなく1行目の一番最初に
赤字で大きく書きますよね笑

説明の順番は、情報の価値の順番
重要なことから書いてあればとても分かりやすいです

このようなドキュメントを作ろうとすると、
人が読んでわかりやすいかを意識するために自分の頭も整理できて、いいことずくめです

##解読する
とはいってもね、一度スパゲッティのように絡まりまくったコードが作られてしまうと

開発陣「コードを書きなおしたいので時間ください」
お客さん「何をするんですか？」
開発陣「機能のアプデはないけどコード見やすくします」
お客さん「アプデしないならウチ利益ないんですけど」
開発陣「今後の開発がスムーズになります」
お客さん「そんなことよりこの案件お願いします」
開発陣「ﾃﾞｽﾖﾈｰ」

ってケースが多く、残念ながら改修をする時間がなかなか取れません
(もちろん場合によりますが...)
リファクタリングは、1度の開発フェーズでやり切ってしまいたい
なるべくごちゃまぜコードを後世に残したくないですね

で、じゃあそんなスパゲッティを改修しなければならなくなった時

...がんばりましょうw

エディタのショートカットで
・メソッド使ってるところにジャンプ
・メソッドを作ったところにジャンプ

ができると思いますが、ドキュメント、javadocなどが優秀だと
メソッドを作ったところにジャンプ　しなくても、説明文呼んだだけで
何をしてくれるメソッドなのか理解できて便利ですね

あまりにもロジックが入り組みすぎている場合は以下のことに注意しながら読み解きましょう
・最初に何をしているか
・最後に何をしたいのか
・細かい条件や、entityにセットする内容などを無視
・結局なにしてるの？
・つまり何してるの？
・で、なにしてるの？

だいたいの処理が
「テーブルからレコード持ってきて、アプデしてDBに戻す」
とか
「画面入力を受け取ってDBを検索して、取れたのを画面に戻す」
みたいなことですよね

基本の骨格を意識しながら読み進めるだけでかなり違って見えるはずです

すなわち
「要約すること」
これが一番大切だと思います

以上、第一回プログラミング講座でした
機械学習(AI)初学者のファーストステップ

#機械学習(AI)初学者のファーストステップ
MicroSoft Azure Machine Learning を使ったGUIベースのAI作成します。

１．とりあえず環境を用意しよう！
２．とりあえず言われるままAIをつくってみよう！

##Azureの無料アカウントを作成

https://azure.microsoft.com/ja-jp/

##Azureにワークスペースを作成

<img width="1054" alt="スクリーンショット (4).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332948/19b09b44-62dc-e9d4-52e0-ff162722b42f.png">
<img width="1067" alt="スクリーンショット (6).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332948/f7ccde30-15f6-a25e-38de-0e210c9243d6.png">
<img width="1076" alt="スクリーンショット (7).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332948/79a1879a-508b-76cd-b9a7-5df806ffa942.png">
※作成したワークスペースが気に入らなかったりして削除した場合はワークスペースだけ削除されるのでプランは前回と同じものを設定してください(無料プランは１個しか設定できない)(もしくはプランも消してください)
⇒(「リソースグループ」や「すべてのリソース」)で探すとよいです。

##Microsoft Azure Mechine Learningを起動する

<img width="1067" alt="スクリーンショット (8).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332948/f1bd3d27-d2d5-c8f0-92b5-b5dfdbb7a695.png">
<img width="862" alt="スクリーンショット (9).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332948/4f306e00-982d-3f92-2769-5c328f4729fc.png">
Sign Inする(ワークスペース作成時にサインインしてるはずなので押せばログインできます。)

##試してみる
https://kinpatucom.tech/azure-ml-mnist/
ここが一番わかりやすかったので提示しておきます。
Node.jsでGoogle Slidesの新規スライド作成


Google Slides APIをNode.jsから触ってみてます。

* [Node.jsでGoogle Slides APIを触ってみる](https://qiita.com/n0bisuke/items/8a343ca4d8fe2f1bac55)
* [Node.jsでGoogle Slides内のテキストを取得してみる](https://qiita.com/n0bisuke/items/325fbe56a48e7d83e8dc)

の記事の続きです。

## Google Slideのcreateを試す

スライドの新規作成が出来そうな雰囲気です。

[presentations.createのドキュメント](https://developers.google.com/slides/reference/rest/v1/presentations/create)を覗くとスライド作成できそうな雰囲気がありました。

## 準備

[Node.jsでGoogle Slides APIを触ってみる](https://qiita.com/n0bisuke/items/8a343ca4d8fe2f1bac55)でAPIへのアクセスなどを出来るようにしましょう。

## ソースコード

前回記事のapp.jsの中身を書き換えます。

* SCOPESを変更

```app.js
const SCOPES = ['https://www.googleapis.com/auth/presentations'];
```

> 元々は'https://www.googleapis.com/auth/presentations.readonly'と書いてあって読み込みのみ許可になってました。

* `listSlides(auth)`の中身を以下に書き換え

```app.js
省略

function listSlides(auth) {
    const slides = google.slides({version: 'v1', auth});
    slides.presentations.create({
        presentationId: '',
    }, (err, res) => {
        if (err) return console.log('The API returned an error: ' + err);
        console.log(res.data);
    });
}
```

## tokenを再発行

`token.json`は読み込みのみ許可のトークンで作成されていたので、一度token.jsonのファイルを削除します。

再度`node app.js`で実行すると、URLが発行されて、ブラウザでアクセスするとこのようにアクセス許可を求められます。

> ![スクリーンショット 2020-02-08 16.41.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/56b58e58-2ca1-1fba-0852-bf34e474d727.png "スクリーンショット 2020-02-08 16.41.22.png")


許可をして、発行されるコードをターミナル側に貼り付けて実行するとtoken.jsonが再発行されて実行できます。

## 新規スライドが出来た！

実行してみます。

```bash
$ node app.js
{
  presentationId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  pageSize: {
    width: { magnitude: 9144000, unit: 'EMU' },
    height: { magnitude: 5143500, unit: 'EMU' }
  },
  slides: [
    {
      objectId: 'p',
      pageElements: [Array],
      slideProperties: [Object],
      pageProperties: [Object]
    }
  ],
  title: '無題のプレゼンテーション',
  masters: [
    {
      objectId: 'simple-light-2',
      pageType: 'MASTER',
      pageElements: [Array],
      pageProperties: [Object],
      masterProperties: [Object]
    }
  ],
  
省略
```

Google Drive上で確認してみると`無題のプレゼンテーション`が作成されてました。

> ![スクリーンショット 2020-02-08 16.43.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/e7188637-1a64-93c8-997f-cdf442cc8e97.png "スクリーンショット 2020-02-08 16.43.15.png")

![](https://i.gyazo.com/367466bec78e9baca73926e704054e21.png)

## 所感

とりあえず、Node.jsからスライドの新規作成ができました。

タイトル指定などをして作ることも出来そうなので引き続き探っていきます。
パッケー管理ツールnpmについて

# パッケー管理ツールnpmについて

npmとはNode.jsのパッケージを管理するツール
Node.jsはJavaScriptでできたWeb Serverであり、開発に便利なツールをnpmを使ってインストールして使う


# npm利用環境

macではnpmをインストールする前に[homebrew](https://brew.sh/index_ja)というmacOS用のパッケージマネージャーのインストールが必要


## npmとNode.jsのインストール

homebrewインストール後に行う
Node.jsをインストールするだけで自動でnpmもインストールされる

```sh
# カレントディレクトリはどこでも良い
$ brew install node.js

# 確認
$ npm -v
$ npm -version
```

# npmを使用するプロジェクト開始
## package.jsonの作成

npmではまずはじめに`package.json`を作成する必要がある
プロジェクトディレクトリに移動し、初期化作業として`package.json`を作成する
`package.json`ファイルが既に存在している場合（他の人からもらったりした場合)は##パッケージインストールから


```sh
# package.jsonの生成
# いくつか質問されるが全部ENTER
$ npm init

# 次の書き方でENTER連打を省略できる
$ npm init -y
$ npm init -yes
```

## パッケージインストール

```ｓｈ
$ npm i
$ npm install

# package.jsonファイル作成しておらず、ファイルへの記述とともにインストール
$ npm i -D パッケージ名
# version指定する場合
$ npm i -D パッケージ名@0.3.17
```

### install時のオプション項目

|オプション |省略 |概要 |
|--- |--- |--- |
|--save |-S | package.jsonのDependenciesへの追記とともにインストール<br>ユーザーがアクセスするようなシステムに必要なもの |
|--save-dev | -D | package.jsonのdevDependenciesへの追記とともにインストール<br>開発の中で必要なもの |
|--global |-g | global領域へのインストール |

- global領域へのインストールでのインストール先
    - mac: `/user/local/lib/node_modules`
    - win: `C:\Users\%USERNAME%\AppData\Roaming\npm\node_modules`
- global領域へインストールする場合はパスを通しておく
    - [パスの通し方](https://qiita.com/KZ-taran/items/0cb2cfa64625ec1b58a9)
    - `export PATH=$PATH:./node_modules/.bin`


## パッケージアンインストール

```sh
# パッケージのみアンインストール
$ npm uninstall パッケージ名
$ npm un パッケージ名
$ npm unlink パッケージ名
$ npm remove パッケージ名
$ npm rm パッケージ名
$ npm r パッケージ名

# package.jsonの記述とともにパッケージアンインストール
$ npm uninstall -S パッケージ名
$ npm uninstall -D パッケージ名

# globalインストールしたパッケージをアンインストール
$ npm uninstall -g パッケージ名
```


# よく使うパッケージリスト

| |パッケージ名 |オプション |概要 |
|--- |--- |--- |--- |
| |browserify |D |モジュール間の依存関係の解決し読み込み順序を気にせず良くなる<br>ビルド(分割された複数のjsファイルを一つのファイルに)<br>requireを使って別のjsを読み込めるようにする<br>scriptタグ減少でサーバーへの通信が減り、高速化 |
| |jquery |S | |
|webpack|webpack |g D |使用するときは`webpack.config.js`を手動で作成し、`package.json`と同階層に保存 |
|webpack|webpack-cli |D |`webpack`と一緒に使う<br>`webpack`コマンドで`webpack`を実行するためにwebkack4より必要となった<br>gulpでwebpackを動かす場合は不要 |
|babel |babel |D |ES6で書けるようにする<br>ES6->ES5へのトランスパイラー|
|babel |babel-loader |D |`babel`と一緒に使う<br>ES6でjsを書いて適切に変換してくれる |
|babel |@babel/core |D |`babel`と一緒に使う<br>ES6でjsを書いて適切に変換してくれる |
|babel |@babel/preset-env |D |`babel`と一緒に使う<br>ES6でjsを書いて適切に変換してくれる |
| |browser-sync |D |ファイル変更を監視し、自動でブラウザ更新する |
|gulp |gulp |D |タスクランナー<br>手動でやっていた処理を自動化する |
|gulp |gulp-clean-css |D |cssファイルを圧縮 |
|gulp |gulp-sass |D |sassファイルをcssへ変換 |
|gulp |gulp-imagemin |D |画像ファイルを圧縮 |
|gulp |gulp-changed |D |他のタスクを使う際に、比較して変更あったものだけ実行する |
|gulp |gulp-notify |D |gulpタスクがエラーになったときに通知表示 |
|gulp |gulp-plumber |D |gulpタスクがエラーになっても止まらずに処理を実行させる |
|gulp |gulp-webpack-stream |D |`webpack`と一緒に使う<br>webpackをgulpで操作するために必要




## 自分用

```sh
#!/bin/sh

npm init
npm i jquery -S
npm i webpack -D
npm i webpack-cli -D
export PATH=$PATH:./node_modules/.bin
npm i babel
npm i babel-loader @babel/core @babel/preset-env -D
npm i gulp -D
npm i gulp-clean-css -D
npm i node-sass gulp-sass -D
npm i gulp-imagemin -D
npm i gulp-changed -D
npm i gulp-notify -D
npm i gulp-plumber -D
```
mysqlのデフォルト値の設定

###やりたいこと
デフォルトのプロフィール画像を設定したい。

###マイグレーションでのデフォルト値設定
カラムにデフォルト値を設定するには**->default('[値]')**をつけるだけです。

```php
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->string('profile_image')->nullable()->comment("プロフィール画像")->default('default_icon.jpeg');
        });
    }
```
profile_imageカラムにdefault_icon.jpegをデフォルト値を設定しました。

あとはストレージにdefault_icon.jpegを置いとくだけ。
クリスタでキャンバスをすばやくチェックするために新規ウィンドウを開きたい



クリスタには、編集中のファイルを新規ウィンドウを開くことで、別の場所に同じキャンバスを表示できます。この機能を利用して、描画中にキャンバス全体や、反転した状態のキャンバスを素早くチェックできます。

便利な機能なのですが、これを毎回手動で行うのは面倒なので、アクションを使って自動化できないか試しました。結果、できませんでした。そこで、AutoHotKey でキー操作を自動化してみました。

##AutoHotKey が初めての方へ

前回の記事「AutoHotKey で、マウスカーソルの座標を調べる」に、利用までの簡単な説明を描きました。こちらをご覧ください。
https://qiita.com/mojajamaru/items/a97f7bfad3238789385c

##スクリプト

cli_open_windowx2_normal.ahk

``` 
; クリスタで開いたイラストを新規ウィンドウで開き、サブモニターに配置する
SetTitleMatchMode, 2
; 生面と、左右反転の2つ
WinActivate, CLIP STUDIO PAINT ;クリスタ をアクティブにする
sleep,1000
; 新規ウィンドウを開く
OpenWindowx2()
return
ExitApp ; アプリを終了
Exit ; AHK を終了
; 新規ウインドウを開く
OpenWindowx2()
{
; 新規ウインドウを開く
	send,{alt}
	sleep,200
	send,w
	sleep,200
	send,v
	sleep,200
	send,n
	sleep,2000

; 新規ウインドウを開く
	send,{alt}
	sleep,200
	send,w
	sleep,200
	send,v
	sleep,200
	send,n
; 表示を左右反転
	send,{alt}
	sleep,200
	send,v
	sleep,200
	send,v
	sleep,200
	send,h
}
```


##動作の概要

WinActive でクリスタをアクティブにします。
あとは、ウィンドウ操作、[Alt] で上部メニューを開き、目的の操作を行います。

##使い方

メモ帳などのエディタを使って、cli_open_windowx2_normal.ahk を作成します。
あらかじめ、クリスタで編集するファイルを開きます。
cli_open_windowx2_normal.ahk を実行します
すると、新規ウィンドウが２枚、正面と、左右反転の二つが開きます。これを、好きな場所に移動させてご利用ください。
Symbolのテストネットのノードを新しいノード構築ツールで立て直した際に発生したトラブルとその対処方法

本記事の情報は古いバージョンの情報に基づいているため、申し訳ありませんが、Symbolのノード関連の情報をお探しの方は、本記事の内容は参照せず、別記事をお探しになることを強く推奨します。

先日以下の記事をQiitaに投稿させて頂きました。

[GCP(Google Cloud Platform)でNEMの次期バージョンSymbolのテストネットのノードを構築する手順](https://qiita.com/salaryman-toushi/items/46a1352fd03ca4ccfa2f)

私自身も、テストネットのノードをたてて維持しているのですが、さすが、現在まさに開発中のSymbolブロックチェーンのテストネットだけあって、新しいノード構築ツールがどんどんリリースされ、そのたびに何回かノードをたてなおす必要が生じました。

その中で新しいノード構築ツールでのノードのたてなおしの流れや、発生したトラブル等の記録をここに残しておこうと思います。

ノードの構築に困ったときにご参考になれば幸いです。間違い等もあるかもしれませんので、何かあればぜひご指摘頂けますと幸いです。

# 古いノード構築ツールで動いているノードを新しいノード構築ツールで立て直す手順

## 大まかな流れ

1. 稼働中のノード(=古いノード構築ツールで動いているノード)を止める
2. (dockerのキャッシュをクリアする)
3. 古いノード構築ツールをフォルダごと消す
4. 以降は通常のノード構築手順と同じ

## 各作業の詳細

### 1. 稼働中のノード(=古いノード構築ツールで動いているノード)を止める

まずは稼働中のノードを停止する必要があります。

1. 現在自分がどのフォルダにいるか確認する(≒lsコマンドを実行し、docker-compose.yamlが表示されるか確認する)
    - docker-compose.yamlがあるディレクトリでないと、2.のコマンドが実行できないと思うので、そのフォルダにいない場合は、cdコマンド等で対象のディレクトリへ移動しましょう。
2. (docker-compose.yamlがあるディレクトリ上で、)以下コマンドを実行する
<pre>sudo docker-compose down</pre>
これで稼働中のノードを停止させることができたと思います。

### 2. (dockerのキャッシュをクリアする)

次にdockerのキャッシュが残っていると意図しない動作となる可能性があるため、キャッシュをクリアしておくと望ましい旨、記載されていましたが、フォルダ毎消す手順の方が望ましいようで、最新のGitHubの手順の説明ページからはこの内容は削除されていました。やらなくてもよいかもしれない？手順ですが、これをやることでトラブルになることはないと思うので、きれいにリセットしたい方はやってみても別に問題は無いと思います。

1. 以下コマンドを実行する
<pre>sudo docker system prune -a</pre>
1. 確認を求められるので`y`と入力し`Enter`

### 3. 古いノード構築ツールをフォルダごと消す

その場合は、消したいフォルダの一つ上のフォルダへ移動し、以下コマンドを実行してください。

ただし、このコマンドは、有無を言わさず該当フォルダを削除してしまうため、間違ったフォルダで実行してしまったり、間違ったフォルダ名を指定して実行してしまうと

> バルス

のように全てを無に帰す破壊力を潜在的に持っています。実行する際は、正しいフォルダ上で、本当に消したいフォルダ名を指定していることを慎重に確認して実行するようご注意ください。

<pre>sudo rm -rf 消したいフォルダ名</pre>

### 4. 以降は通常のノード構築手順と同じ

以降は通常のノード構築と同様の手順でOKです。

# 作業の中で発生したトラブル

## friendly_nameとhostの反映のために設定ファイルに追記しノードを再起動した際にブロック高さの更新が止まってしまうことが何回か発生

この症状がよく発生しました。古いノードを新しいノードに入れ替える要領でノードを再起動すると直るときがあったり、直らないときがあったり色々でした。

色々試行錯誤する中で、そもそも私自身がノード同士の通信で使うポート7900を明示的に開ける処理をしていないことに気づきました。この設定を初回ノード起動前に実施することで、この症状については改善した印象があります。手順を記載した記事の方も7900ポートを明示的に先に開けておくような手順に修正しておきました。もし、ノード再起動でブロック高さの更新が止まってしまった人は、7900ポートが明示的に開けてあるかを確認し、開いていない場合は明示的に開けた後、全部最初からやり直してみると良いかもしれません。

2020/02/11追記
7900番を明示的に開けていても、ノード再起動時にブロック高さの更新が止まる現象が起こるという情報を伺いました。
再起動時、停止していた間に他のノードからの通信が失敗したり、host等の情報が変わって他ノードにて保有されている情報と不一致が発生する等の状況によって、ノードの信頼度のスコアが低下し、ブロックチェーンのネットワークに参加できない状態に一時的になってしまうのでしょうか。その場合、時間をおいてリトライする等の対策が必要かもしれませんが、このあたりはあまり検証できていません。
個人的にはhost, friendly_name等を初回ノード起動前に設定できると、楽でよさそうだな...と感じました。

## フォークしていた？させてしまっていた？

もしかすると、7900番を明示的に開けずにノードをたてていたので、自分のネットワークの中(Googleが暗黙的に7900番での通信を許可している範囲のネットワークの中)だけ、他の全世界のネットワークと異なる全然違う世界だったのでは...という気もしています。(ごめんなさい、きちんと検証はできていません。)ノード運用のハードルは技術的にはかなり低いとはいえ、正確な理解に基づく正確なオペレーションが重要であると感じました。

# まとめ

ノードの再構築については、dockerのキャッシュのクリアという手順が新たに追加されていましたが、その後この手順は公式ページからは削除され、フォルダ毎削除してやり直すという手順が推奨されているように見えました。

なお、トラブルの原因は、現時点では、私の設定もれ(ノード間通信で使う7900ポートの開け忘れ)という印象です。(2020/02/11追記:7900番を開けていても同様の問題が発生した事例はあるようで、他の理由もありそうです。)他にもいろいろあるのかもしれませんので、他にも何か気づいたことあれば、色々追記していこうと思います。

テストネットで色々な問題が明確となり、メインネットが円滑にローンチされ、Symbolブロックチェーンの活用を通じてより良いサービスが実現されることを祈っています。
Cent OS8のコマンドラインでGitを使う為の準備

### 参考
https://xn--o9j8h1c9hb5756dt0ua226amc1a.com/?p=1434
https://qiita.com/kusanoiskuzuno/items/c323446f2707f9950ebb

### Gitの準備
- Gitのインストール

```
$ sudo yum install git
```

- Gitのバージョンを確認

```
$ git version
```

- テストリポジトリを作成

```
$ mkdir /home/ユーザー名/testrepo.git
```

- リポジトリを初期化

`cd`コマンドで先程作成した`/home/ユーザー名/testrepo.git`まで移動して、

```
$ git init
```
を実行する。

### Visual Studio Codeの準備
- rpmをインポート設定

```
$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'
```

- アップデートをチェックして、yumでインストール

```
$ sudo yum check-update
$ sudo yum -y install code
```
その後、Visual Studio Codeを日本語化。
`code`コマンドでVisual Studio Codeを起動する。
Nuxtでvue-carouselを使用していたら'ReferenceError: window is not defined'が出たお話

# 環境
nuxt 2.4.0
vue-carousel 0.18.0

# 現象
nuxtにvue-caroucelを実装したが、下記のようなエラーになり
リロード時に画面がずっと読み込み状態になってしまった。


```
ReferenceError: window is not defined
````

どうやたSSR時に、`document`や`window`にアクセスすると上記のエラーが発生するみたい

[参考サイト](https://github.com/SSENSE/vue-carousel/issues/81)

# 実装方法

```~plugin/vue-caroucel.js
import Vue from 'vue'
import VueCarousel from 'vue-carousel'

Vue.use(VueCarousel)
```

```nuxt.config.js
plugins: [
        { src: '~/plugins/vue-carousel', mode: 'client' }
    ],
```

```index.vue
<template>
  <div class="container">
    <carousel :navigation-enabled="true" :per-page="1">
      <slide v-for="(url, key) in images" :key="key">
        <div class="product-img">
          <img class="product-card-img" :src="url" />
        </div>
      </slide>
    </carousel>
  </div>
</template>

<script>
import { Carousel, Slide } from 'vue-carousel'
export default {
  layout: 'client/simple',
  components: {
    Carousel,
    Slide
  },
  data() {
    return {
      hasError: false,
      images: [
        require('@/assets/img/NoImage.png'),
        require('@/assets/img/NoImage.png'),
        require('@/assets/img/NoImage.png'),
        require('@/assets/img/NoImage.png'),
        require('@/assets/img/NoImage.png'),
        require('@/assets/img/NoImage.png')
      ]
    }
  },
  methods: {
    async login() {}
  }
}
</script>
```

# 解決方法

各画面でのインポートのを下記のように書き換える

```js
- import { Carousel, Slide } from 'vue-carousel'

+ import Carousel from 'vue-carousel/src/Carousel.vue'
+ import Slide from 'vue-carousel/src/Slide.vue'
```

# まとめ
vueのモジュールをNuxtで使用するには色々とトラブルがあってなかなか大変です。。。
SSRについてしっかり理解して進めないと色々ハマっていきそうだと思いました。
キッズがPCB作成にチャレンジ！ 2日目 〜はんだづけして完成、考察〜

「キッズがPCB作成にチャレンジ！ 1日目 〜デザインと設計〜」
https://qiita.com/nanbuwks/items/bc3486348a5cb18090fa
の続きです。


# 2日目  2020年1月26日 約1時間

工場から届きました。
![IMG_20200128_174032.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/df3c4c12-04e2-7ea0-a21a-514ef6d27c94.jpeg)

今回は、コスト削減のために時間のかかる便で届けてもらいました。
また、僕の出張が入ったため2週間空いてしまいました。

パーツを差し込んで
![IMG_20200126_133306.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/bf1b54ab-b9c8-a1d1-8ea1-0c59385f70a6.jpeg)
はんだづけして
![IMG_20200126_133840.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ebbc47b7-50f9-ff4e-9089-261a6df1a8e0.jpeg)
余った足を切って
![IMG_20200126_135753.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/a6f73837-fab6-c809-c548-878c8342af52.jpeg)
動作確認して
![IMG_20200126_141048.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/066f7dca-7d70-ed82-755a-be9224deb4d3.jpeg)
ストラップ付けて完成です！
![IMG_20200126_202416.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/c3cb9aa7-5e2b-1a98-37a7-b7aa23b9ae18.jpeg)




# 考察

難易度：
今回は、小学6年生および小学4年生で試してみました。PC操作にある程度慣れた二人でしたが、次に述べるネックの点で、中学生以上をターゲットにするか、小学生でもうまくできるような工夫を足すことが望ましく思いました。

ネック：
ツールとして、GIMP と KiCad を使いました。回路図を作ったりパターンを作ったりするKiCadの基本操作は小学生でも十分できるようです。しかしながら

GIMP操作で例えば、「表示」-「表示倍率」-「ウィンドウ内に全体を表示」、
KiCadで例えば、「File」-「New」-「Project」

のような操作を、口頭説明だけでやってもらうには小学生には不安です。直感的なUIを備えた代替ツールがあればいいのですがそちらを望むのは難しいので、テキストを工夫するか、自動化スクリプトなどを用いるようにするのが良さそうです。

PC操作について：
今回、PC の OS として Ubuntu Linux を使っています。しかしながら学校の授業などでは Windows に慣れ親しんでいるのかな？
ツールとして使った、GIMP と KiCad はマルチプラットフォームなので環境による操作の違いは致命的ではありません。しかしながらファイル操作は OS による違いがあります。最初の画像を読み込むところ、最後のZIPを作成するところはOS環境を揃えておき、一斉に「右クリックして〜Compressを選んで〜.zipを選んで〜」というように号令をかけるなどをしないと現実的ではありませんでした。

楽しんだ？：
はんだづけが楽しかったそうで、またやりたい！ と何度もせがまれてます。
また作ったものも、ランドセルやリュックに取り付けて使ってもらっていて、気に入っていただけたようです。


![IMG_20200128_173426.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/66d36cc4-af57-3b14-5e56-c1aacbe23a59.jpeg)
Python の gRPC で Redis のデータを削除 (Delete)

設定ファイル、サーバープログラム、クライアントプログラムの３つが必要です。

設定ファイル

```text:redis_delete.proto

syntax = "proto3";

package redis_delete;

service Greeter {
	rpc RedisDelete (RedisRequest) returns (RedisReply) {}
}

message RedisRequest {
	string key = 1;
}

message RedisReply {
	string key = 1;
}
```

サーバープログラム

```py:redis_delete_server.py
#! /usr/bin/python
#
#	redis_delete_server.py
#
#					Feb/08/2020
# --------------------------------------------------------------
import sys
import json
import redis
from concurrent import futures
import logging
import	datetime

import grpc

import redis_delete_pb2
import redis_delete_pb2_grpc

# --------------------------------------------------------------
def redis_delete_proc(key_in):
	rr = redis.Redis(host='localhost', port=6379, db=0)
#
	rr.delete(key_in)
#
# --------------------------------------------------------------
class Greeter(redis_delete_pb2_grpc.GreeterServicer):

	def RedisDelete(self, request, context):
		sys.stderr.write("*** RedisRead ***\n")
		sys.stderr.write("*** key = %s ***\n" % request.key)
		redis_delete_proc(request.key)
		return redis_delete_pb2.RedisReply(key=request.key)

# --------------------------------------------------------------
def serve():
	server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
	redis_delete_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
	server.add_insecure_port('[::]:50051')
	server.start()
	server.wait_for_termination()

# --------------------------------------------------------------
if __name__ == '__main__':
	logging.basicConfig()
	serve()
# --------------------------------------------------------------
```

クライアントプログラム

```py:redis_delete_client.py
#! /usr/bin/python
#
#	redis_delete_client.py
#
#					Feb/08/2020
#
# --------------------------------------------------------------
from __future__ import print_function
import logging
import json
import grpc
import sys

import redis_delete_pb2
import redis_delete_pb2_grpc

# --------------------------------------------------------------
def run():
	key_in = sys.argv[1]
#
	with grpc.insecure_channel('localhost:50051') as channel:
		stub = redis_delete_pb2_grpc.GreeterStub(channel)
		response = stub.RedisDelete(redis_delete_pb2.RedisRequest \
			(key=key_in))
		print("Greeter client received aa: " + response.key)
#
# --------------------------------------------------------------
if __name__ == '__main__':
	logging.basicConfig()
	run()
# --------------------------------------------------------------
```

gRPC のコードを作成します。

```bash
python -m grpc_tools.protoc -I. \
	--python_out=. \
	--grpc_python_out=. \
	./redis_delete.proto
```

サーバープログラムの起動

```bash
./redis_delete_server.py
```

クライアントプログラムの実行

```text
$ ./redis_delete_client.py t0934
Greeter client received aa: t0934
```
AWS APIGatewayに、保有しているドメインを割り当てる方法

APIGatewayをデプロイしただけだと、めっちゃ長い文字の羅列になっているので、
ドメインを割り当てることにしてみました。

#手順
##①ACM証明書（SSL証明書）の発行リクエスト《Certificate Manager》
▼私の設定メモ
パブリック証明書のリクエスト
ドメイン：XXX
DNS検証　※メール検証だと有効期限があり更新忘れの可能性あり。
##②ACM証明書のリクエストの承認（DNS）《Route 53》
DNSレコードを作成し、検証
##③APIgatewayカスタムドメインの設定《APIGateway》
▼私の設定メモ
REST
ドメイン：XXX
リージョン
TLS1.0
ベースパスマッピング（API名:ステージ）
##④DNSレコードに"③"のレコードを追加《Route 53》

#参考
>https://dev.classmethod.jp/cloud/api-gateway-custom-domain-ssl/

以上です。