OmniAICreator/Qiita-1.07M huggingface dataset:
GASでBQのクエリ結果を得る

業務でGASとBQ(BigQuery)を連携して、BQのクエリ結果をGoogleスプレッドシートに転記する、みたいなことが実現できました。この記事ではクエリ結果を得る部分について共有したいと思います。

# なにはともあれコード例
大元の出典があるのですが、そのコードを1つの関数にまとめて、 `var` を `let` や `const` に書き換えました。 

```javascript
function queryTo(query, projectId) {
  let queryResults = BigQuery.Jobs.query({query: query}, projectId);
  const jobId = queryResults.jobReference.jobId;
  
  // Check on status of the Query Job. Wait until job status become complete.
  let sleepTimeMs = 500;
  while (!queryResults.jobComplete) {
    Utilities.sleep(sleepTimeMs);
    sleepTimeMs *= 2;
    queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);
  }
  
  let rows = queryResults.rows;
  while (queryResults.pageToken) {
    queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId, {
      pageToken: queryResults.pageToken
    });
    rows = rows.concat(queryResults.row);
  }
  if (!row) {
    return null;
  }
  
  // convert the result to array
  let data = new Array(rows.length);
  for (let i = 0; i < rows.length; i++) {
    let cols = rows[i].f;
    data[i] = new Array(cols.length);
    for (let j = 0; j < cols.length; j++) {
      data[i][j] = cols[j].v;
    }
  }
  
  return data;
}
```

使い方はこんな感じ：

```javascript
const projectId = ...;
const data = queryTo(`
  #standardSQL
  SELECT 1,2,3 UNION ALL SELECT 4,5,6
`, projectId);
console.log(data); // [[1,2,3],[4,5,6]]
```

クエリ結果が2次元配列で取得できます。フィールド名は捨てられるので、添字で頑張ってアクセスする感じですね。

# 所感

BQのクエリ結果はredashとかtablueとか使って可視化するのが王道ですが、GASを経由してスプレッドシートに転記するのもそれなりに便利だなと思いました。以下その理由。

## Excelに慣れている人にとってスプレッドシートはわかりやすい

特に業務において、社員全員がエンジニアじゃない場合は、モダンなWebアプリの操作感よりも、Officeスイートに似た操作感のほうがウケが良いときがあります。

## GASもBQもGoogleのクラウドサービスなので連携しやすい

そもそも上記GASで使っている `BigQuery` サービスオブジェクトもチェックボックス一つで有効になりますし、アクセスできるBQのプロジェクトはそのスクリプトを実行したユーザーに依存するので、権限のないユーザーが間違えてBQにアクセスすることはありません。

## クエリをJavaScriptで構築できる

個人的にはこれが一番メリットあるかな、と思いました。異なるテーブルから似たようなクエリを作って `join` していい感じの `where` 書いて...のようなSQLを書くことが多いと思います。普通はコピペで対応して結果長大なSQLが出来上がるものですが、GAS(JavaScript)でクエリをかけるので、重複するクエリ部分文字列は変数に入れていい感じに重複を削除できます。

## BQのTIMESTAMP型はJavaScriptのDate互換ではない

1点だけ躓いたのがこれ。一番めんどくさくない変換は string にキャストすること。結果は Date コンストラクタに適合します。

# まとめ

GASでBQのクエリ結果を得る方法を共有しました。例では2次元配列になっているため、 `Range` の `setValues` メソッドに突っ込むだけでスプレッドシートに結果を反映させることができます。もし、BQやredashのクエリ結果をExcelやスプレッドシートにコピペして眺めている作業があれば、GASを使ってトイルを撲滅しましょう。

以上です。
ArchBang / Arch Linux にJDimをインストールする

厳密には僕が使っているのはArchbangです。が、Arch Linuxでもいけると思います。

```
$ cd /tmp
$ git clone https://aur.archlinux.org/jdim-git.git
$ cd jdim-git
$ makepkg -i
$ sudo pacman -U jdim-git-r3338.f9c43265-1-x86_64.pkg.tar.zst 
```
https://aur.archlinux.org/packages/jdim-git/
ビルドに時間はかかりますが手順自体は拍子抜けするほど簡単です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159724/57eb5e6e-da3c-2ca9-1714-3d570de25602.png)
画面はビルドが完了し、sudo pacman -Uしているところです。しかしArch久々に触れましたがクリーン、シンプル、軽快でいいですねこれ。早くもUbuntuを離れて暮らし始めています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159724/59bdb9b0-c818-995b-d617-572f1074c11e.png)
>
[バージョン] JDim 0.5.0-beta20201219(git:f9c4326522)
[ディストリ ] ArchBang Linux (x86_64)
[パッケージ] バイナリ/ソース( <配布元> )
[ DE／WM ] LXDE
[　gtkmm 　] 3.24.3
[　glibmm 　] 2.64.5
[　TLS lib　] OpenSSL 1.1.1i  8 Dec 2020
[オプション ] '--with-native'
'--with-tls=openssl'
'--with-alsa'
'--with-pangolayout'
[ そ の 他 ] LANG = en_US.UTF-8

ではまたどこかで＼(^o^)／にゃーん😺❤
VCIにMatCapで光沢をつける

あなたのVCIをひと手間かけていい感じにする方法です。
なんでそうなるのか、はあんまりよくわかっていないので各自ググってください。

# この記事は、こんな人向けに書いています
- Unityを使って自分でVCIを出力できる
- Unityのシェーダとマテリアルがなんとなくわかってる

# 用意するもの
JMO Assets（導入パートから説明します）

# 親の顔よりみた画面
UnityでVCIを出力したみなさんは、こんな画面になっていると思います。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/6507c59a-eb25-c8ae-dab7-00c834c7a350.png)
今回の3D素材は、[【cluster用アイテム】生秋刀魚](https://booth.pm/ja/items/2407428) からお借りしました。
シェーダはVRM/MTOONを使用しています。サンマがのっぺりしていますね。

蛇足；
シェーダがStandardだとUnity上でいい感じに見えたりするのですが、
バーチャルキャストにもっていくと微妙になるので私は基本的にVRM/MTOONを使っています。
（ここらへんは宗教問題かも）

# 下ごしらえ（JMO Assetsの導入）
1. Asset Storeの画面を出します。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/dd7e8529-383b-1f66-0094-18a6372f6d55.png)
1. "free matcap shaders"と入力して検索します。
1. 検索ワードと同じやつが出てくれるので、それをクリックします。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/0dbbb4e1-bb36-c67d-0396-4c0322830598.png)
1. まずはDownloadをクリックすると、ダウンロードが始まります。
1. ダウンロードが完了するとボタンがImportに変わるので、それをクリックするとunitypackageのインポートが始まります。いつものようにインポートします。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/9f066c02-ff0c-1050-cef9-d99dacf4c689.png)
1. すると、例のごとくAsset直下に"JMO Assets"というフォルダが生成されます。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/176c5911-6239-9cd4-77c7-67597aab2f9e.png)
これで下ごしらえが完了しました。

# 光沢をつける
1. もともと割り当てていたマテリアルを選択し、ctrl+D で複製します。
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/5f6737b1-8caa-2208-d4a2-6beff20335ef.png)
複製したマテリアルには適当に名前をつけてあげましょう。光沢をつけたいので sanma_pika としました。
1. マテリアルsanma_pikaをドラッグしてScene中にあるサンマの上にもっていくことでマテリアルを更新します。複製したマテリアルなので外見は変わりません
1. Inspectorの右側にある鍵アイコンをクリックして、勝手にInspectorのタブの中身が切り替わらないように固定します。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/a6fea822-8b60-33c5-58f0-ea11df72016c.png)
1. 先程生成したJMO Assetsフォルダの中にある画像のフォルダ（Assets > JMO Assets > MatCap Shaders > MaCrea）にたどりつきます。
1. フォルダ内にある MaCrea_4を選択して、EmissionのMatCapの中にぶち込みます。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935496/ed518bca-6009-f451-0fa1-13181248ce3a.png)
するとどうでしょう、サンマが輝きました。やったね！

# 応用編
今回は MaCrea_4 を使いましたが、JMO Assetsの中には他にも色んな種類の画像があるので、
MatCapにぶち込んでみていい感じに光ってくれるやつを探してみましょう。
[Rails]ActiveStorageでファイルダウンロードするAPIを作る[S3]

# やること
バックエンドでRailsを使用した場合に、URLを叩いてファイルをダウンロード出来るようにします。

# 前提
ActiveStorageでファイルをアップロードしていると仮定しています。**保存先は(S3 or minio 使用)**

# 実装
### ルーティング
今回は、**/upload_files/:id/download**となるようにルーティングを設定します。

```ruby
resources :upload_files do
  member do
    get :download
  end
end
```

### コントローラ

```ruby
 def download
    upload_file = UploadFile.find(params[:id])

    file = upload_file.file.blob.download  # ダウンロードする

    if send_data(file, disposition: 'attachment',  # ダウンロードしたファイルを送信する
      filename: upload_file.file.blob.filename.to_s, # ファイル名の取得
      type: upload_file.file.blob.content_type) # content_typeの取得
      head :no_content # 送信出来たら、no_contentを返す
    else
      render json: upload_file.errors, status: :not_found  # エラーを返す
    end
  end

```

``file = upload_file.file.blob.download``とすることで、実際にファイルをActiveStorageからダウンロードすることが出来ます。

上記のやり方がなかなか見つからず困っていたのですが、**ActiveStorageの公式ドキュメントに書いてありました！**
やはり困ったら公式が一番ですね。
https://railsguides.jp/active_storage_overview.html

他にも``upload_file.file.blob.filename.to_s``とすることで、ファイル名を動的に取得しています。
これによって、**ファイル名に正しく拡張子が含まれます。**

# 感想
困ったら公式を読むことが大事だと実感しました！
Apple Silicon対応版のDocker Desktop(Preview)が一般公開されたので試してみます

Apple Silicon対応版のDocker Desktopが一般公開されたとのことで試してみました。人柱としての役割でしょうか。。

検証環境はこちら。
- MacBook Pro (13-inch, M1, 2020)
- macOS Big Sur 11.1

※お試しになられる場合は元ページをよく読み、自己責任でお願いします

## ダウンロード

下記ページからダウンロードが可能でした。

[Download and Try the Tech Preview of Docker Desktop for M1 - Docker Blog](https://www.docker.com/blog/download-and-try-the-tech-preview-of-docker-desktop-for-m1/)

ページ後半`How We Got to a Technical Preview`の項目にダウンロードリンクがありました。こちらのリンクからだと誰でもダウンロードできます。

![スクリーンショット_2020-12-19_15_51_08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/e008a9be-5528-77a0-164e-8edd67c78ea4.jpeg)

ファイル名は`Docker-AppleSilicon-Preview7.dmg`

## インストール

`.dmg`ファイルをダブルクリックし、いつものドラッグアンドドロップですが、このPreview版はUniversalではないので、IntelMacで開いた場合、動作しないAppであることがわかると思います。

![スクリーンショット 2020-12-19 15.47.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/5cf53547-629a-0fb6-83fa-bf82742f4e47.png)

M1（ARM）Macで開くと大丈夫です。

![スクリーンショット 2020-12-19 15.58.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/c210661c-8f4b-8229-8068-a270f9b70f2d.png)

## 起動

`/Applications/Docker.app`をダブルクリックするなり、Spotlightから開くなりで起動します。



起動は全く問題はなさげ。

## 試しに`docker-compose`

手前味噌で恐縮ですが、[コード20行だけで初めるDocker - Qiita](https://qiita.com/teixan/items/73e7d631453776184160)こちらで作ったコードで`docker-compose`を試してみました。

![スクリーンショット_2020-12-19_16_22_33.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/2392f86b-ec73-7c5c-0b91-0f3371949f2d.jpeg)

無事に起動しました。ブラウザからのアクセスも無事に通りました。動作は今の所**軽快な印象です**

Dashboardでも確認できます。

![スクリーンショット_2020-12-19_16_13_57.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/13c2dfc5-d5f2-9b42-6757-7d31bee9429b.jpeg)

## 注意点

ちゃんと検証はできていませんが、古い構成を起動しようとしたら下記のようなエラーが出て立ち上がらないものもありました。

```shell
ERROR: no matching manifest for linux/arm64/v8 in the manifest list entries
```

![スクリーンショット_2020-12-19_16_04_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/5e5cba4b-7491-c0aa-d16e-cb803125b68a.jpeg)

これは読んだ感じだと、`arm64`版がないことが原因らしいので、docker hubなどを確認してみましょう。

[マニフェスト一覧のエントリに、linux/amd64 用のマニフェストが存在しない – CircleCI Japanese Support Center](https://support.circleci.com/hc/ja/articles/360000347167-%E3%83%9E%E3%83%8B%E3%83%95%E3%82%A7%E3%82%B9%E3%83%88%E4%B8%80%E8%A6%A7%E3%81%AE%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%81%AB-linux-amd64-%E7%94%A8%E3%81%AE%E3%83%9E%E3%83%8B%E3%83%95%E3%82%A7%E3%82%B9%E3%83%88%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%97%E3%81%AA%E3%81%84)

## PR

各種アプリがM1（ARM）Macに対応しているかをみんなが共有できるウェブアプリを作りました。
もし、動作が気になるアプリがあれば調べられますし、もしM1Macをお持ちで他の人にもお知らせしたい情報があれば登録やコメントしておいていただけると助かります。

<a href="https://womm.work/" target="_blank"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546514/c264d81f-d8bc-9ff6-ba35-f18ff4ea4a56.png" alt="WOMM project" style="width:50%;margin:auto;"></a>
[WORK ON M1 MAC](https://womm.work/)

投票だけでもご協力いただけると幸いですm(_ _)m
macOS Big Sur (11.1)で Arduino IDE から ESP8266にアップロードできない問題の解決

macOS Bug Sur (11.1) にしてから初めて Arduino IDE を使ったら、ESP8266 にスケッチをアップロードできない問題が発生。
Arduino IDE は最新の 1.8.13にして、ボードとライブラリーもアップデートしたのですが改善されず。

## エラーの現象
以下のエラーでボードにアップロードできないという現象です。
"pyserial or esptool directories not found next to this upload.py tool."

わかる範囲で設定を変えてもどうにもならなかったので、ネットで探したら以下のサイトに解決方法がありました。

https://forum.arduino.cc/index.php?topic=702144.0

## 解決方法
以下は、上記サイトからの引用：
1.- 次のファイルをテキストエディタで開く ~/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/tools/pyserial/serial/tools/list_ports_osx.py
2.- 29 行と 30 行をコメントアウトして下の2行を加える:
   iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
   cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')

下のようにします

```
#iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit'))
#cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
```

このように書き換えて保存したら Arduino IDE を再起動すると、無事に書き込めるようになりました。
アナログ回路 その12

# 概要

circuit sandboxをplunkerでホスティングした。
アナログ回路をシュミレーションで、説明してみる。

# トランジスタ・増幅回路

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/b7315528-edd7-14e4-2936-ee61e13a57a9.jpeg)


# 結果

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5dd29bbb-b783-e9f5-8b61-883affec8066.jpeg)


# 説明

トランジスタは、NPN型。
イン、アウトにコンデンサを入れて、直流をカット。
バイアス電流を上げて、歪を抑える。効率は、落ちる。


# 成果物

https://embed.plnkr.co/plunk/fNb7fw3mhDa6GBBz


以上。
Rails：resourcesとresourceの違い

# 最初に

**カレンダー企画2020の19日目**
プログラミングの勉強を始めて3ヵ月程経ったので学んだことのメモをアウトプットとして記事に残します。
これからプログラミングの世界に入る人の手助けになれたら嬉しい限りです。
間違っていたり、言葉が違っていたり、誤解されるような言葉があったら教えてください^^
言葉を長々と読みづらかったら申し訳ありません。少しずつなれてがんばります。

## resourcesとresourceの違い

**結論は
resources：indexとidが生成される
resource ：indexとidが生成されない**

## resourcesメソッドとは？

railsの基本となる7つのアクション名に対してルーティングが自動生成されるメソッドです。


```ruby:routes.rb
get  "posts/index" => "posts#index"
post "posts"       => "posts#create"
```

こんな感じでアクションごとにルーティングを設定します。
正直めんどくさいよね^^;

そんな時にresourcesメソッドの出番です。

```ruby:routes.rb
resources :posts
```

この一行で7つのアクション名のルーティングが自動生成されます。
とても手間が省けて作業スピード上がります。



```Terminal:ターミナル
$ rails routes

    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PATCH  /posts/:id(.:format)      posts#update
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy
```

ルーティング確認する為にターミナルで確認するとこんな感じで出てくると思います。
ここで注目しておいてほしいのは、indexがあったり、/:idがありますね。



## resourceメソッド

```ruby:routes.rb
resource :posts
```

このようにして同様に出力してみると

```Terminal:ターミナル
$ rails routes

new_posts  GET    /posts/new(.:format)  posts#new
edit_posts GET    /posts/edit(.:format) posts#edit
     posts GET    /posts(.:format)      posts#show
           PATCH  /posts(.:format)      posts#update
           PUT    /posts(.:format)      posts#update
           DELETE /posts(.:format)      posts#destroy
           POST   /posts(.:format)      posts#create
```

こんな感じで出力されると思います。

indexと/:idが無くなっていますね。


## 最後に

### こんな時に使いました

投稿機能にいいね機能をつける時に特別idが必要ないのでresourceを使いました。
userでid管理をしなくて良い時に使用した事もありました。

状況に応じて使い分けみるといいかもです。

個人的にID管理しない状態にしたい時、調べて知ったので書き残しておきます。
Raspberry Pi Zero W で見守りカメラを作ってみた

#はじめに
赤ちゃんが別室で夜泣き、僕は書斎で仕事中、妻はドライヤー中。
気づくのに遅れて可哀想なことをしたなーと反省することがあったので、RaspberryPiとLineNotifyを使って見守りカメラを作ってみる事にした。

よく確認せずにアマゾンでポチってしまった結果、無線非対応のラズパイを買ってしまいました。
家にあったWifiドングルで応急処置したものの、ダサかったため急遽Zero Wを購入してこの記事を書いております。
また、カメラモジュールに関しても、よく確認しなかったため後でトラブルを引き起こします。

この記事ではRaspberryPiの動体検知のみを紹介します。
RaspberryPiOSのインストールやLineNotifyの設定などは割愛します。

#準備
LineNotifyのパーソナルトークンを発行し、控えておきます。
Lineグループに対してトークンを発行することも可能ですが、その場合LineNotifyアカウントをグループに招待する必要があるため注意です。

用意したのは下記
Raspberry Pi Zero W
![スクリーンショット 2020-12-19 15.40.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/d6fcdc3e-91ce-c45d-46ba-46887fa59152.png)
https://www.amazon.co.jp/gp/product/B07BHMRTTY

Raspberry Pi Zero Camera Module 
![スクリーンショット 2020-12-19 15.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/79d29320-8825-902f-c097-4fb1fe2e48ec.png)
https://www.amazon.co.jp/gp/product/B06XFPPBCW

その他、SDカード、リーダ、USB変換ケーブルなどが必要になります。

それでは、ラズパイにカメラモジュールをインストール！

![D07F9794-841B-4371-AB02-43A4EE8178B7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/4debac19-072f-79e6-15d4-141d80e5643c.jpeg)
カメラの向きが逆やんけ...
接続端子の都合上この向きでしか着きません。やらかした。。。
なんとか捻りつつ、ダクトテープなどでカメラを固定しました。**多分そのうち切れます。**

![56E40CF9-D34E-484F-AFED-FE9808868EE3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/5ffb07a8-e68f-e98c-6895-807fd99b53de.jpeg)
うわ、だせえ...

#設定
VNC ViewerかSSHでラズパイに接続し、ターミナルから作業していきます。

四苦八苦しつつもRaspberryPi OSをインストールしました。

では、動体検知用に`motion`というモジュールをインストールしていきます。

```
sudo apt-get install motion
```

続いて`/etc/motion/motion.conf`の設定

```
#解像度上げる
width 1024
height 768

# 10秒以内の動体検知であれば同一イベントとする
event_gap 10

# 一番変化が大きかった画像を保存する
output_pictures center

# 動画は保存しない
ffmpeg_output_movies off

# 動体検知箇の枠表示
locate_motion_mode on

# 画像保存時に、LINE Notifyに送信する
on_picture_save curl -X POST -H "Authorization: Bearer （事前に発行しておいたパーソナルトークン）" -F "message = （好きなメッセージ）" -F "imageFile=@%f" https://notify-api.line.me/api/notify
```
これだけ！

では、`motion`を起動して結果を見てみましょう。

```
sudo motion
```

#結果
ウチの猫にテストに参加してもらいました。
![1271BE2F-2562-4616-AF0F-050ACD91F78B.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/f853e8df-267c-f466-0015-35c72dec9e49.jpeg)
ちゃんと通知されました！

#終わりに
猫の首振り程度でもバンバン通知来てたので、event_gapあたりの設定を見直す必要がありそうです。

本当はLineNotifyではなくLineBotとFirebaseを利用して、こちらの入力をトリガーにあれやこれやとやりたかったのですが、時間が足りずここまでしかできませんでした。。

皆様Amazonでのポチは計画的に！！

![92833FCA-B674-4A52-8064-1F9EF61675BA.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869835/c6ec1992-f115-dfd9-3fc5-7062d4178e48.jpeg)
お疲れ様でした
[Azure Static Web Apps] Vue.js history mode の 404対策

Azure Static Web Apps にデプロイされたVue.jsコンテンツで
ページ遷移した際に、リロードすると404エラーになってしまう。
その対策について。

### なぜ４０４になっちゃう？
実態ファイルのは無いので、404エラーとなってしまう

### 解決方法
Azure Static Web Apps のroutes設定によるリダイレクトで解決する。
(netlify だと _redirects ファイルによる設定）

public ディレクトリ直下に、routes.json を作成。
すべてのルートで/index.html が返されるように以下のよう記述

```routes.json
{
  "routes": [
    {
      "route": "/*",
      "serve": "/index.html",
      "statusCode": 200
    }
  ]
}
```

この設定によって、ページのURLはそのままに
常に、index.html が返されるようになります。

### 関連URL
Azure Static Web Apps プレビューでのルート
https://docs.microsoft.com/ja-jp/azure/static-web-apps/routes
Rails g devise:installが実行できない！

#解決法
rails g devise:installを実行しても動かない...
今まで普通に出来ていたのに！！なんで！

そんな時のはこのコマンドをターミナルで実行してみてください

```perl:ターミナル
$ bundle exec spring stop
```

springを停止することで大体解決します！
TinyDB操作備忘録

勉強用に購入したアプリ制作本から引用したSNS風のwebアプリケーションに
以下の機能を追加したい。
①自分の投稿を削除する機能
②自・他の投稿を音声出力して読み上げる機能

今回は①自分の投稿を削除する機能を実装させる為、
tinydbの操作コマンドを以下にまとめた。
(Ubuntuで動作確認しました)


①sample.dbをインストール&読み込み

```
from tinydb import TinyDB, Query
db = TinyDB('sample.json')
```

②poke_bookテーブルを作成

```
table = db.table('poke_book')
```


③poke_bookテーブルに以下の3レコードを追加

```
# name: フシギダネ, no: 1
# name: フシギソウ, no: 2
# name: フシギバナ, no: 3

dic_list = {[{'name':'フシギダネ', 'no':1}, {'name':'フシギソウ', 'no':2}, {'name':'フシギバナ', 'no':3}]}
table.insert_multiple(dic_list)

# 実行結果
[1, 2, 3]

```

④以下のライブラリをインポート

```
import json
import pprint
```

⑤jsonファイルを辞書として読み込む。
この時にレコードのキー値が現れる。

```
with open('sample.json') as f:
    df = json.load(f)

pprint.pprint(df, width=40)

# 実行結果
{'1': {'name': 'フシギダネ', 'no': 1},
 '2': {'name': 'フシギソウ', 'no': 2},
 '3': {'name': 'フシギバナ', 'no': 3}}
```

今回はこのキー値(1,2,3)を使用してレコードの削除・追加を行う。
(以下を実行すると、キー値が1のレコードを取得できる)

```
print(df['poke_book']['1'])

# 実行結果
{'name': 'フシギダネ', 'no': 1}
```
⑥1番目のレコード(キー値:1)を削除してみる。
レコードの削除はpopやdelで実行できる。
ここではpopを使用する。

```
x = df['poke_book'].pop('1')
print(x)

# 実行結果
{'2': {'name': 'フシギソウ', 'no': 2}, '3': {'name': 'フシギバナ', 'no': 3}}
```
無事、poke_bookテーブルから指定したキー値のレコードを削除できた。

⑦レコードの削除が出来たら、json.dump()を使用してjsonファイルを保存・更新する。
(辞書型として読み込んだjsonファイルを、元のリスト形式にして保存し直すといったイメージ)
書式:*json.dump(テーブル, f(形式), インデント幅)*

```
with open('sample.json', 'w') as f:
    json.dump(df, f, indent=4)
```

⑧ファイルを開きなおし、レコードが削除出来ていることを確認する

```
with open('sample.json') as f:
    print(f.read())

# 実行結果
{
    "poke_book": {
        "2": {
            "name": "\u30d5\u30b7\u30ae\u30bd\u30a6",
            "no": 2
        },
        "3": {
            "name": "\u30d5\u30b7\u30ae\u30d0\u30ca",
            "no": 3
        }
    }
}
```

テーブルへの削除反映が成功!!('ω')ノ
fin.
Mac M1 で Firebase を使用した iOS アプリのビルドが失敗する

# 環境
Xcode 12.3
cocoapods 1.10.0

# 解決方法

Podfile で M1 に対応したライブラリを指定する

```
platform :ios, '14.0'

target 'アプリ名' do
  use_frameworks!

  pod 'Firebase', '7.2-M1'
  pod 'Firebase/Firestore', '~> 7.2-M1'
  pod 'FirebaseFirestoreSwift', '7.2-beta'
  pod 'FirebaseStorage', '~> 7.2.0-M1'
  pod 'Firebase/Auth', '~> 7.2.0-M1'
  pod 'Firebase/Crashlytics', '~> 7.2-M1'
  pod 'Firebase/Analytics', '~> 7.2-M1'

  target 'アプリ名 Tests' do
    inherit! :search_paths
  end

  target 'アプリ名 UITests' do
    inherit! :search_paths
  end
end
```
[UE4] ナビゲ―ション機能を調整する

検証バージョン: UE4.26.0
　本記事では、ナビゲーションの機能を調整する時にどこを変更すればよいか、見ればよいかをまとめたものです。

# ナビメッシュの精度を調整する
ナビゲーションデータの`CellSize`を調整することで、隣接エリアの精度を調整して滑らかなメッシュを生成できるようになります。
![2020-12-18_01h25_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/bf94bf60-3d0e-a5cb-eb1b-0e922fb7660b.png)

# ナビメッシュの傾斜を調整する
ナビゲーションデータの`CellHeight`、`AgentMaxSlope`を調整することで、隣接エリアの判定を広くして滑らかなメッシュを生成できるようになります。
![2020-12-18_01h28_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/18f7f65e-5c80-2e6e-4bc5-64ebd0b9de30.png)

# ナビメッシュの隙間を補完する
Nav Link ProxyやNav Link Proxy Componentを使用します。段差、高所から降りる、隣接面をカバーするようなケースできます。
![2020-12-18_01h29_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/cb90c221-b74c-81e7-fba0-fa43fa2d402b.png)

# ナビメッシュの精度をさらに上げる
RecastNavmeshのRegionPartitioning、LayerPartitioningの方式をMonotoneもしくはChunkyMonotoneに変えることで更に密なメッシュが構築できるようになります（ただし生成コスト増大）。

# 経路探索に優先度をつける
Nav Areaを作成してNav Modifier Componentを配置しコストに重みづけを行います。
![2020-12-18_01h29_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/4df0a539-6a9f-2282-d217-6af41cc3b32a.png)

# ナビメッシュを動的に更新する
RecastNavmeshのRuntime GenerationをDynamicもしくはDynamic Modifiers Onlyに変更する。Dynamic は全て(StaticMesh)などの変更時にNavmeshの再生成を実施、Dynamic Modifiers Onlyは Nav Modifier の変更時のみにNavmeshの再生成を実施します。
![2020-12-18_01h30_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/e3b58837-1e49-499b-9d91-ac0383adb790.png)

# エージェントの種類に応じたナビメッシュを設定する
Project SettingsのSupported Agentsに追加します。Agent数だけNavmeshをレベル上に生成するため、生成コストとメモリに注意する必要があります。
![2020-12-18_01h30_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/de04c790-1cc0-1c8d-128e-582bfc8485c2.png)

# オブジェクトの下部を進行できないようにする
StaticMeshであればbFillCollisionUnderneathForNavmeshを有効にすることで、オブジェクトの下部をヌルエリアを生成できます。

# オブジェクト自体にエリアを設定したい
StaticMeshであればIsDynamicObstacle=trueとしてAreaClassからオブジェクトのクラスを設定できます。

# オブジェクトの空洞の中に生成されるエリアを埋めたい
NullAreaのModifierを埋め込む、CanEverAffectNavigationを無効にするか、少し工夫が必要です。

# サブレベル毎のナビメッシュを展開したい
Dynamicに設定することで予めビルドしたナビメッシュはストリーミングできますが、完全にサポートされていません（ワールドコンポジションのケースなど）。

# パスのコストを調整する
NavigaitonTestingAcotrのPathfinging機能を使うとStepとコスト計算を容易に視覚化できます。
![2020-12-18_17h30_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/5dc9a070-7d95-e825-14a0-4f6d4b86c68a.png)

# パスの軌跡を確認する
NavigationPath::EnableDebugDrawing()を毎フレーム呼び出せばパスを描画し続けることができます。
![2020-12-18_19h24_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/bc759d61-9b6e-c557-2f24-fef9bd6b5b57.png)
NoodlからLINEにメッセージを送信する(LINE Notify)

## やること
ビジュアルプログラミングツールNoodlから、LINEにメッセージを送信したかったので調べてみました。
結果として、RESTノードを使えば手間なく実装できることがわかりました。
LINE Notifyについてはアクセストークンの取得以外は説明しません。LINE Notifyを初めて使われる方は下記の記事などを参考として確認してください。

- @youtoyさんの[記事](https://qiita.com/youtoy/items/76586479c2d4c5893c5b)
- Qiitaの記事:[LINE Notifyタグ](https://qiita.com/tags/linenotify)

## 今回の成果物
下記のNoodlのサンプルでは送信ボタンを押すとTextInputの内容をLINEに送信します。

![img0022.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/a53ba5d6-0a7b-a239-db0b-170545ae1ebd.jpeg)

## 事前準備(LINE Notify)
下記手順でLINE Notifyのアクセストークンを取得しておきます。

1. [LINE Notify](https://notify-bot.line.me/ja/)にアクセスし、自身のLINE IDでログインをします。
1. 右上のLINE ID名をクリックしてマイページに移動します。
1. アクセストークンの発行(開発者向け)でトークンを発行するを選択します。
1. トークン名：任意(通知時に発信元として表示されます)
1. 送信するトークルームを選択：1:1でLINE Notifyから通知を受け取るを選択します。
1. 発行するを選択します。
1. これでサービスが連携し、アクセストークンが発行される。

発行されたアクセストークンをメモしておいてください。

![img0028.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/9f4ad4a6-b072-9a29-11b1-fb9e6cd393d1.jpeg)

## Noodlの設定
### RESTノードの設定
Resource: /api/notify
Method: POST
Endpoint: https://notify-api.line.me

### RESTノードのScriptを記述

```javascript:REST-Script
define({
	// The input ports of the REST node, name of input and type
	inputs:{
	    //ExampleInput:'number',
	    apikey:'string',
	    msg:'string'
	},
	
	outputs:{
	},
	
	request:function(inputs,request) {
	    request.headers={"Authorization":'Bearer '+inputs.apikey};
	    request.parameters.message=inputs.msg;
	},
	
	response:function(outputs,response) {
	}
})
```
###　Nodeの接続とAPIキーの設定
RESTノードに送信ボタンのシグナルと、テキストインプットのテキストを接続します

- 送信ボタンGrouup:ClickをREST:Fetchに接続します
- TextInput:TextをREST:msgに接続します

### アクセストークンの設定
LINE Notifyで発行したアクセストークンをREST:Inputsにあるapikeyに記入します。
![img0024.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/ae38667e-5dc7-c149-23b8-610d8c62b6a8.jpeg)

## 完成！
NoodlのRESTノードを使うことで、LINEへのメッセージ送信を簡単に実装することができました。
ちなみにステッカーや画像も送付することができました。

```javascript:ステッカーや画像を送信する例

define({
	// The input ports of the REST node, name of input and type
	inputs:{
	    //ExampleInput:'number',
	    apikey:'string',
	    msg:'string',
	    img:'string'
	},
	
	outputs:{
	},
	
	request:function(inputs,request) {
	    request.headers={"Authorization":'Bearer '+inputs.apikey};
	    request.parameters.message=inputs.msg;
	    //ステッカーを送信する場合
	    request.parameters.stickerPackageId=1;
	    request.parameters.stickerId=403;
	    //画像を送信する場合
	    request.parameters.imageThumbnail=inputs.img;//サムネール画像は最大240x240
	    request.parameters.imageFullsize=inputs.img;//メイン画像は最大1024x1024
	}, 
	
	response:function(outputs,response) {
	}
})

```
使用できるステッカーの一覧は[こちら](https://devdocs.line.me/files/sticker_list.pdf)を確認してください。
画像の場合はimgのところに表示したい画像のURLを記載してください。
例えばhttps://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/41b89d5d2067df05ce0e71d60deb4fe6ffdd1add/large.jpg?1565416440
とした場合、下記が送信されます。

![img](https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/41b89d5d2067df05ce0e71d60deb4fe6ffdd1add/large.jpg?1565416440)
Vue.jsのWatchを用いてformの値を数字以外の場合にリアルタイムで拒否する

Vue.jsでフォームに入力する値を数字以外の場合に拒否する機能を実装する機会があったのでその時の知見を共有したいと思います。

#Watchと正規表現を用いた
言わずと知れた監視プロパティですが、Vue.jsの公式では``coumputed``プロパティの方を推奨しているようです。いろいろないろいろな兼ね合い(私の実力不足)によって今回は``watch``プロパティを用いて実装しました。


今回は入力したものが正規表現にマッチしないかどうか判断し、マッチするようであれば入力された値をそのまま返し、マッチしないようであれば``slice``を用いて最後尾の文字を除外した文字列を返すというロジックにしました。


```vue
<template>
<div>
  <input type="text" v-model="value" value="">
</div>
</template>
<script>
import LikeHeader from './components/LikeHeader'
    
export default {
  components: {LikeHeader},
  data() {
    return {
      value: '',
    }
  },
  watch: {
    value(val) {
      let check = /^([1-9]\d*|0)$/ //正規表現の定義
        if (check.test(val)) { //testメソッドで入力された値が正規表現に一致するかチェック
          this.value = val //マッチした時はそのままreturn
        } else {
          this.value = val.slice(0,-1) //マッチしない場合は最後尾の文字列を除いた値をreturn
        }
    }
  },
}
</script>
```
ありがとうございました。
次は``computed``プロパティでも実装できるか試します。

もっといいやりかたがあったら知りたいです。。。
【初心者】PHPで文字列から特定の文字を削除する

# 文字列から特定の文字を削除する
文字列から特定の文字を削除したい時に使う。
関数名から察すると、特定の文字を削除するのではなく、特定の文字を特定の文字に「置き換える」というのが正しいのだろう。
ただ、置き換える文字を””として空白でもなく文字でもなくすることで削除することができた。
str_replace関数。表記は以下の通り。

```php
str_replace("置き換える前の文字", "置き換えたい文字", "文字列");
```

これを使用することで、特定の文字を削除することも、他の文字に置き換えることも可能になった。

日々色々なことを知ることで、できることが増えていっている気がする。
あくまで、気がする。
【Flask】Sessionについて

# Sessionについて
Sessionは簡単に言うと、ローカルの簡易データベースだ。JavaScriptで言うとlocalstorageみたいなものだ。Sessionを使うことによって、データベースにデータを保存する必要がなく、データをとってくることが可能になる。さらに、データベースに何度もアクセスする必要もなくなる。

# 階層
今回は仮想環境を用いているため、仮想環境を用いていない場合venvファイルは無視して良い。
![スクリーンショット 2020-12-19 15.11.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656707/d92f67b4-be10-b00b-eb17-d72de08c3090.png)


# プログラムコード
```app.py
from flask import Flask, redirect, url_for, render_template, request, session #session追加
from datetime import timedelta #時間情報を用いるため

app = Flask(__name__)

app.secret_key = 'user'
app.permanent_session_lifetime = timedelta(minutes=5) # -> 5分 #(days=5) -> 5日保存

@app.route("/")
def home():
  return render_template("index.html")

@app.route("/login", methods = ["POST", "GET"])
def login():
  #データベースに情報を送るとき
  if request.method == "POST":
    session.permanent = True  # <--- makes the permanent session
    user = request.form["nm"] #ユーザー情報を保存する
    session["user"] = user #sessionにuser情報を保存
    return redirect(url_for("user"))
  else: #情報を受け取るとき
    if "user" in session: #sessionにユーザー情報があったとき
      return redirect(url_for("user")) #userページに遷移
    return render_template("login.html") #sessionにユーザー情報がなかったときはloginページに遷移

@app.route("/user")
def user():
  if "user" in session:
    user = session["user"] #sessionからユーザー情報をとってくる
    return f"<h1>{user}</h1>"
  else:
    return redirect(url_for("login"))

@app.route("/logout") #ログアウトする
def logout():
  session.pop("user", None) #削除
  return redirect(url_for("login"))

if __name__ == "__main__":
  app.run(debug=True)
```

sessionは辞書型となっており、今回はsessionにキーを"user"、値を入力したユーザー名をセットする。

# 書き込み
`session["user"] = user`

# 読み込み
`user = session["user"]`

こちらでも可能だ。
`user = session.get("user")`
session.get()でなかった場合はNoneが返る。
第２引数に値を入れると初期値を入れることができる
`session.get('user', '名無し')`

`app.permanent_session_lifetime = timedelta(minutes=5)`
これはどれくらい保存するかを設定している。上記のコードでは、5分間保存すると言うイメージだ。5日にしたい場合は、`(days=5)`に変更すれば良い。

# 削除
`session.pop('user', None)`

# 永久に保存したい場合
`session.permanent = True`
と宣言する。

# 注意
ポイントは２つあって、`from flask import Flask, session`で明示的にsessionをimportしている点と、`app.secret_key`が必要な点。これがないと

```
RuntimeError: The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret.
```
とエラーになる。

#参考
[Sessions vs Cookies](https://www.techwithtim.net/tutorials/flask/sessions/)
[Flask Tutorial #5 - Sessions](https://www.youtube.com/watch?v=iIhAfX4iek0&feature=emb_logo)
[Flaskでセッションの読み書きをする](https://thr3a.hatenablog.com/entry/20171106/1509973583)
Pythonでimport時の「attempted relative import beyond top-level package」の解決方法

### エラー概要

以下のようなディレクトリ構成で

```
root/
　├ core/
　│　└ base.py
　│　
　├ sub/
　│　└ subA.py
```

subA.pyからbase.pyを呼び出そうする。

```python:sub.py
from ..core.base import *
```

すると以下のエラーが発生する

```
Exception Value: attempted relative import beyond top-level package
```

原因は「Pythonは実行するファイルのカレントディレクトリを最上位のルートディレクトリとして使用」という仕様があり、上位のディレクトリに移動できないため発生。

## 解決方法

環境変数のPYTHONPATHを設定すればよい。

```
export PYTHONPATH=/root
```

そしてコードを以下のように修正

```python:sub.py
from core.base import *
```
これでpython実行時にパッケージ検索パスに```/root```が加わるため、```/root```から配下のディレクトリを探すことが出来る。

参考：https://docs.python.org/ja/3/using/cmdline.html#envvar-PYTHONPATH
GitHubのコラボレータ招待機能の仕様をデシジョンテーブルや状態遷移図で整理してみた

# はじめに

テスト技法を仕様の整理に使ってみた話です。

テスト技法をクラウドで使える「[GIHOZ](https://www.veriserve.co.jp/gihoz/)（ギホーズ）」というツールが弊社から無償のオープンβ版として公開されている（※2020年12月19日時点）ので、そちらを使ってみました。
せっかくなのでGIHOZの操作方法にも少し触れています。

# お題
よく知られているものをお題にしたほうが、読んだ人が理解しやすいと思いましたので、GitHubのリポジトリへのコラボレータの招待機能を対象にします。
ちょうど自分の業務で似たような機能の仕様を考えることがあり、「普段何気なく使っているけど、そういえば、実際どういう仕様になっているのだろう？」と思って調べたので、その記録という意図もあります。

# 注意点
この記事は「テスト技法を仕様の整理に使えるので、みんな使ってみてね」という話をしたいだけで、「GitHubの仕様を明らかにしたからGitHubを使うときに参考にしてね」という記事ではありません。この記事に記載したGitHubの仕様の正しさは、筆者のほうでは保証しませんのでご注意ください。
いちおう筆者調べで、執筆時点のGitHubの仕様を正しく整理したつもりではありますが、GitHubのほうが変わるかもしれませんし。

# 画面遷移図で仕様を整理してみた
とりあえず、コラボレータの招待がどういう動きになっているのか理解するために、画面遷移を整理しました。
GIHOZの状態遷移テストを使ってみました。四角形の箱が画面、矢印が画面遷移を起こすイベントと条件を表しています。GIHOZではイベントと条件は区別して記載するようになっていないため、`/`で区切って入力しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/e685d0d1-d680-7c7c-d8b2-4373f9e0e0d6.png)


GIHOZの状態遷移テストは、編集エリアをダブルクリックして新しい箱を置いたり、ドラッグアンドドロップで箱から箱へ矢印を繋ぐことができます。また、箱をクリックして、[+]ボタンをクリックすることで、新しい箱と矢印を同時に作ることもできます。（と、[状態遷移テストのヘルプページ](https://gihoz.com/help/state_transition)に書いてあります）

「状態遷移表とテストケースを生成」というボタンを押せば、遷移を網羅するテストケースを一気に生成できます。自分が開発している製品の仕様を整理するときにもGIHOZを使えば、テストケースを生成してそのままテストで使えるので一石二鳥ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/f8f69108-4594-1d35-448e-0b1b30c50005.png)


# デシジョンテーブルで仕様を整理してみた
画面遷移を整理した結果、招待のURLにアクセスしたときの遷移先が色々な条件で分岐していることが分かりました。こういう論理関係を整理するには、デシジョンテーブルを使うのが良さそうです。ということで、GIHOZのデシジョンテーブルを使って、条件ごとの遷移先を整理してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/a7b8c6c0-d049-7a91-e5b5-eb87c5c9b290.png)


GIHOZのデシジョンテーブルは、[+]ボタンを押すことで、行・列を増やして入力することができます。条件を記述する部分では、親子関係の階層を作って記載することができます。階層を作りたい場合は、親となる文字をセルに入力してから、そのセル内の[+]ボタンをクリックすると子の階層を作れます。（と、[デシジョンテーブルのヘルプページ](https://gihoz.com/help/state_transition)に書いてあります）

`Y`・`N`・`X`はプルダウンから選択して入力できます。`Y`はYes、`N`はNo、`X`はExecuteを表し、縦方向に見て`Y`・`N`の条件の組を満たしたときに`X`の動作が実行される、ということを表します。
条件の`-`はY・Nのどちらでも良い、ということを表しています。

このように整理することで条件分岐の仕様が分かりやすくなりますし、実装するときはデシジョンテーブルに記載した条件の上から順に分岐を実装していけば、画面遷移を正しく実装できそうです。

ただ、今回作成したデシジョンテーブルは一部の条件が`-`となっている（圧縮されている）ので、このままテストに使うと漏れがあるかもしれませんので、注意が必要です。デシジョンテーブルの圧縮の注意点については、[「デシジョンテーブルの圧縮（整理・縮小）は要注意」](https://ameblo.jp/pictmaster/entry-11328586874.html)に詳しく記載されていますので、こちらを読んでください。

# おわりに

デシジョンテーブルも状態遷移図（画面遷移図）も、もともと仕様を整理するためのものなので、仕様の整理に使えるのは当たり前ですが、GIHOZはそれらをクラウドで手軽に使うことができるのが良いところだと思います。
GIHOZはテスト技法のツールですが、テストだけでなく今回のように仕様の理解や整理にも使えますし、整理した結果はもちろんテストに活用できますので、使ってみてはいかがでしょうか。

ちなみに、今回使ったGIHOZには、GitHubのような他のユーザを招待したりする機能はいまは無いようです。・・・いまはね:smirk:
Markdown記法の学習メモ

#💭 はじめに
はじめまして、しろくまです。
お読みいただきありがとうございます。  
この記事は、Markdown記法の覚書きメモです。
調べ直す手間を省くために、よく使うものをまとめました。

#💡 Markdown記法？
**Markdown(マークダウン)記法**とは、ファイルの書き方のひとつです。
簡単にHTMLの形に文章を変換してくれる書き方のことです。  
難しい説明はできませんが、
使いこなせるようになると、見やすい文を簡単に書くことができます！

#🔍 目次
* 見出し
* 装飾(太文字)
* 装飾(斜め文字)
* 改行
* 箇条書き(黒丸)
* 箇条書き(数字)
* 区切り線

#✏️ 見出し
**【書き方】**
「``半角#`` +  ``見出しにする言葉``」

>**【例】**
>``#これはh1タグです``
>``##これはh2タグです``
>``###これはh3タグです``
>``####これはh4タグです``
>``#####これはh5タグです``
>``######これはh6タグです``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.37.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/d41e7f54-b1ce-eaf8-f368-200a39bbbf46.png)




#✏️ 装飾(太字)
**【書き方】**
「``半角**``で太字にしたい文字を囲む」
>**【例】**
>``**文字が太くなります**``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.35.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/51a4072d-832f-49a5-2c5d-c3da32e01921.png)

#✏️ 装飾(斜め文字)
**【書き方】**
「``半角*``で文字を囲む」
>**【例】**
>``*文字が斜めに傾きます*``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.34.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/4a0c9a0c-c8eb-b9b4-4691-167e54326083.png)


#✏️ 改行
**【書き方】**
「改行したい文の終わりで``半角空欄2文字``」
>**【例】**
>``改行したいです。□□改行できました。``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.33.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/3c620b72-e41c-f358-7cfa-0c5cd65bd12a.png)

#✏️ 箇条書き(黒丸)
**【書き方】**
「箇条書きにしたい文字の前に``半角*``」
>**【例】**
>``リスト1``
>``リスト2``
>``リスト3``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/de6d1b6f-07b4-b7cc-a947-942888b3bba6.png)

#✏️ 箇条書き(数字)
**【書き方】**
「箇条書きにしたい文字の前に``半角数字``＋``ピリオド``」
>**【例】**
>``1.リスト1``
>``2.リスト2``
>``3.リスト3``  
>**【結果】**
>![スクリーンショット 2020-12-29 23.54.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/655624c1-dedf-ede6-680c-224b4c196c05.png)

#✏️ 区切り線
**【書き方】**
「``半角***``」
>**【例】**
>``区切り線を入れたいです。``
>``***``  
>**【結果】**
>![スクリーンショット 2020-12-30 0.01.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726261/cdc9e2dc-d4bb-9f8e-f70d-2a630de855c0.png)


#💭 おわりに
他にもよく使う書き方があれば、不定期に追加・更新していきます。

間違いもあるかと思いますので、教えていただけると嬉しいです。
これからもよろしくお願いします:seedling:
Ubuntuのプロキシ設定（Docker, kubernetesなど使う環境想定、社内学内プロキシに困っている方向け）

# システムの設定
```
sudo vi /etc/profile
```
以下を追加

```
export http_proxy=http://xxx.xxx.xxx.xxx:xxxx/
export https_proxy=${http_proxy}
export HTTP_PROXY=${http_proxy}
export HTTPS_PROXY=${https_proxy}
export DOCKER_HOST_IP=172.17.0.1
export no_proxy=172.17.0.0/16
export NO_PROXY=${no_proxy}
```

kubernetesのnode間の通信がプロキシを経由しないように`no_proxy`に`172.17.0.0/16`を設定。(Dockerのみ使用するのであれば172.17.0.1で十分) また、ローカル環境で他にもプロキシを経由したくないIPアドレスがある場合は","で区切って記載する。この点は他の設定項目にも共通。

変更を反映させる。

```
source /etc/profile
```
# aptの設定
```
sudo vi /etc/apt/apt.conf
```
以下を記載

```
Acquire::http::Proxy "http://xxx.xxx.xxx.xxxx:xxxx";
Acquire::https::Proxy "http://xxx.xxx.xxx.xxx:xxxx";
```

# curlの設定
```
vi ~/.curlrc
```
以下を記載

```
proxy = "http://xxx.xxx.xxx.xxx:xxx/" 
noproxy = "172.17.0.0/16"
```
# Dockerの設定
```
sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
```
以下を記載

```
[Service]
Environment="HTTP_PROXY=http://xxx.xxx.xxx.xxx:xxxx/"
Environment="HTTPS_PROXY=http://xxx.xxx.xxx.xxx:xxx/"
Environment="NO_PROXY=172.17.0.0/16"
```

```
vi ~/.docker/config.json
```
以下を記載

```
{
 "proxies":
 {
   "default":
   {
     "httpProxy": "http://xxx.xxx.xxx.xxx:xxxx",
     "httpsProxy": "http://xxx.xxx.xxx.xxx:xxxx",
     "noProxy": "172.17.0.0/16"
   }
 }
}
```
変更を反映

```
sudo systemctl daemon-reload
sudo systemctl restart docker
```

# sshの設定
```
sudo vi ~/.ssh/config
```
以下を記載(`hoge@example.com:yyyy`に`ssh -T example`でアクセスしたい場合)
また、example.comのドメイン名解決がうまくできない場合は、IPアドレスを検索
して直接記載するとよい。

```
Host example
    User hoge
    HostName example.com
    Port yyyy
    ProxyCommand connect -H http://xxx.xxx.xxx.xxx:xxxx %h %p
    IdentityFile ~/.ssh/id_rsa_pub
```
データ学習アルゴリズム(データサイエンス・シリーズ：渡辺澄夫先生)  

```r

#データ学習アルゴリズム　p.52

#三層パーセプトロン

library(dplyr)

data("anscombe")

data=anscombe

X=as.matrix(data[,colnames(data) %in% c("x1","x2","x4")])

Y=as.matrix(data[,colnames(data) %in% c("y1","y2")])

Y[,1]=Y[,1]/max(Y[,1]);Y[,2]=Y[,2]/max(Y[,2])

N=ncol(Y);H=2;M=ncol(X);n=nrow(data)

wij=array(sample(1:9,N*H,replace=T),dim=c(N,H))/100

wjk=array(sample(1:9,H*M,replace=T),dim=c(H,M))/100

ite=10^5

eta=0.01


act_f=function(z){
  
return(1/(1+exp(-z)))  

}

for(l in 1:ite){
  
os=act_f(t(wjk%*%t(X)))
  
fs=act_f(t(wij%*%t(os)))  
  
delta_i=(fs-Y)*fs*(1-fs)

delta_j=as.matrix(delta_i)%*%as.matrix(wij)*os*(1-os)

dwij=t(delta_i)%*%os

dwjk=t(delta_j)%*%X

wij=wij-eta*dwij

wjk=wjk-eta*dwjk

#print(sum(abs(fs-Y)^2))
  
}

f_mat=fs

plot(c(1:length(c(Y))),c(f_mat),xlab="index",ylab="predict",xlim=c(1,length(c(Y))),ylim=c(min(c(c(f_mat),c(Y))),max(c(c(f_mat),c(Y)))),col=2,type="o")

par(new=T)

plot(c(1:length(c(Y))),c(Y),xlab="index",ylab="predict",xlim=c(1,length(c(Y))),ylim=c(min(c(c(f_mat),c(Y))),max(c(c(f_mat),c(Y)))),col=3,type="o")


```



```r


#p.67 球形基底関数　RBF
dat=EuStockMarkets/100000
N=1
H=3
t=seq(1991.496,1991.496+1859*0.002,0.002)
Y=dat[,1]
X=dat[,-1]
X=X[,1:H]

#線形加法モデルにするかどうか
linear_pred=1

f=function(x){
w=x[1:(N*H)];x=x[-c(1:(N*H))]
guzai=x[1:H];x=x[-c(1:H)]
sigma=x
if(linear_pred==1){x=x[-c(1:H)]}
w=matrix(as.numeric(w),nrow=N,ncol=H)
oj=c()
for(j in 1:H){
oj=c(oj,exp(-((X[,j]-guzai[j])^2)/(2*sigma[j]^2)))  
}
oj=matrix(oj,nrow=nrow(X),ncol=H)
if(linear_pred==1){
return(X%*%x+apply(w%*%t(oj),2,sum))
}else{
return(apply(w%*%t(oj),2,sum))  
}}



h=0.001
lam=1
ite=300
if(linear_pred==1){
sita=rep(0.01,N*H+2*H+H)
}else{
sita=rep(0.01,N*H+2*H)  
}
eta=10^(-2)

for(l in 1:ite){
sita_pre=sita
Z=array(0,dim=c(length(Y),length(sita)))
vec=sita
for(j in 1:ncol(Z)){
vec_sub=vec;vec_sub[j]=vec_sub[j]+h
Z_sub=(f(vec_sub)-f(vec))/h
Z[,j]=Z_sub
}
beta=solve(t(Z)%*%Z+diag(lam,ncol(Z)))%*%t(Z)%*%(Y-f(sita))
sita=c(sita+eta*beta)
print(sum((Y-f(sita))^2))
}

plot(t,Y,xlim=c(min(t),max(t)),ylim=c(min(Y),max(Y)),col=2)

par(new=T)

plot(t,f(sita),xlim=c(min(t),max(t)),ylim=c(min(Y),max(Y)),col=3)

cor(f(sita),Y)

#DAX(目的変量Y)
hist(dat[,1])

#SMI(特徴量)
hist(dat[,2])

#CAC(特徴量)
hist(dat[,3])

#FTSE(特徴量)
hist(dat[,4])

#RMSE
sqrt(sum((Y-f(sita))^2)/length(Y))

#RMSLE
sqrt(sum((log(Y+1)-log(f(sita)+1))^2)/length(Y))

```
ヒストグラム

# 量的変数の分布
## シンプルなヒストグラム
### matplotlib

```python

import matplotlib.pyplot as plt
plt.hist(df['column1']) # 引数はcolumから選択
plt.xlabel('column1')
plt.ylabel('freq')
plt.show()
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/8a82c581-b32e-c75a-7e41-b0f445b5aa75.png)


### seaborn

```python
import seaborn as sns

sns.set()
sns.distplot(df['column1'], kde=False, bins=10, color='blue')
# kernel density estimation(カーネル密度推定)
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/1648234d-92cd-3a41-468d-7532d5322373.png)

## 複数のヒストグラムを描画したい
・$X_n$が２値分類データ(binaryで0,1など)における、量的変数$Y$に対する分布

```python
import seaborn as sns

df_0 = df[df["x"] == 0] # object型を指定したいときは =="object"
df_1 = df[df["x"] == 1]
sns.distplot(df_0["Y"], kde=False, bins=20, color="r")
sns.distplot(df_1["Y"], kde=False, bins=20, color="b")
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/3aed6cee-040e-3b5f-6493-e6d5eef24aa0.png)

# 質的変数の分布
## カウントプロット

```python
import seaborn as sns

sns.countplot(data=df,x="column2")
```

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/bf39dec2-f09a-6c78-8e0e-92cc7b9330a5.png)


```python
# カウントプロットを別のcolumnの値ごとに色分けする。
import seaborn as sns

sns.countplot(data=df,x="column2",hue="marital")
```

![download-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698282/7c742c42-178a-e354-4686-aa1a00061434.png)
【ネットワークインターフェースのデタッチで】You do not have permission to access the specified resource.が出た時の対処

#はじめに
使わないVPCを削除したときに出たエラーについて共有していきたいと思います。



# 原因
エラー文を翻訳すると
`指定されたリソースにアクセスする権限がありません`

**You do not have permission**と書かれてますが特に権限がないわけではなく、**関連付けられているリソースが存在**するため削除できない状態にあります。


# 対処法
descriptionを参照して、関連付けられてるリソースを先に削除するとエラーが解決します。

`インターネットゲートウェイ`と`RDS`が原因になっている場合が多いようです。

ちなみに自分の場合はRDSが原因でした。
VPCに関連するRDSを削除することで解決できました。
s3バケットの画像の解決をポーリングする

## 困っていたこと

s3にアップロードした画像のURLを参照したときに、lamda関数（リサイズを行う）の処理が追いつかず未解決で403が返ってくる。
（オンメモリのプレビュー画像では要件を満たせない場合）

## 前提

s3にアップロードした画像に対して、リサイズ等のlamda関数を実行して最適化した画像を生成し、専用バケットに再登録するという処理を行っている場合に発生する事例。

<img width="400" alt="s3_upload&resize.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371854/67ca4707-c0f8-ec2b-18aa-fe4356a46dd2.jpeg">

## 解決策

fetch APIで画像のURLを叩いてstatusCode: 200が返ってくるまでPollingする。

```ts
import axios from 'axios'
import * as api from '@/api'

type Form = {
  Content-Type: string
  Policy: string
  X-Amz-Algorithm: string
  X-Amz-Credential: string
  X-Amz-Date: string
  X-Amz-Signature: string
  key: string
  x-amz-server-side-encryption: string
}

const checkS3Resolve = async (url: string) => {
  const res = await fetch(url)
  // 成功したら200を返す
  return res.status
}

export const upload = async (
  host_url: string,
  url: string,
  file: File,
  form: Form,
) => {
  const formData = new FormData()
  for (const key in form) {
    const value = form[key]
    if (value !== undefined) {
      formData.append(key, value)
    }
  }
  // fileは最後にappendしないとエラー
  formData.append('file', file)
  await axios.post(url, formData, { timeout: 10000 })

  const hostUrl = `https://s3-ap-northeast-1.amazonaws.com/hoge/origin`

  const delay = (ms: number) => new Promise(res => setTimeout(res, ms))
  let result = 404
  // S3で画像が処理されるまで待つ
  while (result !== 200) {
    // 1.5秒ごとにS3側の状態確認処理を実施
    await delay(1500)
    result = await checkS3Resolve(hostUrl)
  }
  ...
}

```

## 最後に

かなりエッジケースにはなりますが、個人的にちょっと詰まったので記載しました。
他にもいい方法などございましたらコメントお待ちしております。

AWS周りのアーキテクチャについては画像元データはs3に格納して、リサイズの処理についてはlamdaを直接叩いてしまう方が良さそうとのコメントを頂きました。
（その方がエラーハンドリング等も考えると良さそう）
また、小規模の案件なら画像の管理はFirebase Resize Imagesで行うのが簡単とのお声も頂きました。

勉強になります🙏
Rust のコンパイルを爆速にしよう (nightly で LLD か zld を使ってリンクする)

# まとめ

1. [Linker を替える](https://bevyengine.org/learn/book/getting-started/setup/) と良いようです (『Enable Fast Compiles (Optional) 』) 。 5 倍ほど速くコンパイルできるようになりました。
2. 依存クレートが遅くなる場合は、 `opt-level` を設定します。 `image`などで顕著でした。
3. さらに [sccache](https://github.com/mozilla/sccache) も導入します。複数のプロジェクトでクレートのキャッシュを共有できます。

# Linker を変える

[Bevy - Setup](https://bevyengine.org/learn/book/getting-started/setup/) からのコピペです。

> 2020/12/19 にコピペしたのですが、古くなっていたらごめんなさい。

## LLD or zld のインストール

```sh
# ubuntu
sudo apt-get install lld
```

```sh
# arch
sudo pacman -S lld
```

```sh

# macOS
brew install michaeleisel/zld/zld
```

```sh
# windows
cargo install -f cargo-binutils
rustup component add llvm-tools-preview
```

## nightly rust の有効化

```sh
rustup toolchain install nightly

# use nightly rust for this project
# rustup override set nightly
# or everywhere
rustup default nightly
```

## .cargo/config.toml の作成

プロジェクト毎に `config.toml` を設定する場合:

```sh
cd your/project/root

mkdir .cargo
curl 'https://raw.githubusercontent.com/bevyengine/bevy/master/.cargo/config_fast_builds' > .cargo/config.toml
```

グローバルな `Cargo.toml` を作る場合:

```sh
mkdir ~/.cargo
curl 'https://raw.githubusercontent.com/bevyengine/bevy/master/.cargo/config_fast_builds' > ~/.cargo/config.toml
```

# `.cargo/Cargo.toml` への修正

## 『image クレートによる画像のロードが極端に遅くなった』

`.cargo/config.toml` から最適化レベルを設定できます。やり過ぎな気もしますが、たとえば `opt-level = 3` にするには:

```toml
[profile.dev.package."image"]
opt-level = 3

# [profile.dev.package."*"]
# opt-level = 3
```

最適化レベルを上げ過ぎると、初回のコンパイルは遅くなります。

## sccache

[sccache](https://github.com/mozilla/sccache) も導入します。複数のプロジェクトでクレートのキャッシュを共有できます。

参考: [コンパイルキャッシュでRustのビルド時間を短縮しよう](https://qiita.com/tatsuya6502/items/76b28a6786a1ddc9d479)
バッククオートの表示方法

#バッククオート？どうやって出すの？
シングルクオート　<strong>''</strong>
ダブルクオート　<strong>””</strong>
でなく、
###これ　``　バッククオート

SHIFT + @　で出せます！　以上！

JavaScriptのテンプレートリテラルで急に出てきて？？
となってしまいました。

良かったらご参考にどうぞ。
AWS ECS fargateのタスクのスケジュールでcronが動かないときの確認方法

# 概要

ECSのタスクスケジュールを使ってfargateをcron起動したときにログの確認方法がわからず苦労したのでメモ

## まとめ

- CloudWatch Logsにログがでないときはタスクの「Stopped」を確認する
- タスクのスケジュールで「コンテナの上書き」を指定する場合は、引数はカンマ区切りにする

# 手順

ここにマニュアルがある

[停止されたタスクでのエラーの確認](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/stopped-task-errors.html)

## タスクが起動した形跡がない

ECSのクラスターの画面で「タスク」タブをクリックすると、タスクの一覧が表示されるが、ここがデフォルトで「Runnig」が表示されるようになっているため、cron起動されて終了したタスクはここには出てこない。必要なタスクのステータスの「Stopped」をクリックすると、終了したタスクが確認できる。
このとき、開始時刻が空白のタスクは起動に失敗している（タスクのリンクを押してログをみることが出来る）ので、タスクのスケジュールの設定から「コンテナの上書き」のパラメーターを確認する。
また、何も起こらず正常終了している場合は、Dockerイメージで`ENTRYPOINT`や`CMD`が設定されていない可能性があるので、Dockerfileや、それこそ「コンテナの上書き」を確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/3c3f4c5c-faf3-fa57-a055-f02157e86408.png)

## 起動スクリプトが「no such file or directory」になる

タスクのスケジュールの「コンテナの上書き」で起動スクリプトを指定するときに、引数の指定方法がカンマ区切りでないと、起動スクリプトと引数を含めた文字列を実行しようとしてこのエラーが発生する。
以下のように、起動スクリプトと引数の間、引数と引数の間をカンマで区切る必要がある。
このとき、カンマの前後に空白があっても無視されないため、空白を入れないように注意する必要がある

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/09fc4088-4151-11d4-86aa-106915dedad7.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/1e63f598-e137-0ec9-dc8a-e3e36751972b.png)


参照画面でみるとこんな感じで確認できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38174/7409fd1a-b67a-d88d-c05d-e919c53fa0c6.png)

※最後にCloudWatch Logsのログストリームは自動で作成されるはずなので慌てないで探す。管理画面からだと作成画面に頻繁に飛ばされるため、事前に作成しないと駄目なのかと勘違いしてしまった。
Nuxt.js + TypeScriptで空のモデルを表現する方法

## はじめに

エキサイトでエンジニアのインターンをしている山縣です。

Webアプリケーションの制作において、アンケートフォームや管理画面でのフォームなどでなんらかのフォームを作ることは少なくないと思います。本記事では、Nuxt.jsとTypeScriptを使用した開発環境において、空のモデルを表現する方法についてフォームを題材に紹介します。

## 本記事で扱うフォームについて
本記事では、サンプルとしてフォームに`name`と`item`をもつモデルを扱います。
ディレクトリ構成は、弊社Web・iOSエンジニアの三浦さんがこちらの記事でまとめています。

[Nuxt.js + TypeScriptのアーキテクチャ一例](https://qiita.com/takayuki-miura0203/items/22b9040d9183b1e487f7)

```ts
export class UserForm {
  name: string
  item: string

  constructor(name: string, item: string) {...}
}
```


## 1. フォームをユニオン型で表現する
フォームをユニオン型で表現する場合、下記のようなコードになります。

ユニオン型について↓
[判別可能なUnion型 - TypeScript Deep Dive 日本語版](https://typescript-jp.gitbook.io/deep-dive/type-system/discriminated-unions)

```vue
<script lang=ts>
interface DataType {
  userForm: UserForm | null
}

export default Vue.extend({
  data(): DataType {
    return {
      userForm: null,
    }
  },
})
</script>
```

このようなコードを書くことで、例えばモーダル上にフォームを実装したとき、下記のような処理を書くことができます。

- モーダルが開かれるまではuserFormをnullとして扱う
- モーダルが開かれた時にはフォームを初期化する( `this.userForm = new UserForm(...)` )
- モーダルを閉じる時はuserFormをnullにする

### 利点
現在フォームに値が入力されているかどうかをnullで判別することができます。

### 欠点
TypeScriptを記述する`<script>`内では特に問題がありません。しかし、`<template>`内において以下のような問題が発生してしまいます。
`userForm`をnullとして扱っているため、`v-model="userForm.name"`と記述している箇所が`null.name`となってしまう可能性があり、nullの時にエラーとなってしまいます。そのため、`computed`内でnullである可能性を排除しなくてはなりません。今回示したサンプルでは、フォームのフィールド数が少ないため、computedに新たに関数を追加するコストとはそこまで大きくありませんが、いくつも入力欄があるとその都度computedに関数を追加しないといけないため非常に面倒です。

## 2. フォームをオプショナル型で表現する
フォームをオプショナル型で表現する場合、下記コードのように変数名の後ろに`?`を付与します。nullとundefinedの違いについては省略しますが、1と同ような利点・欠点があります。

```js
interface DataType {
  userForm?: UserForm
}
```

## 3. フォームをクラスのみで表現する
フォームをクラスのみで表現する場合、下記のようなコードになります。

```js
interface DataType {
  userForm: UserForm
}
```

### 利点
フォームをクラスのみで表現したときの利点は下記のとおりです。nullを扱わないため、`v-model`を安全に利用することができます。

- nullを扱わないため、1の欠点を解消できる
- nullを扱わないコードになる

### 欠点
下記に欠点をまとめました。nullやundefinedを使用しないことで面倒になってしまうことも多くあります。

- 初期化する都度、`new UserForm(...)`とタイプするのが面倒
- 入力欄が増えた際に、初期化している箇所すべてに手を加えるのが面倒
- 誤って意図しない初期値をコンストラクタに入れてしまった場合に気づきにくい
- nullで判断していた箇所と同じことをするのは大変

## 3の欠点を解消した方法
`UserForm`クラスに初期化するためのstaticメソッドを実装しました。
これにより、初期化の際には`UserForm.init()`と呼び出すことで意図しない初期値の混入を防ぐことができるようになります。
また、フォームに値が入力されたかどうかを`UserForm`に記述することができたりします。

```ts

export class UserForm {
  name: string
  item: string

  constructor(name: string, item: string) {...}

  static init(): UserForm {
    return new UserForm('', '')
  }
}
```

モデルに初期化用の関数を追加せずに、Vueコンポーネント内に同様の記述をすることも考えましたが、入力欄が増えたときはモデルに手が加えられるため、モデルに近い場所に初期化用の関数を追加するのが望ましいと判断しました。

このような実装を調べてみると、例えばJavaのカレンダークラスを利用する際は、`Calendar now = Calendar.getInstance();` と書くようです。

[Calendar (Java Platform SE 8 )](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Calendar.html)


## 結論
上記にまとめた3の書き方を取り入れてフォームを実装することにしました。これにより、フォームの初期化をしやすくなり、nullを使用しないコードを書くことができるようになります。

ここまでお付き合いいただきありがとうございました！
Bootstrap を使ったサイト作成を無料で快適に行うガイドライン

サイトのデザインを最も手っ取り早く行うなら、テンプレートをダウンロードしてそのコードにアレンジを加えていくことだろう。
以下のサイトは無料で手っ取り早くレスポンシブなブートストラップサイトを作成したい人向けのツール3選です。
モニター３つでこのサイトをそれぞれの画面に貼っておけば爆速でコーディングができますよ。
# ➀templateを探すならBootstrapMade
無料でセンスの良いコードをダウンロードできる。
https://bootstrapmade.com/

#②部品の作成にはBootSnipp
①でダウンロードして作成する際に新しい部品を作ってアレンジしたりするときに使える
https://bootsnipp.com/

# ➂色のアレンジにはBootstrapMagic
簡単にアレンジを加えるなら色の変更が手っ取り早い、このサイトを見れば色とコードを簡単に比較して使える
https://pikock.github.io/bootstrap-magic/