OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonista3 for iphoneを使って簡単ssh port fowarding

# 用意するもの
- sshサーバー
- iphone
- Pythonista3(IOS)

# 鍵の準備
RSA暗号で秘密鍵と公開鍵を作成します。Ed25519は非対応なので必ずRSA暗号で鍵を作ってください！！PythonistaにStaShが入っていない場合はインストールして下さい。StaShで次のコマンドを実行します。

```
$ ssh-keygen -t rsa -b 4096
```

"~/.ssh/"にid_rsa(秘密鍵)とid_rsa.pub(公開鍵)が生成されるはずです。作れたら公開鍵をsshサーバーに追加しておいて下さい。

# sshtunnelのインストール
Pythonで作成されたsshtunnelというPort Fowardingが簡単にできるモジュールがあります。StaShで次のコマンドを実行してインストールします。

```
$ pip install sshtunnel
```

ここで、sshtunnelを少し修正します。sshtunnelはparamikoというモジュールに依存しているのですが、バージョンの食い違いでそのまま使うとエラーが出ます。PythonistaメニューのPython Modulesからsshtunnel.pyを探し出して次のように修正します。

```
1021> if host_pkey_directories is not None:
1022> 	paramiko_key_types = {'rsa': paramiko.RSAKey,
1023> 'dsa': paramiko.DSSKey,
1024>-'ecdsa': paramiko.ECDSAKey,
1025>-'ed25519': paramiko.Ed25519Key}
1024>+'ecdsa': paramiko.ECDSAKey}

...

1215> for pkey_class in (key_type,) if key_type else (
1216> 	paramiko.RSAKey,
1217> 	paramiko.DSSKey,
1218>-	paramiko.ECDSAKey,
1219>-	paramiko.Ed25519Key
1218>+	paramiko.ECDSAKey
1220> ):
```

-の行を消して+の行を追加して下さい。

# プログラムの作成
sshtunnelを使ってポート転送するプログラムを書きます。

```
from sshtunnel import SSHTunnelForwarder

server = SSHTunnelForwarder(
    ('sshサーバーアドレス', sshサーバーポート),
    ssh_username='ユーザー名',
    ssh_pkey='秘密鍵の場所',
    remote_bind_address=('リモート側アドレス', リモート側ポート),
    local_bind_address=('ローカル側アドレス', ローカル側ポート)
)

server.start()
input()
server.stop()
```

例えば次のように作成します。

```
from sshtunnel import SSHTunnelForwarder

server = SSHTunnelForwarder(
    ('server_ip', 22),
    ssh_username='user',
    ssh_pkey='~/.ssh/id_rsa',
    remote_bind_address=('127.0.0.1', 8000),
    local_bind_address=('127.0.0.1', 8000)
)

server.start()
input()
server.stop()
```

適当な場所にこのコードを作成して実行すればポート転送出来ます(iphoneの仕様上、バックグラウンドで動作するのは10分間だそうです)。Pythonistaは作成したプログラムのショートカットをホーム画面に追加する機能があります。作成しておけばワンクリックで実行できます。
Rails x Docker環境にテストDBを構築する

Docker上でRailsアプリを開発するにあたり、テスト用のDBと開発用DBをそれぞれ構築する必要があったのでそのときの構築手順を記載。

## 前提

以下のようなDockerファイルでRailsアプリケーションを構築しているとする。

```name:Dockerfile
FROM ruby:2.6
RUN apt-get update -qq && apt-get install -y nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

COPY . /myapp

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

RailsアプリケーションのDB設定を以下のようになっているとする。

```database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DB_USERNAME', 'root') %>
  password: <%= ENV.fetch('DB_PASSWORD', 'pass') %>

development:
  <<: *default
  host: db
  database: myapp_development

test:
  <<: *default
  host: test-db
  database: myapp_test

production:
  <<: *default
  database: myapp_production
```


## Dockerの設定方法

DBのコンテナ名はdatabase.ymlで設定したhostと一緒にする。

今回の場合、開発用DBのdockerコンテナは'db', テスト用DBのdockerコンテナは'test-db'とする。

MYSQL_USERとMYSQL_PASSWORDはdatabase.ymlで設定したユーザー名とパスワードと一致させる必要がある。

MYSQL_ROOT_PASSWORDはroot用パスワード。設定が必須。

MYSQL_DATABASEはdatabase.ymlで設定したdatabaseと一緒にする。


```docker-compose.yml
version: '3'
services:
  web:
    build: .
    env_file: development.env
    ports:
      - '3001:3000'
    volumes:
      - .:/myapp
    depends_on:
      - db
      - test-db
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: 'pass'
      MYSQL_USER: 'webapp'
      MYSQL_PASSWORD: 'test'
      MYSQL_DATABASE: myapp_development
    ports:
      - '3306:3306'

  test-db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: 'pass'
      MYSQL_USER: 'webapp'
      MYSQL_PASSWORD: 'test'
      MYSQL_DATABASE: myapp_test
    ports:
      - '3307:3306' # ローカルPCから接続するために設定
```

docker-compose.ymlに直接記述してもいいけど、環境変数は外出ししておく。
今回は、webappというユーザーがtestというパスワードでDBに接続できるような設定にする。↓


```development.env
DB_USERNAME=webapp
DB_PASSWORD=test
```


## 接続確認
webのコンテナ名がdocker-rails_web_1という名前で立ち上がっている前提。

```
$ docker exec -it $(docker ps -f name=docker-rails_web_1 -q) /bin/bash

[docker] # rails c -e test
irb(main):001:0> ENV['RAILS_ENV']
=> "test"

[docker] # rails c
irb(main):001:0> ENV['RAILS_ENV']
=> "development"
```

## さいごに
今回は環境ごとにDBコンテナを用意することでテストDBを構築しました。
そのほかには`/docker-entrypoint-initdb.d`配下にスクリプトを配置するという方法もあります。

詳しくは[【Docker】Railsで理解する、複数のMySQLデータベースに一般ユーザーで接続するための環境構築手順](https://nishinatoshiharu.com/dockerdb-user-accesses/)で紹介していますのでご興味のある方はご覧になっていただければと思います。

ツイッター(<a href="https://twitter.com/nishina555" target="_blank" rel="noopener">@nishina555</a>)やってます。フォローしてもらえるとうれしいです！
初心者がconohaでVPSを借りてnginxでphpページを公開するまで -nginxとphp編-

#nginxとphpをインストールして動かす

###1:最新のnginxをインストールする
今回はapacheではなくてnginxでサーバーを立てようと思います。

```bash
#root権限で以下の作業を実行する
sudo -s

#nginx 公式サイトからPGPkeyをダウンロード
wget https://nginx.org/keys/nginx_signing.key

#PGPkeyを登録
apt-key add nginx_signing.key

#aptのリストに追記する
vi /etc/apt/sources.list
→ deb http://nginx.org/packages/ubuntu/ bionic nginx
→ deb-src http://nginx.org/packages/ubuntu/ bionic nginx

#aptのリストを更新
apt update

#nginxをaptでインストール
apt install nginx

#サーバー起動時にnginxも起動するように設定
systemctl enable nginx

#nginxを起動
systemctl start nginx
```

これでひとまずnginxのインストールは完了！


###2:最新のphpをインストールする
僕はphp大好き人間なのでphp使います。

```bash
#root権限で以下の作業を実行する
sudo -s

#aptの一覧にphpを追加する
add-apt-repository ppa:ondrej/php

#aptの一覧の更新
apt-get update

#aptの一覧からphpの最新のバージョンを確認(今回は7.3だった)
apt list|grep -i mariadb-server

#phpをaptでインストール
apt-get install php7.3 php7.3-fpm php7.3-mysql php7.3-mbstring php7.3-zip

#phpの設定ファイルを編集する(その1)
vi /etc/php/7.2/cli/php.ini
→ cgi.fix_pathinfo の値を 0 に
→ date.timezone の値を "Asia/Tokyo" に

#phpの設定ファイルを編集する(その2)
sudo /etc/php/7.2/fpm/php.ini 
→ cgi.fix_pathinfo の値を 0 に
→ date.timezone の値を "Asia/Tokyo" に
```
これでphpもとりあえずインストール完了！


###3:nginxとphpを設定する
nginxとphpの設定を弄ってちゃんと動くようにします。

```bash

#root権限で以下の作業を実行する
sudo -s

#nginxの設定ファイルを以下を参考に編集する
vi /etc/nginx/conf.d/default.conf

- - - - -

location / {
  root   /usr/share/nginx/html;
  index  index.html index.htm;
}

  ↓

location / {
  root /usr/share/nginx/html;
  index index.html index.htm index.php;
}

- - - - -
- - - - -

#location ~ \.php$ {
#  root           html;
#  fastcgi_pass   127.0.0.1:9000;
#  fastcgi_index  index.php;
#  fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
#  include        fastcgi_params;
#}

  ↓

location ~ \.php$ {
  root /usr/share/nginx/html;
  fastcgi_pass unix:/run/php/php7.2-fpm.sock;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  include fastcgi_params;
}

- - - - -
- - - - -

#location ~ /\.ht {
#  deny  all;
#}

  ↓

location ~ /\.ht {
  deny all;
}

- - - - -

#phpの設定ファイルを編集する
/etc/php/7.3/fpm/pool.d/www.conf
→ user の値を nginx に
→ group の値を nginx に
→ listen.owner の値を nginx に
→ listen.group の値を nginx に

#サーバー起動時にphpも起動するように設定
systemctl enable php7.3-fpm

#phpを再起動
systemctl restart php7.3-fpm

#nginxを再起動
systemctl restart nginx

#nginxグループにユーザーを加入させる
usermod -aG nginx ユーザー名

#ドキュメントルートの所有者を変更する
chown -R nginx:nginx /usr/share/nginx/html/

#ドキュメントルートの権限を変更する
chmod -R 755 /usr/share/nginx/html/
```

#ここまでやればwebは動く。
…ハズ。
大体設定を変に弄っちゃってエラー吐くパターンが多いです。
僕もかれこれ6時間くらいはハマりました、めげずにがんばりましょう！！
python bigquery and MySQLからpandas dataframe作成。dataframeから bigquery and Google Cloud Storage as csv

#内容
bigquery, MySQLからpandas dataframeを作る関数と、dataframeからBigqueryへ保存とGCSへのCSV保存をやりたく、ついにまとめたので、ちと記事作成。

#コード
```sample.py
import pandas as pd
import os
import pymysql
import dsclient

# init client with project id and creadential
client = dsclient.Client(
    "YOUR_GCP_PROJECT_NAME",
    "/hogehoge/hogehoge/credentials.json"
)


def bq2df(query):
    # read data with big query
    return client.query(query)


def mysql2df(query):
    # read data from MySQL with Google Cloud SQL
    connection = pymysql.connect(
        host='XXXXXXX',
        user='YOUR_USERNAME',
        password='YOUR_PASSWORD',
        db='YOUR_DBNAME',
        charset='utf8',
        cursorclass=pymysql.cursors.DictCursor
    )
    return pd.read_sql(sql, connection)


def df2bq(df, dataset, table):
    # store data with big query
    tb_name = dataset + "." + table
    client.load(df, tb_name)


def df2gcs(df, folder, file_name, bucket="gs://hogehoge/"):
    # store data to Google Cloud Storage as csv
    buket_name = os.path.join(bucket, folder, file_name)
    client.write_csv(df, buket_name)


if __name__ == "__main__":
    df = pd.DataFrame([i for i in range(100)], columns=["test"])
    folder = "write_test"
    file_name = "test1.csv"
    df2gcs(df, folder, file_name)

```
英語のコメントアウトは気にしないでちょ
各hogehoge等を変更してガムバッテクレ
これ超便利。

#参考サイト
https://github.com/orfeon/gcp-python-dsclient
これは便利！MySQLの結果を縦表示にする方法

うちの新人が知らなかったので、
小ネタとして。。。

見本用に適当にテーブルを作り、データを用意しました。
※ここでは割愛します。

とりあえずSELECTで中身を見てみます。

```sql
mysql> SELECT * FROM sample;

+------+----------+------------+-------+-------+-------+-------+-------+-------+-------+---------------------+---------------------+
| id   | password | is_deleted | text1 | text2 | text3 | text4 | text5 | text6 | text7 | created             | modified            |
+------+----------+------------+-------+-------+-------+-------+-------+-------+-------+---------------------+---------------------+
| 1    | test     | 0          | aaa     | bbb     | ccc     | ddd     | eee     | fff     | ggg     | 2019-06-25 20:00:00 | 2019-06-24 20:00:00 |
+------+----------+------------+-------+-------+-------+-------+-------+-------+-------+---------------------+---------------------+
1 row in set (0.00 sec)
```

デフォルトだと横長で表示されるので、
ウィンドウのサイズによって見づらかったりします。

そこで今度は;を\Gに変えます。
そうすると...

```sql
mysql> SELECT * FROM sample\G

*************************** 1. row ***************************
        id: 1
  password: test
is_deleted: 0
     text1: aaa
     text2: bbb
     text3: ccc
     text4: ddd
     text5: eee
     text6: fff
     text7: ggg
   created: 2019-06-25 20:00:00
  modified: 2019-06-24 20:00:00
1 row in set (0.00 sec)
```

##まとめ

このように縦に表示されて見やすくなりました。

##参考
MySQL 5.6 リファレンスマニュアル 
https://dev.mysql.com/doc/refman/5.6/ja/mysql-commands.html
スマートスピーカーを遊びたおす会vol.6に参加した

# はじめに

[スマートスピーカーを遊びたおす会 vol.6](https://kotodama.connpass.com/event/132347/)に参加してきたので、そのことについて書きます。今回登壇されたのは**加川さん**、**池田さん**、**コバヤシトールさん**、**金谷さん**です。各登壇者がどのような方であるかの紹介はリンク先を参照してください。登壇で使われた資料もそこで見れると思います。

# 加川さんの発表

テーマは「サービス観点で見る家族のお手伝い帳」です。これは去年3月にあったハッカソンから生まれたClovaスキルですね。実は私もハッカソンの同じチームで参加してました。  
今回はサービスの中のVUIという観点でお話されました。この「家族のお手伝い帳」のVUIには２つの役割があります。

- お手伝いの報告
- 貯めたポイントの利用

これらをAIエージェントが代弁することで、子どもにお手伝いを継続させることに繋がります。この部分にVUIの役割を位置づけたそうです。ここではVUIのみのサービスを考えるのではなく、サービス全体の中にVUIをどのように役割分担させるのかを考えることが重要だと話されました。

また、**効果音の利用によるUXの向上**についてもお話されました。効果音の有無で、スキル利用の継続性が増すとのことです。このお手伝い帳にも効果音を効果的に組み込むことで、子どもの注目を引き付けるための仕組みを入れたというお話をされました。 

効果音のライブラリはちょっと使ってみたいし、またそれを記事にしてみたいですね～

# 池田さんの発表

「背景スキル様お元気ですか」というタイトルでお話されました。このセッションではスキル開発をしたその後のスキル利用のレポートについてのお話をされました。  

**作って出しただけのスキル**は時期が過ぎるにつれてスキルの呼出し回数が減少していきます。**いかにスキルをアップデートをしていくか**がユーザー数を増やすことへの重要な要素になってきます。  ここでは「**歯磨きくん**」という超人気スキルがリリースからどのようにアップデートされてきたのかについて語られました。

この全体の講演を通して、スキルは**ただ作る**だけでなくちゃんと育てて**成長させる**ことが人気スキルを作るために必要なことであるとお話されました。  

いまのところ私もスキルは作り捨てが基本なので考えさせられました。。。

# コバヤシトールさんの発表

「プラットフォーム別攻略法＆「オレオレボイス」でVUIを遊び倒す」という２つのタイトルでお話されました。  

## プラットフォーム別攻略法

ここではGoogle、Alexa、Clovaそれぞれのプラットフォームの特色についてお話されました。スキル開発にはそれに適したプラットフォームがあるそうです。実際「**VoiceApp Lab**」さんのスキルも各プラットフォームで人気の順位が異なるとのこと。（例えばスキルの見つけてもらえる度合い・ユーザー層etcが異なります）  

## 「オレオレボイス」でVUIを遊び倒す

[GaradeBand](http://musicviral.jp/2018/02/28/iphone/)というフリーソフトを使って自分の声をボイスチェンジさせる方法についての技術解説をされました。「ゾンビのまち」などの人気スキルの音声素材は実際にこのツールを使って作られたそうです。  
しかしここで重要なのは、ソフトのパラメータのみで声を調整するのではなくメインは**自分の声の出し方の調整**とのこと。

# 金谷さんの発表

「水族館でスマートスピーカーを活用してみた」というタイトルでお話されました。兵庫県のある水族館で「**マゼランペンギン型解説システム**」というものを導入されたそうです。マゼランペンギン型解説システムについての紹介は[こちら](http://www.city.kobe.lg.jp/information/press/2019/05/20190514142001.html)にありました。  

今回はこのシステムをつくった目的についてもお話されました。まず水族館が抱える課題に飼育員の人員コストがあり、こういったシステムの開発が求められたとのこと。

この対話システムの実現にはCloud Speech-to-Text・Dialogflowなどのクラウドサービスを使っているそうです。またこのシステムはラズベリーパイを使ってオリジナルのハードウェアで使ったそうで、スマートスピーカーではできない**デバイスからユーザーに話しかける**ことをして、よりユーザーに使ってもらいやすいシステムを実現させたとのこと。（対話シナリオはGoogleSheetから収集をしているみたいですね）  

スマートスピーカーとは違いましたが、こういうオリジナルハードウェアの話は個人的に結構好きです。

# さいごに

今回はどちらかというとスキルをつくっている人向けの勉強会だったなという印象を感じました。だからこそとても自分にとって勉強になりました。
Ruby on Railsで検索機能を実装

```application.html
<%= form_tag("/posts/search", :method => 'get', :class => 'form') do %>
<div class="search-form">
<%= text_field_tag :search, '', class: 'search-input' %>
<%= submit_tag "検索", :name => nil, class: 'search-btn' %>
</div>
<% end %>
```
```posts_controller.rb
def search
    @posts = Post.search(params[:search])
  end
```
```post.rb
def self.search(search)
        if search
          Post.where('name LIKE? OR gender LIKE? OR country LIKE? OR job LIKE? OR content LIKE?',
                    "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%")
        else
          Post.all
        end
    end
```
```search.html.erb
            <% @posts.each do |post| %>
            <div class="img-link">
                <a href="<%= "/posts/#{post.id}" %>" class="thumb-link">
                    <div>
                        <img src="<%= "/images/#{post.image}" %>" class="thumb-img">
                        <div class="thumb-name"><%= post.name %></div>
                    </div>    
                </a>
            </div>    
            <% end %>
```
Rustでグラフをプロットする

[plotlib](https://github.com/milliams/plotlib)というPure RustのSVGを生成するライブラリがあります。

```rust
use plotlib::page::Page;
use plotlib::scatter::{Scatter, Style};
use plotlib::style::Point;
use plotlib::view::ContinuousView;

fn main() {
    let data = [
        (-3.0, 2.3),
        (-1.6, 5.3),
        (0.3, 0.7),
        (4.3, -1.4),
        (6.4, 4.3),
        (8.5, 3.7),
    ];

    let s = Scatter::from_slice(&data).style(
        // プロット点の色を指定
        Style::new().colour("#35C788"),
    );

    let v = ContinuousView::new()
        .add(&s)
        .x_range(-5., 10.)
        .y_range(-2., 6.)
        .x_label("Some varying variable")
        .y_label("The response of something");
    // SVGとして保存する
    Page::single(&v).save("scatter.svg").unwrap();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30426/85f0fed6-b37f-01e0-8906-529b22015860.png)

QiitaはSVGを埋め込めないのでimagemagickでPNGにしてあります

```sh
convert scatter.{svg,png}
```
WSLでは大文字小文字だけ違うファイル作っても同一のものとして認識されるんだなーという話

WSL2が発表され（だいぶ前だけど）Windows10上で満足にlinuxでできることがやれると（個人的に）期待が高まる中、あらためてWSLの欠点を探してたところ気づいたものをちょっと書く。

# 記事にした理由
これが"WSL"の欠点や（原文ママ）って僕よりよっぽどできる友達に話題を投げかけたところ、「記事でも書いてみれば？」って言われたので...というわけで初投稿です。よろしくお願いします。

# 本題：WSLでは同一ファイル名だと大文字小文字の違いは認識されない
以下の画像の通りです。
![WS000302.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114507/593f8b0e-f7f3-3e81-c663-46c178b7de5d.jpeg)

元々`kt.txt`というファイルがありまして、そこに

```bash
touch Kt.txt
touch KT.txt
```
と入力して同一ファイル名の大文字小文字の違いがあるファイルを作成しようとしました...が
このとおり元の`kt.txt`しか残りませんでした。
## そもそもこれはWindowsの仕様
![WS000303.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114507/91ba6197-592e-4683-0d37-3af6e0330146.jpeg)

このように同一ファイルとして扱われてしまいます。
## 仮想環境下ではどうか
![CFD8W1pE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114507/25f3427a-cac8-7d1f-ada4-5e2a27996313.png)

仮想環境下では同一ファイル名でも無事別物として認識されましたね...（そもそもlinuxではそうだからこそこの記事を書こうと思ったのです。）

# まとめ：WSL2なら...？
というわけでWSLだと大文字小文字だけ違うファイル作っても同一のものとして認識されるんだなーという話でした。
linuxカーネルまでも備えるWSL2ならこんなことも起こらないでしょう...多分:thinking:
何かあればコメントのほうまでお願いします。喜びます。

# 参考
[Windows 10のWindows Subsystem for Linux（WSL）を日常的に活用する](https://www.clear-code.com/blog/2017/11/8.html)
ESP8266の書き込みエラー

以下のデータをESP8266にesptool.py v2.4.1で書き込むとエラーになります。

```
$ printf "%0.se5 " {1..256256} | xxd -r -p > test.dat
$ esptool.py -p /dev/cu.usbserial-DA00WSMM -b 921600 write_flash 0x300000 test.dat 
esptool.py v2.4.1
Serial port /dev/cu.usbserial-DA00WSMM
Connecting....
Detecting chip type... ESP8266
Chip is ESP8266EX
Features: WiFi
MAC: 5c:cf:7f:00:00:00
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 256256 bytes to 272...
Wrote 256256 bytes (272 compressed) at 0x00300000 in 0.0 seconds (effective 127832.5 kbit/s)...

A fatal error occurred: Timed out waiting for packet header
```

ボーレートを115200にしてもダメです。

データーサイズが246256でもエラーで236256だとエラーになりません。普通にデータの入った256256バイトのデータはちゃんと書けます。単一のデータと大きさが要因で不具合があるように見受けられます。

read_flashは921600ボーだとダメで115200ボーだと大丈夫だったりします。

El CapitanのFTDIドライバの問題の可能性もあるかもしれませんが、なんだかとっても不安定です。
[Python]文字列型を1要素のリスト型にする方法

Pythonで文字列を1要素のリスト型にする時に以外と苦戦したので、その方法を書いておこうと思います。

#訂正　(2019 06/26)
ご指摘頂いたのでですが、無駄に複雑な方法で文字列型をリスト型に変換していました。。。
こちらの方法で十分でした。。。

```python
test = 'hogehoge'
#文字列型をそのままリスト型に変換
test_list = [test]

print(test_list) #['hogehoge']
```

なので、下記の方法は悪い例です。皆さん上記の方法で普通にリスト型にしましょう。

#文字列型を1要素のリスト型にする
今回は、文字列を任意の文字で区切り、リスト化する事ができるsplit関数を使います。

```python
test = 'hogehoge'
#split関数は、第１引数に区切りたい文字、第２引数に区切る回数を指定
test_list = test.split(None,0)

print(test_list) #['hogehoge']
```
split関数は、第１引数に区切りたい文字列、第２引数に区切る回数を指定する事で、区切り文字で区切られたリストを得る事ができます。
今回は区切りたくないので、第２引数を0にしています。第１引数はNoneにしていますが、恐らくなんでも問題ないと思います。
これで、文字列型を1要素のリスト型にする事ができました！！

##参考文献
[【Python入門】split関数で文字列の分割の仕方](https://qiita.com/Morio/items/38701038ad098dd5dc3d)
progate 6/25

/login」で2つのルーティングが被っているように見えますが、「get」と「post」では異なるルーティングとして扱われるので問題ありません。（link_toメソッドではデフォルトでgetのルーティングを探し、form_tagメソッドがデフォルトでpostのルーティングを探します。）


2. インスタンス変数に対するnameメソッドの意味
@user.name = params[:name]

次回 8回 8/15以降
React.js勉強中に気になった些細なこと

# なんやねん
React.jsの勉強中に気になった部分
サンプルコードは[公式](https://www.shuwasystem.co.jp/book/9784798056920.html)から誰でも見れるし内容についての話じゃないので大丈夫だと思うけど怒られたら消します
# ほんで？
```react
<script type="text/babel">
let dom = document.querySelector('#root');

let message = 'お名前は？：';
let nameVal = '';


let doChange = (event)=>{
    nameVal = event.target.value;
    message = 'こんにちは、' + nameVal + 'さん';
};


let doAction = (event)=>{
    let element = (
    <div>
        <p>{message}</p>
        <div>
            <input type="text" id="input" onChange={doChange} />
            <button onClick={doAction}>
                クリック
            </button>
        </div>
    </div>
    );
    ReactDOM.render(element, dom);
};


doAction();
</script>
```

ふむふむ

クリックイベントのサンプルですね

ん？

```react
<input type="text" id="input" onChange={doChange} />
            <button onClick={doAction}>
                クリック
            </button>
```
ここの部分
クリックイベントでレンダリングしてるけど、onChange（inputの中身を変更する）のたびにレンダリングするように変えればそうなるって理解でOKだよね（イベントだから）

・・・

確かめずにはいられない！！

```react
let doAction = (event)=>{
    nameVal = event.target.value;
    message = 'こんにちは、' + nameVal + 'さん';

    let element = (
    <div>
        <p>{message}</p>
        <div>
            <input type="text" id="input" onChange={doAction} />
            <button  onClick={doAction}>
                クリック
            </button>
        </div>
    </div>
    );
    ReactDOM.render(element, dom);
};
```

確かめるだけならこれでOKかな、確かめよう

よし、onChangeのたびに表示が変わった！
スッキリ！！

**そんな、ほぼ日記のような些細なこと**
Burp Suiteでのセキュリティテスト[攻撃編2]

前回：https://qiita.com/ta2roo/items/5324805d9e68f18279c4
送信中のデータの改ざんについて書いたので、該当するデータを総当たりでアクセスを行い
パスワードを調べるブルートフォース攻撃を行ってみようと思います。

前回同様、改ざんするべきデータをProxyタブのInterceptで見つけたら、
テキストフォーム内で該当データを選択し、Send to Intruder を押します。

Intruderに移動します。　Positionsから調査するパスワードのパラメータを選び§§ではさみPayloadタブへ移動します。
<img width="828" alt="burp0625-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/838c2a06-dae4-dcf2-47fb-e46becded66b.png">

<img width="794" alt="burp0625-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426556/e986115c-a74e-edcf-4f24-02ca85e1df4c.png">
PayloadTypeをBrute Forcerに変更にします。

PayloadOptionsで文字数や対象となる文字の種類を選択します（小文字大文字数字記号など）

右上の[Starck Attack]を押すと攻撃が開始されます。
![Bad Request]()

サーバーの性能に左右されますが
文字/大文字/記号/数字
8文字～12字のパスワードで試したところ
1分で35件処理されました。
１秒=0.5件
4桁の数字であれば、19998秒　＝　333分で全パターン処理できることなります。
FikeMaker18で追加されたファイル操作機能を使ってシーケンシャルログを書き出す

#初めに
ファイルメーカ18で新規追加されたファイル操作機能を使って
テキストログを作成するためのHow Toです。
ファイル操作に関するスクリプトステップ毎の機能紹介はあるのですが
シーケンシャルなログ書き出しについて、まとまった記事を見かけなかったため
整理してみました。
実はファイルオープンしっぱなしでどんどん書き込む場合、
あまり意識しないでもシーケンシャルに処理可能ですが、あまりそのような使い方はしないと思います。
複数のスクリプトから呼び出す等、ファイルのオープンクローズしながら同一ファイルに書き込むには
ちょっとだけコツが必要でした。

#ファイル操作機能とは
言葉の通りファイルを操作するための機能です。
ファイルメーカ18より前は、テキストファイルの操作をするには
プラグインや外部のスクリプト等に頼る必要がありました。
今回の新機能で、ファイルメーカから直接テキストファイルを
操作することができるようになりました。


#スクリプト（テキストログ作成）

~~~

エラー処理[オン]

#１．ファイル名指定
変数を設定 [ $PATH; 値:Get ( デスクトップパス ) & "log_" & Substitute ( Get( 日付 ) ; "/" ; "" ) & ".txt" ]

#２．ファイル有無確認
ファイルの存在を取得 [  「$PATH」 ; ターゲット: $FileExist ]

#３．ファイルがなければ新規作成
If [ $FileExist = 0 ]
 データファイルを作成 [  「$PATH」 ; フォルダを作成: オフ ]
End If

#４．ファイルOPEN ファイルIDを取得
データファイルを開く [  「$PATH」 ; ターゲット: $FileID ]

#５．エラーの場合終了
If [ Get ( 最終エラー ) ]
 現在のスクリプト終了 [  ]
End If

#６．ファイルサイズ（ファイルの最終位置）を取得
ファイルサイズを取得 [  「$PATH」 ; ターゲット: $FileSize ]

#７．ファイル位置指定
データファイルの位置を設定 [  ファイル ID: $FileID ; 新しい位置: $FileSize ]

#８．LOG書き込み内容生成　タイムスタンプ＋（引数：LOGの内容）
変数を設定 [ $WriteData; 値:Get ( タイムスタンプ ) & " " & Get ( スクリプト引数 ) ]

#９．ファイル書き込み
データファイルに書き込む [  ファイル ID: $FileID ; データソース: $WriteData ; 書き込み: UTF-8 ; 改行を追加: オン ]

#１０．ファイルCLOSE
データファイルを閉じる [  ファイル ID: $FileID ]

~~~

#処理概要
###１．ファイル名指定
　作成するファイル名です。私はデスクトップにlog_yyyymmdd.txtとして作成しました。
###２．ファイル有無確認
　ファイルの有無確認です。
###３．ファイルがなければ新規作成
　２で取得した条件をもとに、ファイルがなければ新規作成します。　

###４．ファイルOPEN ファイルIDを取得
　操作するファイルをOPENします。

###５．エラーの場合終了
　ファイルが操作中であったりすると、エラーが発生するためエラー判定します。

###６．ファイルサイズ（ファイルの最終位置）を取得
　最終位置に書き込むため、ファイルの最後のバイト位置を取得します。

###７．ファイル位置指定
　ファイルの最終位置に書き込み位置を指定します。

###８．LOG書き込み内容生成　タイムスタンプ＋（引数：LOGの内容）
　ログの書き込み内容を生成します。
　自分はタイムスタンプ＋書き込み内容としました。

###９．ファイル書き込み
　ファイルに８で生成した内容を追記します。

###１０．ファイルCLOSE
　最後にファイルをクローズします。

#使い方
・ログを書き出したいスクリプトから、スクリプトステップ 「スクリプト実行」を設定する。
・引数に記入したいログテキストを記入してください。
スクリプトを実行し、デスクトップに、ファイルが作成されたら成功です！
2回目以降、スクリプト実行したら、ログが下に追記されていきます。


#最後に
DBを作る必要なくスクリプトを作るだけのため、かなり使い勝手がいいです。
そのまま打ち込むだけで使えるのでぜひ試してみてください。
Azure Bastion（プレビュー）をLinuxVMに対して試した

[n番煎じ](https://qiita.com/search?q=Azure+Bastion)ですが、個人的にタイムリーだったので。適当に立ち上げたLinux VMに対してSSHで試した。

告知はこのあたり。

https://azure.microsoft.com/ja-jp/blog/announcing-the-preview-of-microsoft-azure-bastion/



利用手順
-------------

https://docs.microsoft.com/ja-jp/azure/bastion/bastion-create-host-portal

- Azure CLI からプレビュー機能を有効にする

``` cmd
az feature register --subscription サブスクリプション名 --name AllowBastionHost --namespace Microsoft.Network

# showで`"state": "Registered"` になったらOK。Registeringは作業中の状態なのでダメ。
az feature show --subscription サブスクリプション名 --name AllowBastionHost --namespace Microsoft.Network
```

- プレビュー版のポータル（[https://aka.ms/BastionHost](https://aka.ms/BastionHost)）へ移動する。（上のバーがオレンジ色になる）
- 「Bastions」で検索すると出てくる。「Bastions(プレビュー)」の方を選ぶ
  - ![検索画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/dc415762-8ded-476a-5979-0b7f2e6d27ac.png)
- サブネットを用意する。注意として「`AzureBastionSubnet`」という名前とCIDERブロックが`/27`よりも広いサブネットで作らなければならない。（Bastionsの作成中に「サブネット構成の管理」から追加できる。）
  - ![サブネット作成イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/7e9ad66d-5dbf-6594-5403-c463795a4f2d.png)
- Bastionsの作成設定はこんな感じになる。
  - ![Bastions設定イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/e7747201-5968-93f8-d797-5a2151d14a89.png)


仮想マシンを見に行くと、ブレードにBastionが追加されている。
以下の画像は概要のConnectからBastionタブを開いたところ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/37b6e120-975d-29c9-5ab7-5d1080ec9be2.png)

RDPとSSHをサポートしており、SSHの場合はユーザ名と秘密鍵(パスフレーズ可)を与えることになる。

SSH接続の場合、コンソール画面が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52527/db6c5f01-2ec3-c814-1538-f69f8a7c8cc1.png)


雑談
---------

- [新しめなssh-keygenで作成した秘密鍵](https://www.openssh.com/txt/release-7.8)をサポートしていない。
  - （何形式と呼ぶんだろう？新しい方はBEGIN RSA ...ではなくBEGIN OpenSSH...という文言になっており、パスフレーズ等の情報が見えなくなっている形式）
- Azure Bastion自体はまだSSH接続をサポートしていない模様。（告知の通り）

課金形態は[こちらを見た](https://azure.microsoft.com/ja-jp/pricing/details/azure-bastion/)感じ、稼働時間とデータ送信量のように見える。接続時間になれば最高なんですが...。B1lsなVMと比べてしまうと、金額的に安くない気もする。

告知にあるSSHやRDPのネイティブクライアント対応や、Azure AD連携辺りは気になりますね。これ相互に排他的な利用になりそうだけど、どうやるんだろう。

妄想を膨らませると、AAD連携が出来ればKeyVaultと連携して、踏み台利用者に秘密鍵を与えずに作業させたりできるようにもなり良さそう。一方でこれぐらいやってくれないと利用するうま味が無い気がした。
[メモ]Alexaハッカソンに参加してASK SDK for Pythonで開発してみたら困ったことや小ネタ

すみません、以下は殴り書きです。
公式のフォーラムに清書しようかなと思います。

# [永続アトリビュート](https://ask-sdk-for-nodejs.readthedocs.io/ja/latest/Managing-Attributes.html#id5) の サンプルコードはどこに!?
ask new で high low game を選択するとsrc見れる

# [ユーザーからの返事がなかった時の発話](https://developer.amazon.com/ja/docs/custom-skills/handle-requests-sent-by-alexa.html#process-request) reprompt

なんで関数名がちがうんだろう

Node.jsは `reprompt`

```javascript
const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};
```

Python3は `ask`

```python
class LaunchRequestHandler(AbstractRequestHandler):
    """スキルを起動するハンドラーです。"""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "ようこそ、アレクサスキルキットへ。こんにちは、と言ってみてください。"

        handler_input.response_builder.speak(speech_text).ask(
            "続けて、こんにちは、と言ってみてください。").set_card(
            SimpleCard("ハローワールド", speech_text))
        return handler_input.response_builder.response
```

# Cloneするとvenvとlambda/pyがまとめられちゃう

## 概要

.venv内の pip のパッケージ一覧と、自分のsourceが同じDirにまとめられちゃう

## 再現手順

1. `ask clone`

2. あなたのAlexa project(python)を選択

で、自分でdeployしたprojectをcloneすると、

- .venv/skill_env/lib/python3.7/site-packages/ 内の modules 
- skill.json/manifest.apis.custom.endpoint.sourceDir で定義したprojectのsource

が、lambda/${skill.json/manifest.apis.custom.endpoint.uri} にまとめられている...

# Cloneすると、 .venv がない

`requirements.txt` はあるので、 .venv を自作するのかな...?
何も考えずにvenv作ってpip installするだけでは、lambdaにpackageを追加できなかった
macのスクショの保存場所

①スクショを入れたいフォルダを作って
②コマンドに以下入力↓
```$ defaults write com.apple.screencapture location ~/Desktop/ScreenShots（スクショを入れたいフォルダへのパス）
$ killall SystemUIServer```

そこで
Unexpected argument screenshot; leaving defaults unchanged.
と怒られたのですが

ファイル名に意図せずして空欄が入ってたので
それを消して再試行してみるとできました
macosx  の node 環境がぐちゃぐちゃになったので再インストール

備忘録で。

二つのプロジェクトでnodeを使っているのですが、'npm start'がよくわからないエラーで走らない！　（先日何か brewで更新したような・・）　どうも小手先の修正で直せそうなエラーではないので nodeごと再インストールしました。 (最悪 macosxのクリーンインストールまで考えたけど、macはnodeでできてるわけじゃないからそこまで必要ないだろうと）

```
# 一応やってみるけどこれだけでは不足だった
brew uninstall --ignore-dependencies node
# そもそも /usr/localより下を rootにならずに書き換えられるようにしておく必要あり。私のはそうなっていなかった。
sudo chown -R your_login_name:wheel /usr/local

# なんかunlinkできないとかlinkできないとかいわれるのでみつけたnode関連のフォルダは消す！
sudo rm -rf /usr/local/share/doc/node
sudo rm -rf /usr/local/include/node
sudo rm -rf /usr/local/Cellar/node/

#これでやっとクリーンインストールが通りました。
brew install node
```
Azureでオートスケーリング（調査）

# お断り

私の人は AWS勉強中のオープン系エンジニアです。
AWS触ったことがある人向けに書くので、
わかりやすさ優先でEC2の独特の名称を使ってお話します。
カッコの中に書かれているのは Azureの中の名称です。
例：インスタンス（VM→Virtual Machines）


# Azureでオートスケーリングするための準備

## イメージの作成

[公式Doc](https://docs.microsoft.com/ja-jp/azure/virtual-machines/windows/capture-image-resource#create-a-managed-image-in-the-portal)  
オートスケールする対象のインスタンス（VM）のAMI（イメージ）を作成しておく必要があります。
　→停止してからAMIの作成を行うため本稼働中のインスタンスではない、イメージ作成ようのインスタンスを作る必要がある模様です。

## ALB（ApplicationGateway）の場合は先に作成する

オートスケールの設定する画面に、ALB(ApplicationGateway)かNLB(ロードバランサー)かを選択する部分があります。
ここで、ALB(ApplicationGateway)の場合は既存のリソースを選択する必要があるので、
ない場合は先に作っておきましょう。

## NLBは作ってはいけない

NLBは新規に作ることが必須なようで、ALB(ApplicationGateway)と違い既存選択肢が出現せず、新規作成時に必要な情報入力になります。
（既存で稼働中の場合はどうしたらええんよｗ

# オートスケール設定

設定方法については一応日本語訳されたDocを見ながら作ることは(コツを知ってれば)できます

## とりあえずオートスケーリング（スケールセット）の設定しよう

Azureでは　Virtual Machine Scale Setsの作成 を行うことになります。  
日本語訳Docは[こちら](https://docs.microsoft.com/ja-jp/azure/virtual-machine-scale-sets/)

### 1.コンソール（ポータル）で作成する場合のつまづきポイント

[仮想マシン スケール セットを作成する](https://docs.microsoft.com/ja-jp/azure/virtual-machine-scale-sets/quick-create-portal#create-virtual-machine-scale-set)  
の、`"スケール セット" を検索し、[仮想マシン スケール セット] を選択し、[作成] を選択します。`
の部分。  
  
検索ウィンドウに いくら"スケールセット"と入力しても出てきません。
"scale set" と入力すると　" Virtual machine scale set"というやつが出てくるので、選びましょう。（悲しみ）

### 2. Azureも進化しているよ！！

Azureのオートスケールの記事を探しても、公式Doc か 
[Azureオートスケールやってみて感じた問題点](https://qiita.com/miwato/items/3fd89573d04f2dcd1b2a)
か [くらう道](http://www.cloudou.net/virtual-machine-scale-sets/vmss001/)か
という記事がTOPに出てきますが 調査としては記事をみるだけでは不十分でした。
(記事が2017年と古いんで)

先のQiitaの記事では Azureが提供しているイメージしか使えないとか、
ApplicationGatewayが コマンドからしかできないとか書いてありますが、
自作AMI（イメージ）での起動もできるし、コンソール（ポータル）からALB（ApplicationGateway）の設定も行うことができます。


### 3. AMIの変更はコマンドラインからのみ！

２年経過して、ALB(ApplicationGateway)が 画面からできるようになっていることもあり
そのうちできるようになるのかもしれませんが、
201906現在、AMI（イメージ）の変更はコマンドラインからしかできないので、
[公式Doc](https://docs.microsoft.com/ja-jp/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-upgrade-scale-set#examples)を見て下さい。

# 感想

既存のNLBにつけることができない、画面上から イメージの変更ができないなど
玄人向けなのかなって思いました。

また、AWSは必要なときに必要なものを追加していくアジャイル型、Azureはしっかり設計してOKが出たら手を動かすウォーターフォールモデル　って感じがしました。


いずれにしても慣れなのかなぁ。。。AWSが恋しい(T_T)
AppCompatSpinnerにデフォルトで指定されるstyleではまったのでメモ

# 概要

JavaからKotlinに書き換える中でこの２つのコンストラクタ定義でSpinnerの挙動が異なってはまったのでメモ。

```kotlin

public class CustomSpinner extends AppCompatSpinner {

    public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomSpinner(Context context) {
        super(context);
    }
}
```

```kotlin

class CustomSpinnerKt @JvmOverloads constructor(
    context: Context,
    attr: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatSpinner(context, attr, defStyleAttr) {
}
```

# 原因

`super(context, attrs);` では defStyleAttr に `R.attr.spinnerStyle` デフォルトで適応されるのが原因だった。

```java:AppCompatSpinner.java

public AppCompatSpinner(Context context, AttributeSet attrs) {
    this(context, attrs, R.attr.spinnerStyle);
}
```

`R.attr.spinnerStyle` の定義を遡っていくとv21以上では `Widget.Material.Spinner` を継承している。


```xml:Widget.Material.Spinner
<style name="Widget.Material.Spinner" parent="Widget.Spinner.DropDown">
    <item name="background">@drawable/spinner_background_material</item>
    <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
    <item name="popupBackground">@drawable/popup_background_material</item>
    <item name="popupElevation">@dimen/floating_window_z</item>
    <item name="popupAnimationStyle">@empty</item>
    <item name="popupEnterTransition">@transition/popup_window_enter</item>
    <item name="popupExitTransition">@transition/popup_window_exit</item>
    <item name="dropDownVerticalOffset">0dip</item>
    <item name="dropDownHorizontalOffset">0dip</item>
    <item name="overlapAnchor">true</item>
    <item name="dropDownWidth">wrap_content</item>
    <item name="popupPromptView">@layout/simple_dropdown_hint</item>
    <item name="gravity">start|center_vertical</item>
    <item name="disableChildrenWhenDisabled">true</item>
</style>
```

一方、v21未満では `Base.Widget.AppCompat.Spinner` を継承している。


```xml:Base.Widget.AppCompat.Spinner

<style name="Base.Widget.AppCompat.Spinner" parent="Platform.Widget.AppCompat.Spinner">
    <item name="android:background">@drawable/abc_spinner_mtrl_am_alpha</item>
    <item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
    <item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
    <item name="android:dropDownVerticalOffset">0dip</item>
    <item name="android:dropDownHorizontalOffset">0dip</item>
    <item name="android:dropDownWidth">wrap_content</item>
    <item name="android:clickable">true</item>
    <item name="android:gravity">left|start|center_vertical</item>
    <item name="overlapAnchor">true</item>
</style>
```


# 解決策

`defStyleAttr`のデフォルト引数に `R.attr.spinnerStyle` を指定した。

```kotlin

class CustomSpinnerKt @JvmOverloads constructor(
    context: Context,
    attr: AttributeSet? = null,
    defStyleAttr: Int = R.attr.spinnerStyle
) : AppCompatSpinner(context, attr, defStyleAttr) {
}
```
[Vue]<li>をループで回してつくった箇条書きに、十字キーでフォーカスを合わせてる風の挙動をさせる(＋enterキーでその要素を取得する)

# はじめに
今日は続編ということで、サンプルだけサクッと共有します。
# 前回の記事
前回の記事からの続きになります。
まだ読まれていない方は、[こちら](https://qiita.com/riotam/items/bcd2252c90c044b31f2f)からどうぞ。
# 今日のサンプルコード
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="riotam4" data-slug-hash="xoraPx" 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="[Vue]ループ&amp;lt;li&amp;gt;のフォーカス移動+選択">
  <span>See the Pen <a href="https://codepen.io/riotam4/pen/xoraPx/">
  [Vue]ループ&lt;li&gt;のフォーカス移動+選択</a> by riotam (<a href="https://codepen.io/riotam4">@riotam4</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
サンプルコードのみですが、こちらです。
入力欄にフォーカスした状態で、下キーを押すと、その下の箇条書きのところを、フォーカスしているような挙動をします。
enterキーを押すと、ダイアログ画面でその要素を受け取り、出力します。
jQuery世代がReact+OpenAPIでSPAを作ってみた感想

# jQuery世代がReactでSPAを作ってみた感想
ここ数年サーバーサイド専任だったので、知識としては知っていても経験はjQueryで止まっていたのですが、色々あってReactでSPAを作ることになりました。
何とかおっかなびっくり作り終えたわけですが、発想の転換を色々と迫られたので感想でも。

ちなみに成果物は社内用システムなので具体的なソースの公開についてはご容赦を

## Component
かつてのjQuery時代と言うと、コンポーネントはjQueryUIくらいしかなく、
基本的に画面はゴリゴリとhtmlとcssを書いて作るものでした。
ロジックの側でもDOMを書き換えたりクラスを付け替えたりするのは日常茶飯事でした。
例えば、jQuery時代に入力値をチェックしてInputにエラーを付ける場合

``` js
$(selector).addClass('error');

```
みたいな感じでした。(しかもこんなクラス名の付け方をしていたら衝突しまくってひどい目に会っていたでしょう。)
それが、今の時代基本的に基準はComponentです。
自分でhtmlを書くなんて言うのはComponent自体を実装する場合くらいで、
ロジック内ではstateを書き換えるだけです。
例えば、前述のようなエラーを付ける場合にはstateにエラーを渡すだけです。

``` javascript
component.setState({
  isError:true
});

```
始めは戸惑いましたが、コンポーネントの範囲内だけのスタイルも簡単にかけて影響度も小さくて済みますし、いい時代になったものだなと思います。

## DOM操作のコストをあまり考えなくていい
ブラウザの平均的性能が上がったことと、VirtualDomのおかげでDOM操作のコストをあまり考えなくてよくなったのもありがたいと感じる部分でした。
スクロールで延々DOMが足され続けるとか明らかなアンチパターン実装をしない限りここでストレスを感じなくていいのはちょっと感動です。
某ブラウザの古いバージョンとかは本当にちょっと触るだけで固まりやがりましたので。

## APIだけ決めてフロントとサーバーサイドで作業分担してみた
折角SPAで作ることに決めたので、OpenAPIでAPIの仕様だけ決めて、サーバーサイドとフロントエンドで別々に作って、ある程度できてから組み合わせるというのもやってみました。

これが、予想外に快適でした。
始めは実際に組み合わせたらエラーばっかりだろうなぁと思っていましたが、きちんと決めていただけあってAPI仕様上のエラーはほぼなく繋がりました。(トラブルが起こったのはAPI定義外の認証周りだけ)
二人で作業をしていたのですが、完全に触る場所がずれているのでコードのConflictもなく、Mockサーバーや自動テストベースで開発が進むのでオーバーヘッドがかなり少なかったように思えます。

## エコシステムの発達がすごい
ReactやVue,Angularといったメジャーどころのフレームワークは、CLIからwebpack組み込みのプロジェクトを作るのが当たり前で、yarnやnpmのコマンドをちょっと打てば必要なプラグインやライブラリは簡単に組み込めるようになっていますし、ビルドや開発サーバーの立ち上げも一発です。
かつて頑張ってscriptタグを書きまくって、自分で依存性制御していた時代から考えると嘘のようです。

ただ、色々なところを隠蔽してしまっているという面もあるので、何となくでもそれなりに書けるようになってはいますが、きちんと分かっている人とそうではない人の差が広がっているのかも知れません。

## どんどん抽象化と分割が進んでいる
今回はそこまで大きくないプロダクトだったのでReduxは使わなかったのですが、より大規模になってstate管理が複雑になると確かに直接stateを弄っていられなくなるんだろうなと感じました。
やはり大規模化⇒より抽象化されて分割されるという図式はどのレイヤーでも変わらないようです。

## まとめ
* 10年分くらい一気に歴史を進めて見たが非常に楽しかった
* 発想の転換は迫られるものの何とかなる
* フロント屋さん以外も触ってみるといいと思います。
（レポート）よなよなコーチングリトリート2019 (yycr2019) 参加レポート

# はじめに

2019/06/17-19に開催された、[yycr2019(アジャイルコーチとスクラムマスターの宴、通称：よなよなコーチングリトリート)](https://www.attractor.co.jp/info/coaching_event/)の参加レポートです。

このイベントは、アトラクタ主催で、COLONY箱根という素敵プレイスに集まり、みんなでRetreat（退却）しましょうというもの。
基本的にOST（Open Space Technology)形式でセッションを進めていき、各々が自由に参加しながら、Retreatを作り上げていきました。

# 参加の目的

普段、仕事やカンファレンスなど、自分を鼓舞しながらやってきました。
アジャイルコーチとして、チームファシリテーターとして、という他の人から見えているロールに応えるように無意識で動いていたのを、この3日間だけはそういうのをやめてみようかな、と。
「本当に自分の好きなように動いて、少しでもやりたくないことは絶対しない、というのをやる」というのが一番の目的でした。
あとは、副次的な目的として、「何か研修で使えそうなワークショップを作ってみたい」というのがありました。
今回は、どちらも達成できました。

# やったこと

**太字**の内容はOSTのテーマと起案者です。

Day 1

- みんなで自己紹介
- **ボードゲームいろいろ体験(やっとむ)**
- けん玉
- 飲み会
- 闇を吐き出す

Day 2

- **Social walk near the lake (Joe)**
- **アジャイル界隈の派閥を可視化する (関）**
- **ヨガを通してアジャイルを考える (いわむー)**
- **コーチするときに一番大事にしていることは？ (山田)**
- **イイ感じのふりかえり方法考えてみよう(森)**
- **実験：昼からビールは本当に美味いのか？(原田)**
- **アジャイル・スクラムを教えるワークショップ・研修(森)**
- **モブで研修を綺麗に作る(森)**
- ワークショップ記事もくもく作成
- けん玉
- みんなでふりかえり
- 飲み会

Day 3

- のんびり朝読書
- けん玉
- 人狼ゲーム
- **スクラムショーワークショップをやってみよう＆カイゼン(森)**
- みんなでふりかえり

# 得られたもの（内面）

1. 開放

普段からやっている「意識して知らない人に話しに行く」「情報交換して学びに変える」「場を見る」「ファシリテートを意識する」を全部捨てました。
話したい人としか話していないし、嫌なことには近づかないし、やりたいことしかしない。これだけ自分本位で動いた経験はほとんどありませんでした。

2. 本当に好きなもの

ふりかえりのことは考えないようにしていたのですが、疲れたなー、と感じた時に「やりたい」と感じたことは、結局ふりかえりでした。やっぱりふりかえりが好きなんだなー、と自覚しました。

3. 自分の特性

新しいものや楽しそうなことはとりあえず手を出してみる、という自分のパーソナリティ。湖に散歩したり、けん玉を教えてもらってハマったり、普段ほとんどやらないボドゲをやったり。この場の雰囲気があったからこそ、色々やってみようと思えたのかもしれません。

# 得られたもの（経験）

1. けん玉

yycr2019と言えばけん玉、というくらいけん玉やってました。多分4時間は触った。汗かくくらいやった。
そもそも大皿にすら乗せるのが困難だったのに、世界一周や灯台も一度出来るようになったし、帰ってからけん玉をポチって、家でやっています。

けん玉を持ってきたy.ohtaさんと、ライバルだった（もう追い抜かした）笹さんには感謝。

けん玉をやりながら、頭の中でカイゼンが回っていくのが分かるんです。この角度でやるとこうなる、じゃあ次はこの変数を変えると...みたいな。また、他の人の動きを見て、「こういう体の使い方をするとこう動くのか」と分析してみたり。
無意識に高速なふりかえりが行われていたのだと思います。

2. 大人数でのワークショップ作成

一人でワークショップを考えることは何度かありましたが、大人数で考えて実際に形にするところまでを1日でやり遂げられたのはいい経験でした。ワークショップ作りのプロセス自体がアジャイルで、他のコーチの方たちから気付きを得ながら、これまでの知識や経験が高速でアップデートされていく感覚。自分が成長していく感覚をずっと味わっていられました。
ここち作られたワークショップである「スクラムショーワークショップ」は、すごくいいものです。ぜひ、色んな人にやってもらえるようになるといいなと思います。
[記事はこちら](https://qiita.com/viva_tweet_x/items/0afaa163b5b3085ca893)

3. ふりかえり読本の次のネタ

ふりかえりの話を一人で考えるかー、と画用紙広げていたら、わらわらと人が集まってきました。新しいふりかえりのやり方を編み出す、という元々想定していた結果には至りませんでしたが、ふりかえりを広めるための、私だからこそ書けるネタを得ることができました。これは、そのうち必ず本にします。

4. モブブロギング

モブプロは実を言うとずっと自己流で、社外の人とやる機会がありませんでした。普段からモブモブしている人たちに流されながらモブをやって「たーのしー」となって。
記事の大枠を作るまででしたが、一人だと綺麗な構成にならなかっただろうなーと思います。

# Retreat

がっつりリトリートされました。今までどこか無理していたんだろうな、と気づかされました。全員が自由で、かつ顔見知りもそこそこ多い。そんな環境だから、解放されて、でも気ままに動く中でそんな中で自分が真に欲しかったアウトプットも出せて。普段の仕事以上にイノベーティブなことをしていたという自覚があります。

# 黒と黄色

普段は黄色のシャツを着ていますが、ここでは黄色のシャツは持ってきていませんでした。そして闇を吐き出したり、やりたいように動いていた結果、黒い森というあだ名が生まれました。

yycr2019の感覚を覚えておけば、黒いTシャツでスイッチを入れることが出来そうです。
カンファレンスやイベント等で、私が黒い服を着ているのを見かけるようになる日も来るかもしれません。

# 次があれば

また参加したいです。きっと、次回は今回以上に自由になれる気がします。
組み込みモジュール１－関数デコレータを定義

関数に適用することの出来る、デコレータという機能がある。
関数をラップすることで、関数の前後に処理を追加することが出来る。
それによって、入力値、計算結果へアクセスし、値を変更することが出来る。

セマンティック強化、デバッグ、関数登録等を行うための強力なツールとなる。

# デコレータ
ある関数をラップして、処理を改変したい場合には、デコレータ機能を利用する。
ラップする、デコレート関数を用意し、＠を用いて関数に適用する。

自作したデコレータ関数名を@で指定し、ラップしたい関数を引数として呼び出す事で、デコレータ機能が有効化される。

呼び出した関数を、目的にそって既述することによって、デコレートされた結果を出力することが出来る。

# 注意点
上記の実装だけでは、インストロペクションを行う処理を、正常に行えない。
解決する方法として、wrapsの利用が挙げられる。
適切にwrapsを実装すると、Python標準のインストロペクション機能を利用できるようになるので、忘れず適用したい。
Unity2018.3以降のPrefab作成

2018.3以降では、NestedPrefab対応でPrefabUtilityが変わっていて、
色々調べたのでPrefabUtilityの使い方のメモとして残しておこうと思います。

### ＃スクリプトからPrefab作成
EditorスクリプトでFBXデータからそのままPrefabを作成する。
2018.2以前はこれでプレファブ作成できた。

``` old.cs
  var obj = AssetDatabase.LoadAssetAtPath("Assets/fbx.fbx", typeof(GameObject)) as GameObject;
  PrefabUtility.CreatePrefab("Assets/prefab.prefab",obj);
  AssetDatabase.SaveAssets();
```

ところが、2018.3以降は「PrefabUtility.CreatePrefab()」が非推奨になっていた。
いつなくなるかわからないので、この際新しい書き方に切り替えてみようと思う。

色々試した結果、以下のコードでいけるようだ

``` new.cs
{
  var obj = AssetDatabase.LoadAssetAtPath("Assets/fbx.fbx", typeof(GameObject)) as GameObject;
  //　インスタンス化
  var game = PrefabUtility.InstantiatePrefab(obj) as GameObject;

  // 〜　処理　〜

  // プレファブ作成
  var prefab = PrefabUtility.SaveAsPrefabAsset(game, "Assets/prefab.prefab");
  //　リンクを解除
  PrefabUtility.UnpackPrefabInstance(game, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
  // シーンから削除  
  Object.DestroyImmediate(game);
  AssetDatabase.SaveAssets();
}
```
以前はシーンにインスタンスを作成しなくてもできたが、今回からは一度シーンに作成しないとダメのようなので
最後にシーンから削除するようにした。



ちなみに、リンクについてはあんまりわかっていない。。即反映されるわけでもなさそうだし。。
リンクについては今後調べていこうと思います。

なお、リンクなしでインスタンス化するメソッドもある模様？（インスタンス化時にCloneとつく）

``` .cs
  var game = PrefabUtility.InstantiateAttachedAsset(obj) as GameObject;
```
組み込みシステムでのシリアル通信の実装

シリアル通信は、かつてコンピュータ間でデータを転送するための最も広く使用されている方法でした。あなたが長い間使ってきた多くのコンピューティング機器はシリアル通信を採用しています。

このタイプのデータ転送に関連する用語のいくつかは、FTDIチップ、COMインターフェイス、RS232プロトコル、およびシリアルポートです。

**シリアル通信**とは、一度に1ビットずつ電子的に送受信されるデータのことです。それはパーソナルコンピュータ間のデータ伝送の基盤を形成します。その使いやすさは、それをパーソナルコンピュータおよび周辺機器の初期の製造業者にとって魅力的なものにした。

あなたのラップトップにシリアルポートが含まれていない可能性が高いです。それらは、USB規格によって提供されるより高速なインタフェースと接続によって、消費者市場で大部分置き換えられています。

しかし、シリアル通信はまだ終わっていません。多くの産業用および商業用プロセスは、シリアル通信のシンプルさと安定性に依存しています。

産業用オートメーションシステムはますます普及してきており、さまざまな企業のビジネスプロセスにとって不可欠です。

これらは、生産施設でさまざまな機能を実行する一連のネットワーク化された電子機器で動作します。これらのデバイスは、複雑な自動化プロセスの制御に役立つセンサーおよびモニターとして機能します。

産業用オートメーションシステムのコンポーネントの多くは、相互に通信するためにシリアル通信に依存し、それらを制御するコンピュータに依存しています。

これらは、これらのオートメーションシステムでの使用を容易にするためにシリアルポートで設計されています。シリアルアプリケーションはデバイスを制御するために使用され、通信するそれらの能力は世界中の無数の生産ラインの操作に不可欠です。

##組み込みシステムは、シリアル通信をさまざまな方法で利用します。
組み込みシステムがシリアル通信を使用する方法のいくつかは以下のとおりです。

- デバイスのさまざまな部分が互いに通信できるようにする。
- ファームウェアアップデートをダウンロードして最新の状態に保ちます。
- ユーザーにテストまたはデバッグ用のインターフェースを提供する。
- 外部機器との通信
- 
組込み機器にシリアル通信を実装することを任されている開発者やソフトウェアエンジニアは、このデータ伝送方法のバックボーンを形成するプロトコルに関する彼らの知識に頼らなければなりません。

多くのシリアルプロトコルが使用されていますが、RS232 / RS422 / RS485規格が最も一般的に使用されているものです。

これらのプロトコルは産業用オートメーションやシリアル通信を利用する組み込みシステムで広く使われています。

USBからシリアルへのコンバータなどのデバイスを使用することで、シリアルポートを持たないコンピュータでもシリアルデバイスと通信できます。

シリアル通信には、イーサネット、USB、WiFiなどの代替テクノロジよりも優れた利点がいくつかあります。これらの他のオプションはより速い接続速度を提供するかもしれませんが、それらはシリアル技術ほど実装するのが容易ではありません。

ほとんどのオペレーティングシステムはシリアルポートのサポートを内蔵しており、インターフェイスにカスタムドライバをインストールする必要はありません。

現世代のラップトップにはシリアルポートがないことが、シリアルデバイスやアプリケーションとやり取りする必要がある開発者にとっての障害になる可能性があります。

それは間違いなく彼らの開発努力を妨げ、そして彼らの製品をテストしそしてトラブルシューティングすることを困難にする。しかし、あらゆるWindowsコンピュータのポート制限を回避するのに役立つことができるハードウェアとソフトウェアの解決策があります。

USBとシリアル信号間の変換に使用できる単純なハードウェアデバイスがあります。これらのデバイスは使いやすく、コンバータの一方の端をラップトップのUSBインタフェースに接続する必要があります。

コンバータのもう一方の端は、シリアル周辺機器に接続するために使用できるシリアルインタフェースです。そのデバイスは、シリアルポートが装備されている場合と同じ方法でラップトップで動作することができます。

シリアルインターフェースの欠如に対するより効果的な解決策は、あたかも実際の物理インターフェースであるかのように振る舞う仮想シリアルポートを作成することができるソフトウェアを採用することです。

これにより、追加のハードウェアとそれに関連するケーブル配線が不要になります。

これは、仮想シリアルポートを作成する機能をユーザーに提供するソフトウェアツールの2つのバージョンです。シリアルハードウェアおよびソフトウェア開発者およびテスターはこれらのツールが不可欠であることを見つけるでしょう。

シリアル通信を採用している組み込み機器で作業しているユーザーにも同じことが言えます。

**仮想シリアルポート**を使用すると、物理シリアルポートを持たないコンピュータがシリアルデバイスと通信することができます。

仮想COMポートは、周辺機器とのケーブル接続ではなく、ネットワークを介してデータを送受信することによって機能します。
これは、特殊なソフトウェアを使用してコンピュータを構成することによって達成されます。 仮想ポートを作成するための優れた選択肢は、Eltima Softwareの[Virtual Serial Port Driver](https://www.virtualserialportdriver.com/)（VSPD）です。
![uploading-0]()


この通信ソフトウェアは、無制限の数の仮想シリアルポートの作成を容易にします。 仮想ポートは、仮想ヌルモデムケーブルを介して接続できます。 作成したすべての仮想インターフェイスは、物理ポートの機能を完全にエミュレートします。

VSPDを使用すると、仮想ポートを簡単に作成および管理し、シリアルデバイスの機能を拡張することができます。 このソフトウェアを使用すると、物理インタフェースがなくても、どのマシンからでもネットワーク接続のシリアルデバイスにアクセスできます。

今やあなたはシリアルデバイスを世界の反対側にいても十分に利用することができます。

VSPDはスタンダード版とプロ版があり、14日間のトライアルで両方の製品を試乗することができます。
さくらサーバー PDOがSQLSTATE[HY000] [2002] No such file or directory

いろいろ調べたけど下記を修正するだけで通った。

define('PDO_DSN','mysql:dbhost=localhost;dbname=' . DB_DATABASE);
↓
define('PDO_DSN','mysql:host=localhost;dbname=' . DB_DATABASE);
IntelliJ IDEA で "Cannot Run Git (xcrun: error:)" が出たときの対処

新しい Macbook Pro に旧 Macbook Pro から移行後、IntelliJ を起動したら出たエラーに対処したときのメモ

エラーダイアログなどは以下のリンク先に詳しい
https://intellij-support.jetbrains.com/hc/en-us/community/posts/360002200340-Cannot-Run-Git-xcrun-error-invalid-active-developer-path

# 原因
色々調べたところ、どうやら XCode 絡みの問題らしいと分かった。
取り急ぎ今までどおりの状態にしたかったので詳しいことは調べてないが、
いずれも統合開発環境だし、XCode は Apple のやつだし、何かあるのかなと想像している。
もしかして Mac 使いの人には常識的なことなのかもしれない。
後、実はターミナルからの実行は可能で、一時回避できたのかなと今思っている。

# 対処
私のケースだと以下の手順でエラーを解消できた
1. Apple の App Store から XCode をダウンロード
2. ダウンロード完了後、ターミナルを開いて以下のコマンドを実行
`sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`
`sudo xcodebuild -license`

# 参考にしたサイト
- https://intellij-support.jetbrains.com/hc/en-us/community/posts/360002200340-Cannot-Run-Git-xcrun-error-invalid-active-developer-path
- https://intellij-support.jetbrains.com/hc/en-us/articles/207241205-Mac-OSX-can-t-start-Git-after-updating-Mac-OS-XCode

# 対応経緯
私が対処したときにどんな手順を辿ったのか、一応記載しておく
1. 冒頭のエラーが出る
2. エラーダイアログ内のリンク Configure をクリックして Git の設定を開いて内容を確認する
3. 特に関連しそうな設定が無かったので閉じる
4. 同じくエラーダイアログ内のリンク Download をクリックして Git のダウンロード/インストールを実行（移行時に何かが失われたのかなと思った）
5. 解消しなかった
6. エラーダイアログに出ているディレクトリパスが存在するかを確認した
7. 存在しなかったので、アプリケーションか OS の設定不備だなと想像した
8. まずアプリケーションからかなと思って、JetBrains から情報が出ていないかを Google した
9. 結構な数がヒットした（あるあるのようだった）
10. 参考文献にあるサポート情報から自分の状況にマッチしそうなものを探り当てて実行した
11. 解決
[node.js]Expressでパスワードのハッシュ化を行おう

はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います

#はじめに
パスワードをデータベースに保存するときにはその情報をハッシュ化する必要があります。
なぜパスワードをハッシュ化しないといけないかなどを詳しく知りたい方は[こちら](https://glorificatio.org/archives/3417)を参考にしてみてください。
今回は[bcrypt](https://www.npmjs.com/package/bcrypt)というライブラリを使ってデータのハッシュ化を行って行きたいと思います。

##実装
###インストール
`npm install bcrypt -s`
###パスワードのハッシュ化
まずExpress内の自分がデータをハッシュ化したいファイル内で以下のように記述してください

```hash.js
const bcrypt = require('bcrypt');
const password = "hoge";
let hashed_password = bcrypt.hashSync(password, 10);
console.log(hashed_password);
```
`"hoge"`という文字列がランダムな文字に変換されています。
###ハッシュ化されたパスワードの照合
ハッシュ化されたパスワードと自分の知っているパスワードの照合を行っていきます

```hash_confirm.js
const bcrypt = require('bcrypt');
bcrypt.compareSync("hoge", hash_password) // =>ture
bcrypt.compareSync("fake_hoge", hash_password) // =>false
```
 * `hash_data`はもともと`"hoge"`をハッシュ化したものなので、`bcrypt.compareSync(hash_data, "hoge")`は**true**となります。
 * `bcrypt.compareSync(hash_data, "fake_hoge")`はハッシュ化される前のデータと比較されているデータ(今回は`"hoge"`と`face_hoge`を比較している)が異なるので**false**となる。

#最後に
今回は非常に簡単なhash化の方法をご紹介しました。
もっと詳しいことが知りたい場合は[こちら](https://www.npmjs.com/package/bcrypt)を参照ください。callbackやasync/awaitでエラーハンドリングを行うことも出来ます。
なにか間違いなどありましたら指摘していただけると幸いです。
AWS Glueの概要を図と用語で整理する

[AWS Glue](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/what-is-glue.html)をざっくりと理解するために基本的な概念とコンポーネントを、図と用語で整理してみます。

# AWS Glueとは?
- **フルマネージド・ETL&データカタログツール**
    - ETL = どっかからデータ引っ張って、いい感じに変換してどっかに突っ込むこと
    - データカタログ = データ活用をしやすくするためのメタデータの目録

# ざっくりとした概念図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50056/20ea4c59-cc30-3cd3-1274-a91e74115efe.png)



# 特徴
- サーバレス
- 高セキュリティ
- [etc..](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/what-is-glue.html)


# 用語
- データストア
    - S3, DynamoDB, RDBなど
- データソース
    - Glueへの入力に使われるデータストア
- データターゲット
    - Glueからの出力に使われるデータストア
- データカタログ(Data Catalog)
    - Glueを利用するための箱
    - ジョブ、メタデータ(データベース,テーブル)などGlueに関わるコンポーネントはすべてここに含まれる
    - 1AWSアカウントの1リージョンにつき、1データカタログ
- データベース
    - データカタログに含まれる。テーブル等をまとめておくもの。
- テーブル
    - データベースに含まれる
    - データソースについてのメタデータを格納したもの
    - あくまでメタデータを格納しており、実際のデータは含まない
- 分類子
    - データのスキーマを決定する
- [その他の用語](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/components-key-concepts.html)


# メモ
- テーブルは手動(またはCloudformation等)でも作成できるが、クローラから作成するのがミスが少なくて良い
- CloudformationはデータソースにDynamoDBを使用したクローラの作成には未対応(2019/06現在)
    - というかDynamoDBはいろいろ未対応。まだDynamoDBには少々使いづらい印象。
iPhoneでテザリング時にUIの表示崩れを見かけたので原因を探ってみる

#iPhoneでのテザリング

iPhoneでのテザリング時には画面上部にOS側で使用する領域が増える。
`インターネット共有： X台接続中`の分、画面が縦に狭くなる。

| 通常時 | テザリング時 |
|:-:|:-:|
| ![Screen Shot 2019-06-25 at 17.01.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53273/342f1afb-f879-1d70-0a04-a4936977508d.png) | ![Screen Shot 2019-06-25 at 17.00.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53273/4a3316d7-0576-8af1-05d2-feda740fa0f2.png)
 |

これが影響し、以下のような挙動のアプリを割と見かける。
- スプラッシュ画面のロゴの中段が潰れる
- スクロールが最上部/最下部までスクロールできない
- 画面上部が潰れる
- フッタボタン群が一部しか表示されない
- 下スクロール時にフッタボタン群が隠れるような動作をする場合、上スクロールでフッタボタン群が一部しか表示されない

| 通常時 | テザリング時 |
|:-:|:-:|
| ![Screen Shot 2019-06-25 at 16.54.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53273/2e9c946d-5cd1-cc61-8db1-04f177dee0ee.png) | ![Screen Shot 2019-06-25 at 16.55.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53273/761a0ad5-0a40-bc73-bf8a-d8830311d4aa.png) |

#原因を推測してみる
表示だけの問題で、UI操作自体には問題ないようなので、
高さ取得に問題があると推測する。

#検証
コードで通常時/テザリング時の画面の高さを取得する。
検証時の端末：iPhone 8 Plus(iOS12.3.2)

| 高さ取得 | コード | 通常時 | テザリング時 |
|:-:|:-:|:-:|:-:|
| 画面の高さ | UIScreen.main.bounds.size.height | 568 | 568 |
| ディスプレイの高さ | UIScreen.main.nativeBounds.size.height | 1481 | 1481 |
| 基底viewの高さ | ViewController#view.frame.height | 568 | 548 |

違いがあるのは、基底viewの高さのみ。

iPhone Xシリーズでの検証をしていませんが、
見た目では、テザリング時にOSが使用する領域は変わらないため、
表示上の問題はなさそうです。
（テザリング時はSafe Areaの表示領域が変わらず見た目が変わるだけです）

#対策案
（実際にコードを書いて対策案を検証した訳ではないため、現段階では案のみです）

`画面の大きさを取得し、それを元に表示領域を判定する`と
テザリング時に表示領域が狭くなった場合に、
画面自体の大きさは変わっていないため、
表示やUI操作に問題が発生する可能性があります。

また、アプリ画面表示中に別のiPhoneからの操作により、
突然テザリング時の表示に切り替わるため、
画面初期表示時の基底viewの高さもあてになりません。

これらに柔軟に対応するためには、
`AutoLayout`と`NSLayoutConstraint`で画面を定義し、
自動的にサイズや位置を調整したり、
`NSLayoutConstraint`で上限/下限などを定義などの対策になると思われます。

#追記：対策案2
- UIApplication.didChangeStatusBarFrameNotification

OSが使用するステータスバーの表示領域変更を検知できるので、
アプリ画面表示中の`突然のテザリング表示への切り替わり`に対応できます。

#追記：メモ
- テザリングの他にも通話中のアプリ操作も同様に、画面上部のステータスバーの領域が増える
- iOSシミュレータのHardware/Toggle in-call Status Barで再現できるため、実機検証より手軽
python3系でJANコードを読みたい件

#はじめに
　python3系でJANコードを簡単に読みたいと思いました。

#環境
　ubuntu 18.04.2 LTS
　python 3.6.7
　zbar 0.10
　pyzbar 0.1.8
　pillow 6.0.0

#環境構築
```
$sudo apt install libzbar0 libzbar-dev
$pip3 install pyber
$pip3 install pillow
```
#対象JANコード
![jan_code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413003/8b031a22-01ba-2c9b-8ba6-236967959e57.png)

##サンプルコード
```python:decode.py
from PIL import Image
from pyzbar.pyzbar import decode

jan_image = "jan_code.png"

decode_data = decode(Image.open(jan_image))

print(decode_data[0][0].decode('utf-8', 'ignore'))
```
#結果
```
1234567890128
```

#まとめ
簡単にJANコードがデコードできました。
Mahalo