OmniAICreator/Qiita-1.07M huggingface dataset:
（paiza スキルチェック見本問題）「長テーブルのうなぎ屋 JavaScript編」

#長テーブルのうなぎ屋
paizaラーニングのスキルチェック見本問題セットについて、、、
この問題はなんとか正解することはできたが、絶対にもっと効率の良い書き方がある、、、と思い、解説を見てみた。
すると、簡潔なわかりやすいコードが、、、↓↓↓
#ポイント
・空席状況を管理する変数 (配列がいい) を用意し、すべて空席としてマークしておく。
・グループが来店する度に、そのグループが座れるかどうかを判定。
座れるなら、座った座席を使用済みとしてマークする。座れないなら、何もしない。
**・座席 n と座席 1 が隣り合っていることに注意。**
**これをプログラム上で表現するには、n で割った剰余を用いる！**
⏩ここがわからなかった。。。

#実装例

```javascript
process.stdin.resume();
process.stdin.setEncoding('utf8');
// 自分の得意な言語で
// Let's チャレンジ！！
var lines = [];
var reader = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
reader.on('line', (line) => {
  lines.push(line);
});
reader.on('close', () => {

// ファイルを読み込む
const fs = require("fs");
// inputに入力値を入れる
const input = fs.readFileSync("/dev/stdin", "utf-8");
// linesを配列にする（＼nは、改行で区切るという意味）
const lines = input.split("\n");
// linesの要素それぞれを配列に分割する(n=座席数,m=グループ数)
const [n, m] = lines[0].split(" ").map((num) => Number(num));
// n個の要素を持った配列seatsを作る
const seats = Array(n);
// n個の要素を持った配列(中身は空)seatsの中身をすべて0にする
// seats[?]が０なら空席、１なら座れない
seats.fill(0);

let count = 0;
for (let i = 1; i <= m; i++) {
  // a=グループの人数,b=着席開始座席番号
  // lines[1](1グループ目）から順に、２個の要素を持った配列を作り、文字列を数値に変換して、aにその[0]を入れる
  const a = lines[i].split(" ").map((num) => Number(num))[0];
  // bもaと同様
  let b = lines[i].split(" ").map((num) => Number(num))[1];
  b -= 1;

  // flag=trueなら、座れる
  let flag = true;
  // 座席が埋まっているか確認
  for (let j = 0; j < a; j++) {
    // seats[?]が０なら空席、１なら座れない
    // 座席 n と座席 1 が隣り合っていることに注意しましょう。これをプログラム上で表現するには、n で割った剰余を用いる
    if (seats[(j + b) % n] !== 0) {
      // flag=falseなので座れない
      flag = false;
    }
  }
  // flag=trueなら、座れる
  if (flag) {
    // 席を埋める
    // 座席 n と座席 1 が隣り合っていることに注意しましょう。これをプログラム上で表現するには、n で割った剰余を用いる
    for (let j = 0; j < a; j++) {
    　// seats[?]が０なら空席、１なら座れない
      seats[(j + b) % n] = 1;
    }
    // 無事に座席に着席出来ている人数をカウントする
    count += a;
  }
}
console.log(count);

});
```

**座席の状態を管理する配列 seats を用意し、グループが来店する度にシミュレートして、そのグループが座れるかどうかを確かめている。**
親コンポーネントでURLのクエリパラメータからページ数を取得して、子コンポーネントでページのリロード後にページャの値を変更する

子コンポーネント内の `v-pagination` の値を
親要素のリロード時のURLについてる `?page=2` 等のパラメータを元に合わせる。

# 親

### 親のtemplate

```vue
<ChildComponent
  :page-num-to-child="pageNumToChild"
/>
```

### 親のdata

```js
data() {
  return {
    page: 1
  }
}
```

### 親のmounted

```js
let page = '' // pageパラメータの値としてあとでURLに付加する。
const queryPage = this.$route.query.page // 現在のURLの「?」以降の「page」のパラメータの値を入れる

// queryPageが全部文字列としての数値で帰ってくるため「Number.isInteger(queryPage)」では全部false
// 数値かどうか判定する用
const judgeNumOrNot = /[0-9]+/u
// 数値ならtrue、その他文字列ならfalse
const resultNumOrNot = judgeNumOrNot.test(queryPage)

// pageに「パラメータあり」かつ、「数値である」、かつ「'0'じゃない」時には現在のURLのpageのパラメータの数値を入れる
if (queryPage !== '0' && resultNumOrNot) {
  page = queryPage
  this.page = queryPage
  this.pageNumToChild = Number(page) // 子に渡す（v-paginationの数字の表示のため）
} else {
  page = this.page
}

/**
 * APIコール
 */
this.$router.push({ path: `${this.$route.path}?page=${page}` }) // URLの?page=パラメーターをつける
// APIへリクエスト
await this.storeGetApiRequest()
```

# 子

### 子のtemplate

```vue
<v-pagination
  v-model="page"
></v-pagination>
```

### 子のdata

```js
data() {
  return {
    page: 1
  }
}
```

### 子のprops

```js
props: {
  // 親から渡ってくるページ数（ページリロード時に使う）
  pageNumToChild: {
    type: Object,
    default: null
  }
}
```

### 子のwatch

```js
watch: {
  /**
   * ページャーの数字（pageのパラメータの値）を親から渡す用
   */
  pageNumToChild() {
    if (this.pageNumToChild !== null) {
      this.page = this.pageNumToChild
    }
  }
}
```
MySQL: 5.7.12 FULLTEXT インデックスによる全文検索では条件によって正常に検索が出来ない時がある

# はじめに

本記事では以下のversionのMySQLでの動作を確認しています。
> MySQL: 5.7.12

MySQLには[N-gramパーサーを利用した全文検索のための「FULLTEXT インデックス」](https://dev.mysql.com/doc/refman/5.7/en/innodb-fulltext-index.html)というものが存在します。

N-gramなのでそこまで精度の高いものではないのですが既にMySQLを利用していてわざわざElasticsearch導入するまでもない、かつLIKEだとちょっとパフォーマンスが気になるというちょっとした検索機能を実装するには十分有用な機能です。

なのですが、特定の条件でFULLTEXT インデックスを使用した検索が正常に行われない事象が発生したので解決策を含めて備忘録として残しておきます。

# 何が起きたか

問題が起きたSQLは以下の通り

```sql
SELECT hoge.*
FROM hoge
WHERE 
  hoge.type = ?
AND hoge.id IN (
  SELECT hoge.id
  FROM hoge
  WHERE MATCH (caption) AGAINST (? IN BOOLEAN MODE)
     OR MATCH (description) AGAINST (? IN BOOLEAN MODE)
)
/***以下はfilterの条件により可変で追加される***/
AND hoge.id IN (
  SELECT fuga.hoge_id
  FROM fuga
  WHERE fuga.user_id = ?
)
AND hoge.id IN (
  SELECT piyo.hoge_id
  FROM piyo
  WHERE piyo.type = ?
)
```

WHERE条件は可変で、検索対象tableのidをIN句で絞り込むサブクエリが幾つか存在し
サブクエリの1つでFULLTEXT インデックスを使用した全文検索によるkeywordでの絞り込みが行われており
filterの条件によって更に絞り込みの為のサブクエリが増えるような実装になっています。

本来期待する動作としては

- 全文検索のみの場合はkeywordで全文検索した結果が取得される
- 全文検索 + filter条件が指定された場合はkeywordで全文検索した結果から更にfilter条件で絞り込みが行われる

となるはずですが

- 全文検索のみの場合は正常に検索できる
- 全文検索 + filter条件を付与すると**全く関係ないレコードが取得される** or **0件になる**

複数ある環境の中でも特定の環境でのみ上記のような挙動をしており
最初はロジックを疑ったのですが、調査の結果MySQLに問い合わせた結果自体がおかしいという結論に至りました。

# 再現条件

## 全文検索のID絞り込みサブクエリのみ

このパターンはOK

```sql
SELECT hoge.*
FROM hoge
WHERE hoge.id IN (
  SELECT hoge.id
  FROM hoge
  WHERE MATCH (caption) AGAINST (? IN BOOLEAN MODE)
     OR MATCH (description) AGAINST (? IN BOOLEAN MODE)
)
```

## 全文検索のID絞り込みサブクエリ＋同テーブルのcolumnの絞り込み

このパターンもOK

```sql
SELECT hoge.*
FROM hoge
WHERE 
  hoge.type = ?
AND hoge.id IN (
  SELECT hoge.id
  FROM hoge
  WHERE MATCH (caption) AGAINST (? IN BOOLEAN MODE)
     OR MATCH (description) AGAINST (? IN BOOLEAN MODE)
)
```

## 全文検索のID絞り込みサブクエリ＋別のサブクエリでのIDの絞り込み

このパターンだと本来結果が取得できるはずのデータでも結果が取得出来ないのでNG

```sql
SELECT hoge.*
FROM hoge
WHERE hoge.id IN (
  SELECT hoge.id
  FROM hoge
  WHERE MATCH (caption) AGAINST (? IN BOOLEAN MODE)
     OR MATCH (description) AGAINST (? IN BOOLEAN MODE)
)
AND hoge.id IN (
  SELECT fuga.hoge_id
  FROM fuga
  WHERE fuga.user_id = ?
)
```

どうやらIDを絞り込む条件が複数存在すると上手く動作しない事があるようです。

INDEXが上手く構築されていない可能性も考慮して `OPTIMIZE TABLE` を試してみましたが結果は変わらず。
取得できる環境と取得できない環境とでNGパターンのSQLを流してみたのですが `EXPLAIN` は全く同じでした。

# 対策

原因はいまいち分かっていないのですが
再現条件としては `WHERE TABLE.id IN () AND TABLE.id IN ()` のように
IDでの絞り込みが複数出現する際に発生するようですので
全文検索のためのSQLとfilterによる絞り込みのためのSQLを分けました。

- 全文検索 SQL

```sql
SELECT DISTINCT hoge.id
FROM hoge
WHERE hoge.id IN (
  SELECT hoge.id
  FROM hoge
  WHERE MATCH (caption) AGAINST (? IN BOOLEAN MODE)
     OR MATCH (description) AGAINST (? IN BOOLEAN MODE)
)
```

- filter SQL

```sql
SELECT hoge.*
FROM hoge
WHERE hoge.id IN (
  /* 全文検索 SQLの結果のID */
)
AND hoge.id IN (
  SELECT fuga.hoge_id
  FROM fuga
  WHERE fuga.user_id = ?
)
```

全文検索の結果が大量に取れた場合など考慮しなければいけないことが増えたのですが、上記の対策で現状上手く動作しているようです。
原因についてはもう少し調べてみます。
Elmer に STL を読み込ませる の２

# はじめに
[前回の投稿](https://qiita.com/superrino130/items/033bed411b99d8b23bde) では一旦`MeshLab`経由で`Elmer`にSTLデータを読み込ませましたが、`OpenSCAD`の設定で直接読み込ませることができることが分かりました。
# OpenSCAD
![20210519a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/73e2ee60-9761-6815-0843-59240369d79a.png)
Edit > Preferences > Advanced > Export Features で`Default to ASCII STL export` にチェックを入れます。
![20210518a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/a4943317-3665-7fd6-3980-8f0d0c74ebc9.png)

```openscad.js
cube([0.3, 0.02, 0.01]);
```
ここで躓きましたのは、１）高さ方向の座標が `z` ではなく `y` ２）mm ではなく m が単位、の２点です。
# Elmer
[「Elmer」ではじめる連成解析](http://www.kohgakusha.co.jp/books/detail/978-4-7775-2128-9) にある Linear elasticity equation – 3D – Loaded elastic beam 風の例題を解きます。
# 成功
![20210518c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/a4790b03-368c-a843-45f4-6c5c871f5d07.png)

# まとめ
+ Elmer さんありがとう
+ OpenSCAD さんありがとう
+ 「Elmer」ではじめる連成解析 さんありがとう
URLのパラメータ取得→ページ更新時にその値を元にいろいろする


URLのパラメータ取得→ページ更新時にその値を元にいろいろする
https://router.vuejs.org/ja/guide/essentials/navigation.html


```vue
https://test.com?page=1
↓
this.$route.query.page // 1


https://test.com?page=1&submode=time
↓
this.$route.query.submode // time
```

---

参考URL

https://router.vuejs.org/ja/guide/essentials/dynamic-matching.html

こちらの記事より🙏

https://qiita.com/turmericN/items/b849ebe8313d863855b1

```vue
#　paramsの取得方法
localhost:8080/example/:id
→ this.$route.params['id']
```
Go: Server Sent Events のサンプル

こちらのページと同じことを行いました。
[Go言語でServerSentEvents(SSE)](https://qiita.com/taqm/items/e132a1aa55690a22b655)

![go_server_sent_events.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/c1782f05-b9dd-046c-2223-4b171ca56eca.png)

フォルダー構成

```text
$ tree
.
├── main.go
└── static
    └── index.html
```

```go:main.go
// ---------------------------------------------------------------
//
//	main.go
//
//						May/19/2021
// ---------------------------------------------------------------
package main

import (
	"fmt"
	"os"
	"log"
	"net/http"
	"time"
)

// ---------------------------------------------------------------
func sse(w http.ResponseWriter, r *http.Request) {
	flusher, _ := w.(http.Flusher)

	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")

	// 1秒おきにデータを流す
	t := time.NewTicker(1 * time.Second)
	defer t.Stop()
	go func() {
		cnt := 1
		for {
			select {
			case <-t.C:
				fmt.Fprintf(w, "data: %d\n\n", cnt)
				cnt++
				flusher.Flush()
			}
		}
	}()
	<-r.Context().Done()
	log.Println("コネクションが閉じました")
}

// ---------------------------------------------------------------
func main() {
	fmt.Fprintf (os.Stderr,"*** 開始 ***\n")
	fmt.Fprintf (os.Stderr,"http://localhost:8080/\n")

	dir := http.Dir("./static")
	http.HandleFunc("/event", sse)
	http.Handle("/", http.FileServer(dir))
	http.ListenAndServe(":8080", nil)

	fmt.Fprintf (os.Stderr,"*** 終了 ***\n")
}

// ---------------------------------------------------------------
```

```html:static/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8" />
<title>Server Sent Events</title>
</head>
<body>
<h2>Server Sent Events</h2>
<blockquote>
<p>count: <span id="cnt">0</span></p>
</blockquote>
<script>
    const ev = new EventSource('/event');
    ev.addEventListener('message', (e) => {
      cnt.textContent = e.data;
    });
</script>
<hr />
May/19/2021 PM 17:40<br />
</body>
</html>
```

サーバーの起動

```text
$ go run main.go
*** 開始 ***
http://localhost:8080/
```

ブラウザーで、
[http://localhost:8080/](http://localhost:8080/)
にアクセスします。
VPCフローログの中身を見てみよう

## そもそもVPCフローログとは？

VPC、ネットワークインターフェイスやサブネットとの間で行き来するトラフィックに関する情報をキャプチャできるようにする機能で
CloudWatch Logs か s3で発行できます。

VPCフローログの詳細は [こちら](https://qiita.com/miyuki_samitani/items/08c4823e2a43f8a99825) をごらんください

## VPCフローログの中身を見てみよう

VPCフローログはcloudwatchのロググループで確認することが出来ます。

![2021-05-181CloudWatch Management Console - Google Chrome 2021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/1267248c-14f2-8bec-ddeb-084e73d998ac.png)

中身は以下のようなものが出ています。

```
タイムスタンプ                       メッセージ
2021-05-17T22:48:43.000+09:00    2 XXXXXXXXX eni-aaaaaaa xx.xx.xx.xx 10.0.0.1 123 40658 17 1 76 xxxxxxxxxx xxxxxxxxxx ACCEPT OK
```

↑の中身の形式としては以下になります。
これはVPCフローログを設定する際にAWS のデフォルト形式として設定されているものです。

```
${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}
```

- ${version} : VPCフローログのバージョン
- ${account-id} : AWSアカウントのID
- ${interface-id} : ネットワークインターフェイスのID
- ${srcaddr} : 送信元IPアドレス
- ${dstaddr} : 送信先IPアドレス
- ${srcport} : 送信元ポート
- ${dstport} : 送信先ポート
- ${protocol} : プロトコルの番号
- ${packets} : パケットの数
- ${bytes} : バイト数
- ${start} : 開始時刻(unixtime)
- ${end} : 終了時刻(unixtime)
- ${action} : アクション(ACCEPT or REJECT)
- ${log-status} : ログ(OK or NODATA or SKIPDATA)

上記を踏まえて再度メッセージを確認します

```
2                           XXXXXXXXX             eni-aaaaaaa             xx.xx.xx.xx     10.0.0.1      123         40658          17              1         76      xxxxxxxxxx   xxxxxxxxxx    ACCEPT     OK
VPCフローログのバージョン   AWSアカウントのID   ネットワークインターフェイスのID     送信元IP      送信先IP   送信元ポート  送信先ポート  プロトコルの番号 パケットの数  バイト数     開始時刻      終了時刻     アクション  ログ
```

## 勉強後イメージ

わかるけど...そのまま見るのは難しい。。。

## 参考

- [VPC フローログの取得方法とログ見方](https://oji-cloud.net/2020/08/04/post-5242/)
MacOSでMattermostにログインできない問題を解決できたので試したことを晒す

## tl;dr
* デスクトップアプリの再インストールで解決した。

## 発生した問題
mattermostがログアウト状態になり、デスクトップアプリ"Mattermost"からログインしようとすると、以下の文章が出る。

```
不正または期限切れのセッションです。ログインし直してください。
```

## 試してうまくいかなかったこと
* パスワード変更：メールアドレスではじかれます。
* ブラウザからログイン：mattermostに問題なくログインできました。
* OS再起動：状況変わらず。ブラウザからはログインできます。

## 試してうまくいったこと
* デスクトップアプリのアンインストール・再インストール：ログイン成功した。
    * デスクトップアプリは最初に2つの情報の入力を求められるので、以下で対応する。
        * `Server Display Name`には任意の文字列を入力。（組織名など）
        * `Server URL`には「 https://mattermost.hogehoge.net/ 」と入力。

## おわり
もし同じ状況の方がいたら参考にしてください。
DBなしのchrome拡張でsecureに認証情報を扱いたい

chrome拡張は

- webのUIで
- サーバーやDBを用意せず
- 類似のwebサービスよりも強い権限をもって

ツールが提供できるのが利点

---

ただしDBをつかわない場合、データを永続化しようとしたときになかなか難儀します。
javascriptからデータを保存したい場合、以下のような方法が考えられます。

---
- local storage: 永続、文字列のみ
- session storage: タブ閉じるとデータが消える
- chrome storage: chrome限定

どれもkey-value型のデータストア
ただしこれらに重要な情報を置くのはどれもおススメできません

---
## 理由

- chrome storageふくめどれも平文で保存されます
![スクリーンショット (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538921/abe97870-959d-b79e-41da-92b7c06f30bb.png)

https://developer.chrome.com/docs/extensions/reference/storage/　

のchrome storageのドキュメントより


---


これらのstorageに保存した情報は、保存したサイトと同じドメインのサイトでなくてはアクセスできませんが、サイトやchrome拡張内にXSSの脆弱性があった場合窃取される可能性があります
自分が気を付ければ防げる話でなく、使っているライブラリの脆弱性を突かれる可能性もあり、これらに認証情報を置くことはかなり危険といえます
(ちなみにいくつかのchrome拡張のコードを見ましたが、そのままストレージに保存して使っているのもちらほらありました)


---


自作の拡張ではlocal storageに以下の形で保存するようにしました


key: ハッシュ化 
value: 元データを、ユーザーの入力したパスワードをハッシュ化したものをキーとして暗号化


---


よしこれで完全に安全だな！
→ __NO!__


---


このロジックはjavascriptに書いてあるため、この拡張を使う全ユーザーに筒抜け
攻撃者がソースコードを見てロジックを知っていた場合、ユーザー入力のパスが割れた瞬間、認証情報がばれます


---


## まとめ
- chrome拡張単体で認証情報を安全に扱うのは難しい(逆に自分がtokenなどの入力を求められたときは注意)
- google系のサービスだけを扱うなら、chrome拡張からのoauth認証をサポートしてくれているので安全にできそう
- 現在lambdaに置き換えていくことを検討中
- マサカリ求む


---
EC2にgit cloneしようとした際のエラーに関して

railsで作成したアプリをEC2にgit cloneしようとした際にエラーと格闘することになったので、その備忘録を記載しておきます。


##fatal: could not create work tree dir
まず最初にgit cloneを実施した際に発生したエラーです。

```
ec2-user@~ $ git clone URL
>fatal: could not create work tree dir 'アプリ名': Permission denied
```

これは権限の問題である場合が多いです。
そのため、sudoを付けて実行します。


##Permission denied (publickey).

```
ec2-user@~$ sudo git clone URL
>Cloning into '~'...
~省略〜
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```

まず「Permission denied (publickey).」という部分から公開鍵の設定がうまくいっていないと考え、configファイルの記述を見直したり、鍵の再作成をしてGitHubのSSH keysの再設定も実施しましたが、同じエラーが出るだけでした。
その過程でSSH認証はきちんとできていることが確認できたので、後怪しい部分はユーザー権限ぐらいしか思い浮かびません。
今回は、/var/www/railsディレクトリにクローンしたかったので、各ディレクトリに権限を付与していきます。

```
$ cd /
$ sudo chown ec2-user var
$ cd var
$ sudo chown ec2-user www
cd www
$ sudo chown ec2-user rails
$ cd rails
```
個別ユーザーでログインしている場合は、ec2-userの部分が個別ユーザー名となります。

これで再度、git cloneを実施すると無事クローンすることができました。
念のため、lsコマンドで確認してもしっかりとアプリ名が記載されていました。

いつの間に権限が変わってしまったのか不明だったので、エラー解決まで時間がかかってしまいました。

同じエラーが発生しており、鍵の再作成などでも解決できない方は一度権ユーザー権限の確認をしてみてはいかがでしょうか。
【Wordpress】特定のカスタム投稿タイプの記事を呼び出し（コピペ用）

```
<?php
$query = new WP_Query(
    array(
        'post_type' => 'カスタム投稿タイプ',
    )
);
?>
<?php
if ( $query->have_posts() ) : ?>
        <?php while ( $query->have_posts() ) : $query->the_post();?>
            <!--ここにループさせたい内容-->
        <?php endwhile; ?>
<?php endif; wp_reset_postdata(); ?>
```
仮想環境の整理　jetsonNX jetpack4.5

ここで紹介するツールは、python自体では無く、pythonと一緒に利用するライブラリ群を切り替えるもの。

##virtualenv
python2.7以上及びpython3.4以上
python3.0-3.3はだめ。
サードパーティー互換ソフトである。
python3 -m virtualenv -p python3 envname1　で環境構築。

##venv
python3.3以上（記載時3.94最新）
python純正：自分的にはvenvが良いと思う。
python3 -m venv envname2　で環境構築。

##pyvenv
pyenvと似ていて紛らわしい。
python3.3 3.4で推奨　3.6で非推奨になった。

pyenvはpythonのバージョン切り替えのツール
https://qiita.com/katakaku/items/105d7cf74a2196add992

##sourceコマンドによる環境の有効化
いずれのツールでもターミナルから
source envname/bin/activate　で仮想環境をスタート。
sourceコマンドは、実行後開いたままになるスクリプトを実行するコマンドらしい。

python -V ,python3 -V　いずれも、仮想環境のフォルダのリンクから、さらに元環境のpython3にリンクされている。
pip も pip3　どちらも仮想環境フォルダの同じpipにリンクしている。

従って、仮想環境内ではpipとpip3を使い分けなくて大丈夫。pythonとpython3も使い分けなくて大丈夫。

##pip ライブラリのインストール

仮想環境内でpip install ライブラリ名　でインストールする。
ここで、sudo pip install ライブラリ名 をやってしまうと仮想環境を飛び越して元環境にライブラリをインストールしてしまうので注意。

仮想環境内でwhich pip , which sudo pip, which sudo pip3 など試すと分かります。
もう一度、仮想環境内ではsudoを使わない！！
GoogleContainerTools/distrolessでshしたい俺の人生

俺です

[コレ](https://github.com/GoogleContainerTools/distroless)でshしたいときはdebugタグを使います。
busybox/shを実行するかpath通ってるのでsh実行するです。


```
❯ docker run -ti --entrypoint "/busybox/sh" gcr.io/distroless/nodejs:14-debug
Unable to find image 'gcr.io/distroless/nodejs:14-debug' locally
14-debug: Pulling from distroless/nodejs
5dea5ec2316d: Already exists
bb771d6dc9a1: Already exists
e4a0058105ea: Already exists
6f78ac1091ad: Already exists
826040e829d0: Already exists
Digest: sha256:5ba14b105d669655871b511466100cc5ba06cff7a988e9d8e104e6a73c553b9e
Status: Downloaded newer image for gcr.io/distroless/nodejs:14-debug
/ #
```

終わりなので。
パイプライン処理楽しい！

# はじめに
ふざけたタイトルをつけてしまったので、内容をざっくりと説明すると、「あるディレクトリに含まれているファイルのうち(ファイル名でソートされているものとして)上からn個のファイルを別ディレクトリにまとめてコピーする」ということを(シェルスクリプトを書いたりしてゴニョゴニョするのも面倒なので)1行のコマンドで行えないものかと思い、調査した際の備忘録になります。

上記の目的を果たすためにどうしたかを述べただけであり、体系的にまとまっているわけではない点はご容赦ください。

# 設定
状況設定として`/Datasets/A`というディレクトリに`data00.h5` ~ `data99.h5`という100個のデータがあり、このうち上から20個を`/Datasets/B`というディレクトリにコピーしたいとします(ここではh5データを仮定していますが、h5データでなくても問題ありません)。

上から10個であれば、ワイルドカードを使って

```bash
$ cp /Datasets/A/data0*.h5 /Datasets/B/.
```
などとすることもできますが、このやり方では10個以外の場合ではうまくいきません。

そこで、`ls`コマンドや`head`コマンドなどをうまく組み合わせて、ディレクトリにある任意の数のデータをまとめてコピーしたいというのが今回の趣旨になります。

# パイプライン処理について
まず簡単にパイプライン処理についておさらいしたいと思います。
パイプライン処理というのは、あるコマンドの出力を別のコマンドの入力として使うことで、複数のコマンドを繋げパイプラインにして行う処理のことです。

例として、先程の`/Datasets/A`にあるデータのうち上から5個だけを表示するには次のようにします。

```bash
~/Datasets/A$ ls -1 | head -5
```
`ls -1`はディレクトリ内のファイルの一覧をリストとして縦に出力するコマンドです。
これを実行すると次のようになります。

```bash
~/Datasets/A$ ls -1 
data00.h5
data01.h5
(中略)
data99.h5
```
また`head -5`は入力のうち上から5行だけを表示するコマンドになっていて、`|`で二つのコマンドを繋ぐことで、`ls -1`の出力を`head -5`の入力とすることができ、その出力は次のようになります。

```bash
~/Datasets/A$ ls -1 | head -5
data00.h5
data01.h5
data02.h5
data03.h5
data04.h5
```

# 本題
ディレクトリ内のファイルのうち上位20件をコピーするには、パイプライン処理を使って`ls`コマンド、`head`コマンド、そして`cp`コマンドを組み合わせると上手くいきそうな気がします。
しかし、`cp`では二つ引数を取ることが問題になります。つまり

```bash
~/Datasets/A$ ls -1 | head -20 | cp /Datasets/B/.
```
のようにしてしまうと、`/Datasets/B/`というのがコピー元のパスなのかコピー先のパスなのかが明らかでなく、エラーが出てしまいます。

そこで`xargs`というコマンドを用います。
`xargs`コマンドというのは「パイプライン処理を明示的に行うためのコマンド」というように筆者は理解しています。用途としては様々あるのですが、ここでは`-i`というオプションにより、直前のコマンドの出力を次のコマンドのどの位置で引数にするかを指定できることを利用します。

つまり、これにより`ls -1 | head -20`の出力を`cp`コマンドの一つ目の引数とすることを明示的に指示することが可能になります。具体的には次のようにします。

```bash
~/Datasets/A$ ls -1 | head -20 | xargs -i cp {} /Datasets/B/.
```
`-i`オプションを使うことで`{}`の位置に直前のコマンドの出力が入力されます。

以上のようにすることで、ディレクトリ`/Datasets/A`にあるファイルのうち上から20個のファイル`data00.h5` ~ `data19.h5`を無事、ディレクトリ`Datasets/B`にコピーすることができました。

# おわりに
やったこととしては全然大したことはやっていないのですが、パイプライン処理は上手く組み合わせると何でもできる気がしてきて楽しいですね。今回は「`xargs`コマンドってすごいね！パイプライン処理楽しい！」っていうお話でした。
【LaTeX】改行のある数式環境の最後に一つだけ番号をつける方法

# 目的
専門書などを読んでいると長〜い align 環境の数式の最後に一つだけ番号が振ってあることがよくあります．あれを自分でも書きたいという時が誰しも往々にしてあると思います．

# 課題
質問掲示板でよくあるように，最後以外のすべてに`\notag`などを振って番号を消すのは，もし数式が10行くらいになった場合非常に面倒です．

また，数式環境を split，aligned などを使って二重にする方法も考えられるかもしれません．しかし，コードや頭の中が煩雑になるうえに，番号の位置が最後ではなくて環境の真ん中に番号が付きます．

加えて，`\tag{1}`などと具体的に自分で番号をつけた場合，LaTeXの最も重要な利点である自動番号付けが失われてしまいます．当然のことながら一般的で自動的な書法がほしいです．
どうしたらよいでしょうか．→ 追記はこちら [追記 (2021 11/15)](#追記-2021-1115)

#結論


```latex
\stepcounter{equation}\tag{\theequation} 
```

これを使ってください．環境は`align*`です．

# 具体例

```latex
\begin{align*}
    x
    &= abc\\
    &= abc\\
    &= abc.
    \stepcounter{equation}\tag{\theequation} 
\end{align*}
```


#まとめ

改行してある数式中で，真ん中ではなく最後に一つだけ番号をつけたいときに，最も簡潔な記法は`\stepcounter{equation}\tag{\theequation}`です．

#追記 (2021 11/15)
故あって現在のデフォルトの環境は `align` ではなく `aligned` にしています．上記の結果と同様の結果が

```latex
\begin{equation}
    \begin{aligned}[b]
        x
        &= abc\\
        &= abc\\
        &= abc.
    \end{aligned}
\end{equation}
```
によって得られます．利点は以下です．

* `align` 同様に１行の数式でも通常通りの `equation` 環境の書き方で問題なく書けます．
* 数式番号を消したいときは `equation*` 環境にすればできます．
* 数式番号の位置は `[c]` (center, default), `[t]` (top), `[b]` (bottom) から選べて便利です．特に `[b]` と 2行立ての際の中央の番号 `[c]` が便利です．
* `\usepackage{ulem}`で使える下波線`\uwave{}` が `aligned` 環境全体を囲むことで複数行に適用できます．波線をよく使うので個人的にはこれが嬉しかったです．


数式番号は最後に一つがデフォルトで，振りたい箇所が２つ以上になった時に `align` を使っています．
VSCode のユーザスニペットはこちらです．

```json
	"equation": {
		"prefix": ["equation", "ee"],
		"body": [
			"\\begin{equation}",
			"\t\\begin{aligned}[b]$0",
			// "\tx$0",//"\t${1:x}",
			"\t\\end{aligned}",
			"\\end{equation}",
		],
		"description": "equation"
	},
```
コンパイルエラーを出したくないので`[b]`の横にカーソルが来るようにしています．お好みで `$0` と`$1` の記述を変更して下さい．VSCode の Math Preview 機能は `align`,`aligned` どちらも問題なく使えます．

（**旧タイトル** 長い align 環境の数式の最後に番号（タグ）を一つだけ振る小ネタ）
laravel5.8ミドルウェアについて[メモ]

laravelで自作のミドルウェア作成する機会があったのでメモとして残していこうと思います。

■目次
・laravel MVC+(ルーティングとミドルウェア)について　[図]
・ミドルウエア実装

■環境 
・laravel5.8
・php7.2


#laravel MVC+(ルーティングとミドルウェア)について　[図]
![Untitled (Draft)-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700700/46834ab6-abf8-0550-e66b-4731d912faac.jpeg)

図で書くとこんな感じです。
ミドルウェアは
ルーティングからコントローラ行くまでに実行されます。
（例）認証チェックなど


#ミドルウェア実装
■前提(投稿アプリ作成)
現状として、
投稿４つあります。
id:4が自分が投稿したidとします。
しかし、localhost/post/1/editにアクセスするとid:1の投稿にアクセスでき変更更新ができてします。
今回はミドルウェアを使って解決していこうと思います。

####１、ミドルウェア作成

```php:
php artisan make:middleware EditCheck
```
app\Http\Middlewareに作成されます。

####２、ミドルウェア編集

```php:EditCheck.php
<?php

namespace App\Http\Middleware;

use Closure;
use App\Post;　//追加

class EditCheck
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $param = $request->route()->parameter('id');
        //リクエストできたルートのパラメータidを取得
        //今回は1が入ってくるとします
        
        $post = Post::findOrFail($param);
        //投稿したidを見つけます。今回は１の投稿を見つけます。

        if (auth()->user()->id !== $post->user_id) {
        //ログインしたユーザーidと投稿したユーザーidが一致しない場合
            return redirect('/')->with('error', '許可されていない操作です');
            //一番初めの画面に戻ります。
        }
        return $next($request);
        //正常なら通常のページが表示されます
    }
}
```

####３、ミドルウェア登録

登録方法が３つあります

①全ての処理に共通して処理を行う場合→$middleware 

②複数のミドルウェアをまとめて(グループ)登録する場合→$middlewareGroups

③単体で使うミドルウェアを登録しておく場合→$routeMiddleware

```php:Kernel.php
protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'editcheck' => \App\Http\Middleware\EditCheck::class,//追加
```
今回は③を採用します。

####４、Routeでミドルウェア設定

```php:web.php
    Route::get('post/{id}/edit', 'PostsController@edit')->name('post.edit')->middleware('editcheck');
```

これでlocalhost/post/1/editにアクセスすると最初の画面に戻されると思います。
Roomビルド時に「A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction    > java.lang.reflect.InvocationTargetException (no error message)」が出た時参考にしたこと

Roomビルド時に「Execution failed for task ':app:kaptDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction    > java.lang.reflect.InvocationTargetException (no error message)」が出た時参考にしたこと

#参考
[こちら](https://friegen.xyz/android-room-introduction-procedure/)の「作る順番はEntity→Dao→Databaseが良いかなって感じた。

この順番なら基本的にビルドエラーは起こらない、、、はず！」という記述がとても参考になりました。

[逆引きRoom的ななにか(使ったところを加筆中) - Qiita](https://qiita.com/Nabesuke/items/24b9be49ae1d43895cee#%E5%AE%A3%E8%A8%80%E6%99%82%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%AA%E3%81%A9-2)

[[Android Architecture Components] – Room 詳解 – PSYENCE ...]
(https://tech.recruit-mp.co.jp/mobile/post-12311/)

またデフォルトではtrueのexportSchemaについて上記で確認し、「app/schemasの下にschemaのjsonを吐いてくれるらしい」とあったため、「exportSchema=false」とすることにしました。


#やったこと
Roomのみ依存関係を記述し、Entity、Dao、Databaseと段階的に一つずつビルドしてみる。

```gradle:build.gradle(app)

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
   
}

(中略)    
    implementation "androidx.room:room-runtime:2.3.0"
    kapt "androidx.room:room-compiler:2.3.0"

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:2.3.0"

    // optional - Test helpers
    testImplementation "androidx.room:room-testing:2.3.0"

```

またDaoで指定したSQLiteのQueryアノテーションで指定したSQL文は正しいか確認したり、Entityで指定したtableNameを合わせて確認したりしました。

```kotlin
package com.example.rworksample00019

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SupportFactory

@Database(entities = [RequestDocumentsPersonInfo::class], version = 2, exportSchema = false)
abstract class RequestDocumentsPersonInfoDatabase: RoomDatabase() {

    abstract fun requestDocumentsPersonInfoDao(): RequestDocumentsPersonInfoDao

    companion object {
        fun buildDatabase(context: Context): RequestDocumentsPersonInfoDatabase {
            return Room.databaseBuilder(
                context,
                RequestDocumentsPersonInfoDatabase::class.java,
                "request_documents_person_info.db"
            ).openHelperFactory(SupportFactory(SQLiteDatabase.getBytes("PassPhrase".toCharArray())))
                .build()
        }
    }
}

```
python の google-cloud-storage で バケット内のオブジェクトを start_offset, end_offset, (prefix)を使って直近の日付ファイル名に絞り込む

# 背景
対象バケットのオブジェクト一覧は以下で出力できる

```py
from google.cloud import storage

client = storage.Client()
bucket = client.bucket('BUCKET_NAME')
blobs = client.list_blobs(bucket)
for blob in blobs:
    print(blob.name)
```

出力イメージ

```
others/20210515.csv
others/20210516.csv
others/20210517.csv
others/20210518.csv
others/20210519.csv
others/20210520.csv
path/to/files/20210516.csv
path/to/files/20210517.csv
path/to/files/20210518.csv
path/to/files/20210519.csv
path/to/files/20210520.csv
```

この時、 `取得日から2日前までのobjectのみを取得したい` とする。
prefixだけではうまくできない。
start_offsetを使うとこの絞り込みができたので紹介。

# まずはprefixで絞り込む方法
よく使うprefixで絞り込む方法を一応紹介。
prefixで指定したpathのオブジェクトに絞り込んでくれる。

```py:prefix.py
from google.cloud import storage

client = storage.Client()
bucket = client.bucket('BUCKET_NAME')
blobs = client.list_blobs(bucket, prefix='path/to/files')
for blob in blobs:
    print(blob.name)
```

出力イメージ

```
path/to/files/20210516.csv
path/to/files/20210517.csv
path/to/files/20210518.csv
path/to/files/20210519.csv
path/to/files/20210520.csv
```

# start_offset で直近2日に絞り込み

実行日は `2021/05/20` だった場合に、2日前の `2021/05/18` からのファイルだけ取得したいとする。その場合 以下のようにstart_offsetを使うと絞り込める。

```py
from google.cloud import storage
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

client = storage.Client()
bucket = client.bucket('BUCKET_NAME')
now = datetime.now(ZoneInfo("Asia/Tokyo"))
two_days_ago_str = (now - timedelta(days=2)).strftime('%Y%m%d')
blobs = list(client.list_blobs(
    bucket,
    prefix='path/to/files/',
    start_offset=f'path/to/files/{two_days_ago_str}'
))
for blob in blobs:
    print(blob.name)
```

出力イメージ

```
path/to/files/20210518.csv
path/to/files/20210519.csv
path/to/files/20210520.csv
```

start_offset　で指定した日付以降のオブジェクトに絞り込まれているのが確認できる。
gcsのオブジェクトは辞書順にsortされており、start_offset を指定すると、その文字列よりも辞書順でそれ以降のオブジェクトを取得してくれるようになる。
end_offsetを指定すると辞書順でそれ以前のものに絞り込まれる。


# 参照
### [Storage Client#list_blobs](https://googleapis.dev/python/storage/latest/client.html#google.cloud.storage.client.Client.list_blobs)
herokuへデプロイした時にApplication errorが起きた

##はじめに##

とうとうfurimaアプリが完成し、 herokuにデプロイした時にApplication errorが起きてしまいました。
その時にどう対応したかをアウトプットします。

エラー時に参考させて頂いた記事になります。↓↓↓

https://qiita.com/kents1002/items/e1b90c71897c55b8e870




![image-644x378.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264460/c1d894c1-097b-7107-4ba0-e04189f1cc02.png)


##heroku logs --tail##

Heroku上のアプリケーションが稼働したログを、表示するためのコマンドです。
ログの最後の10行を表示するためのオプション(--tail)

まずは画面に出てきた通り、*heroku logs --tail*を確認しました。

```
2021-05-19T05:57:12.141751+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=アプリ名.herokuapp.com request_id=f257bdcd-bb86-416d-ba92-7650c15d3253 fwd="124.211.233.13" dyno= connect= service= status=503 bytes= protocol=https

```
heroku公式でも調べてみました。


https://devcenter.heroku.com/articles/error-codes#h10-app-crashed


*A crashed web dyno or a boot timeout on the web dyno will present this error.*
和訳：Web dynoがクラッシュした場合や、Web dynoの起動がタイムアウトした場合に、このエラーが表示されます。

分かりませんでした。。。

##heroku run rails c##

はじめに記載させて頂いた記事を参考にheroku上でコンソールを開いて詳細を調べました。

```
 `require': /app/app/controllers/purchase_histories_controller.rb:47: syntax error, unexpected end-of-input, expecting end (SyntaxError)
```

がっつり誤字で構文エラーが出ていた事が分かりました。
pyenv のインストール　jetsonNX  jetpack4.5

本体環境と異なるバージョンのpythonを利用したい場合にpyenvを利用する。
pythonのパッケージ管理pipではなく、その上流側のpythonをいじるのでpip install などではインストールできない。
仮想環境自体の準備はこちらhttps://qiita.com/katakaku/items/9ad80ffe6d52b14de0ad
##JetsonNXでvenvとpyenvを利用

pyenvのインストール（参考HP)
https://karaage.hatenadiary.jp/entry/2016/04/04/073000
https://github.com/pyenv/pyenv

```
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```

コピーしてきて、パスを通せば使えるらしい。パスを通すためには、~/.profileの上の方？（sources ~/.bashrcを実行する前ということか）に以下の３行を書くらしい。
（pyenv のgitでの記載よりPut these lines into ~/.profile before the part that sources ~/.bashrc:）

```
$ export PYENV_ROOT="$HOME/.pyenv"
$ export PATH="$PYENV_ROOT/bin:$PATH"
$ eval "$(pyenv init --path)"
```

再ログインすることで環境変数が有効になる。
```pyenv install -l　　利用できるpythonバージョンの一覧
```
PythonやAnaconda、Minicondaなどいろいろ選択可能。今回は3.8.10を選んでみる。
```pyenv install 3.8.10
```

インストール失敗　
		WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
		WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
		ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?


　　　　https://codelife.cafe/entry/pyenv-install-3.7.0を参考に
```sudo apt-get update
sudo apt-get install -y libbz2-dev libreadline-dev libsqlite3-dev
sudo apt-get install libssl-dev
```としたところ
再度```pyenv install 3.8.10
```でインストール成功

```pyenv versions``` でインストールされていることが確認できる。
　　
```pyenv global 3.8.10```で切り替えた環境に移行
```python -V
python3 -v```いずれでもpython3.8.10を参照していることが確認できる。
##仮想環境を構築する
この状態でvenvを使って仮想環境を構築。
python3 -m venv py38
pyenvの環境を終了して
pyenv global system
仮想環境に入る。
source py38/bin/activate
python -Vで再度バージョン確認

pyenvは仮想環境を作るときだけ動作させれば良いようです。

初めての記事2021.5.19
##その他
pyenvのリストにはanacondaも出てくるけど、導入しようとしたところ
ERROR: The binary distribution of Anaconda3 is not available for Linux-aarch64.
とエラーになってしまった。jetsonではアーキテクチャーが異なる問題なのか、どう対応すれば良いかよく分からない。
python3.8.10なんかも、pyenv-build みたいなコマンドが必要でしょうか。まだビルドには馴れていません。
JSONデータベースという選択肢（Oracle、MongoDB、そしてCouchbase) 序. 第一正規化制約を超えて

# はじめに

この記事は、およそ取り止めのない言葉から構成された、雑感とでもいうべき内容ですが、そのようなところから始めることにも、一定の意義があるのではないかと思っています。

## NoSQLデータベースのカテゴライズについて

NoSQLデータベースの世界では、JSONを扱うデータベースは、「ドキュメント指向データベース（ドキュメントストア）」と呼び習わされてきました。

「ドキュメント」は普通名詞としてあまりに一般的すぎるため、リレーショナル・構造化データベースに対比して、「準構造化データベース」とでも呼んだ方が、技術的には、実態をよりよく表現することができる、と思います。

あるいは、いっそのことJSONデータベース、と読んだ方が通りが良いとも感じます（Oracle社は、自社のクラウドサービスをそう呼んでいます）。

## 「準（半）構造化データ」という呼称について

「semi-structured data」という用語には、暗にstructured(構造)イコール、リレーショナルデータベースのテーブル形式、ということが含意されている、と解釈できます。「構造」という言葉を、素直に受け止めるのであれば、これはリレーショナルデータベースに対する行き過ぎた偏向であって、テーブル形式でないデータも、紛れもなく「構造」を持っていると考えられます。

テーブル形式は、リレーショナルデータベースにデータを格納する際の制約である、第一正規化を強制された構造、ということができ、その意味では、「準構造化データ」というよりも、「非第一正規化形データ」とでも呼ぶ方が、実態を表しているのではないでしょうか？

# 最後に

この記事は、流通している用語を変更するべきと訴えているのではなく、そうした地点から考えてみることを意図しているため、疑問形のセンテンスを投げ出したまま終わります。


# 参考情報

https://en.wikipedia.org/wiki/Semi-structured_data
【個人開発】プログラマ専用のコミュニティサイトを作った話。

![TOPページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197628/e894b610-c376-2023-796e-10307a9c2d60.png)
# 始めに
プログラミングに関して気軽に雑談したり、情報交換したり、質問したりできる場所があったらいいなと思い、このサイトを作りました。

# サイトの概要
https://programmer-community.herokuapp.com/

情報交換や質問、雑談等自由に行えるコミュニティサイトです。

# 機能
機能は至ってシンプルで、グループに参加してチャットを行えます。
グループは自由に作ることができます。


# 使い方
1. [アカウントを作成](https://programmer-community.herokuapp.com/register)
2. [グループ](https://programmer-community.herokuapp.com/groups)に参加
3. グループのチャットのページでチャットを行う

# 使用技術
- Laravel
- Vue
- Heroku

# 製作期間
約2ヶ月

# 大変だったこと
開発環境ではうまく動作したのに本番環境だと思ったように動かないことが多々あり、大変でした。

# 学んだこと
Herokuは便利だけど、Herokuの特性を理解していないと嵌まりやすいです。
Herokuの特性を理解するために少しインフラを学びました。(インフラを考えなくていいって聞いたのに！笑)

# 感想
Laravelって便利。
まぁ、結局、Herokuも便利。

# 最後に
「こんな機能が欲しい」、「ここを直して欲しい」等要望がありましたら気軽にコメントしてください。(反映するかはわかりませんが。笑)
初心者から玄人まで、誰でも気軽に参加してください！
RubyのExceptionとStandardError

- StandardErrorは、Exceptionを継承している。
- rescue句において、`resue => XxxError`という形でキャッチするエラー名を指定しなかった場合
   - StandardErrorはrescueされるが
   - Exceptionはrescueされない


👉 自前でエラークラスを定義するときは、エラー種類の指定なしのrescueでもcatchできるように、`StandardError`を継承する。
Flutter firebase_auth 0.18.0以降におけるログイン処理の実装例

#はじめに
本記事はFlutterでFirebase Authenticationを使ってGoogleアカウントログインを行うための情報を記載しています。
とても参考になる既存の記事は存在しますがfirebase_auth 0.18.0以前の内容がほとんどで、一部実装を書き換える必要があります。

#参考にさせて頂いた記事
[FlutterでFirebase Authenticationを使ったGoogleアカウントログインを実装してみた](https://qiita.com/unsoluble_sugar/items/95b16c01b456be19f9ac#%E3%83%97%E3%83%AD%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E8%A1%A8%E7%A4%BA%E7%94%BB%E9%9D%A2)

#pub.devのchangelog
https://pub.dev/packages/firebase_auth/changelog

該当のアップデート内容は主に以下の部分になります。

>0.18.0
>　BREAKING: The FirebaseUser class has been renamed to User.
>　BREAKING: The AuthResult class has been renamed to UserCredential.
>　DEPRECATED: All sub-class (e.g. GoogleAuthProvider) getCredential() methods have been deprecated in favor of credential().

#実装例
```main.dart
Future<User> _handleGoogleSignIn() async {
    final GoogleSignIn _googleSignIn = GoogleSignIn();
    final GoogleSignInAccount googleUser = (await _googleSignIn.signIn())!;
    final GoogleSignInAuthentication googleAuth = await googleUser.authentication;

    // getCredential()をcredential()に置き換える
    final AuthCredential credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    // AuthResultをUserCredentialに置き換える
    final UserCredential userCredential = await _auth.signInWithCredential(credential);

    // FirebaseUserをUserに置き換える。
    final User user = userCredential.user!;
    assert(user.email != null);
    assert(user.displayName != null);
    assert(!user.isAnonymous);
    assert(await user.getIdToken() != null);

    // FirebaseUserをUserに置き換える。
    final User currentUser = _auth.currentUser!;
    assert(user.uid == currentUser.uid);

    return user;
  }

```

#まとめ
今回はGoogleアカウントによるログイン処理を例に上げましたが、Facebookやその他ソーシャルログインでも同様の対応が必要になります。
【Python】祝日情報を追加する

# 目的
時系列データの分析で曜日依存について検証するとき、祝日情報も追加したい。

# ゴール
時系列データフレームの日付列に対し `dt.weekday` で 曜日列"weekday" 追加し `月:0、火:1、水:2、木:3、金:4、土:5、日:6` とした後、祝日該当日に `祝:7` を上書きする。
さらに、カレンダーでは平日となっている年末年始休暇期間など経済活動が休日モードになっている日も任意に `祝:7` として扱えるようにする。

# 実践
```python
import pandas as pd
```
2019年→2020年の年末年始を含む期間の日時情報のデータフレーム "df" を処理

```python
# 日付列"date"をタイムスタンプ型にする
df["date"] = pd.to_datetime(df["date"])

# 曜日情報列"weekday"をデータフレームに追加
df["weekday"] = df["date"].dt.weekday
```
年末年始休暇期間中（2019-12-30:月曜、2019-12-31大晦日:火曜、2020-01-01元旦:水曜、2020-01-02:木曜、2020-01-03:金曜）は全て平日判定👇
![df.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493633/adaa7023-9dd0-55f7-a57d-ff805faeedc9.png)
1955年以降の日本の祝日がリストアップされたcsvデータが取得できる内閣府webページ『[国民の祝日について](https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html)』から祝日データ "[syukujitsu.csv](https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv)" を読み込み
!!ATTN!!
"syukujitsu.csv" は厳密な祝日しかリストアップされていない（年末年始休暇期間やお盆休みなど経済活動が休日モードな平日は別途定義してリストへ任意追加する必要あり）

```python
# "syukujitsu.csv"読み込み
syukujitsu = pd.read_csv("https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv", encoding="SHIFT-JIS")

# "国民の祝日・休日月日"列をタイムスタンプ型にする
syukujitsu["国民の祝日・休日月日"] = pd.to_datetime(syukujitsu["国民の祝日・休日月日"])
```
2019年→2020年の年末年始期間を絞って表示すると、元旦のみ上がってくる👇
![yobi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493633/36751f86-5e02-41f0-2833-bbfb86d6144d.png)
経済が休日モードの大晦日や三が日も祝日と同様に扱いたい！
　→ 祝日同様に扱いたい休日をリストアップしてデータフレームにする

```python
# 年末年始休暇の追加休日リスト"add"
add = ["2019/12/30", "2019/12/31", "2020/1/2", "2020/1/3"]

# 追加休日リストをデータフレーム"ADD"にする
ADD = pd.DataFrame(add, columns=["国民の祝日・休日月日"])
ADD["国民の祝日・休日月日"] = pd.to_datetime(ADD["国民の祝日・休日月日"])
```

内閣府の祝日データフレームに、作成した追加休日データフレームを結合して、祝日データフレームを更新

```python
# "syukujitsu"と"ADD"を結合して更新した祝日データフレーム"syuku"
syuku = pd.concat([syukujitsu, ADD]).sort_values("国民の祝日・休日月日").reset_index(drop=True)
```
2019年→2020年の年末年始に期間を絞って表示すると、元旦を含む年末年始休暇期間も祝日として上がってきた👇
![syuku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493633/238ae131-1340-f9b8-e98f-117690f62f0d.png)
日時情報のデータフレーム "df" の曜日情報列に対し、更新した祝日データフレーム"syuku"でリストアップした日に `祝:7` を付与する

```python
# 祝日フラグ７を該当日に付与する
for i in syuku["国民の祝日・休日月日"]:
    df["weekday"].mask(df["date"] == i, 7, inplace=True)
```
祝日データフレームで指定した日の"weekday"を7に上書き完了👇
![df_rvs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493633/8f14c878-4fc8-ab5a-1c18-422dfdabac2b.png)
【HTML】HTML5 タグ一覧

HTMLのタグの一覧を作りました。
随時更新をしていきます。

## ルート要素

ルート要素とは、ドキュメントの中で最初に現れるタグのことで、そのドキュメントの最上位に位置する要素のことです。

| 要素名 | 説明 |
| -- | -- |
|html|	文書のルート要素|

## 文書のメタデータ

ページが読み込まれてもウェブブラウザーには表示されない部分です。
headタグの中に記述します。
この部分には、例えば、 title といった情報や CSS へのリンク (もし HTML を CSS で修飾したいならば)、独自のファビコンへのリンク、そしてほかのメタデータ (HTML を誰が書いたのかとかその HTML を表現する重要なキーワードなど) の情報を含んでいます。

| 要素名 | 説明 |
| -- | -- |
|head|	メタデータの集まりを表す|
|title|	文書にタイトルを付ける|
|base|	相対リンクの基準となるURLを指定する|
|link|	別の文書類と関連付ける|
|style|	スタイルシートを指定する|
|meta|	文書に関する様々なメタデータを表す|

## 構造化タグ
HTML5では構造化タグと呼ばれる新しい要素（タグ）が追加され、
「ヘッダは header で囲み、フッタは footer で囲む」など、構造が明確になったといえる。
注意点として、レイアウト目的での構造化タグ使用はしないこと。

| 要素名 | 説明 |
| -- | -- |
|body|	文書の本体を表す|
|article|	自己完結したセクションを表す|
|section|	一般的なセクションを表す|
|nav|	ナビゲーションを表す|
|aside|	補足的なセクションを表す|
|h1 ~ h6|	見出しを付ける|
|header|	ヘッダを表す|
|footer|	フッタを表す|
|address|	連絡先を表す|

## コンテンツのグループ化タグ

| 要素名 | 説明 |
| -- | -- |
|p|	段落を表す|
|hr|	テーマの区切りを表す|
|pre|	整形済みのテキスト|
|blockquote|	引用セクションを表す|
|ol|	順序付きのリストを作る|
|ul|	順不同のリストを作る|
|li|	リストの項目を表す|
|dl|	記述リストを作る|
|dt|	記述リストの名前部分を表す|
|dd|	記述リストの値部分を表す|
|figure|	参照される図版を表す|
|figcaption|	図版にキャプションを付ける|
|main|	文書のメインコンテンツを表す|
|div|	特定の範囲をグループ化する|

## テキストの意味
| 要素名 | 説明 |
| -- | -- |
|a|	リンクを設定する|
|em|	強調を表す|
|strong|	重要性を表す|
|small|	細目のような注釈を表す|
|s|	すでに正確ではない内容を表す|
|cite|	作品のタイトル・著者名・URLを表す|
|q|	引用フレーズを表す|
|dfn|	定義される用語を表す|
|abbr|	略語や頭字語を表す|
|code|	ソースコードを表す|
|var|	変数を表す|
|samp|	プログラムの出力サンプルを表す|
|kbd|	ユーザーが入力する内容を表す|
|data|	コンピュータ向けのデータを指定する|
|sub|	下付き文字を表す|
|sup|	上付き文字を表す|
|time|	日付や時刻を表す|
|i|	他と区別されるテキストを表す（思考・専門用語 等）|
|b|	他と区別されるテキストを表す（キーワード・製品名 等）|
|u|	軽めのラベル付け|
|mark|	テキストのハイライト表示|
|ruby|	ルビ（ふりがな）を振る|
|rb|	ルビの対象テキストを表す|
|rt|	ルビテキストを表す|
|rtc|	ルビテキストのコンテナ|
|rp|	ルビテキストを括弧で囲む|
|bdi|	隔離されたテキストを表す|
|bdo|	書字方向を指定する|
|span|	特定の範囲をグループ化する|
|br|	改行する|
|wbr|	改行位置の候補を表す|

## 訂正
| 要素名 | 説明 |
| -- | -- |
|ins|	追加された部分を表す|
|del|	削除された部分を表す|

## コンテンツの埋め込み
| 要素名 | 説明 |
| -- | -- |
|img|	画像を表示する|
|picture|	レスポンシブイメージ（画像内容の最適化） |
|iframe|	インラインフレームを作る|
|embed|	プラグインデータを埋め込む|
|object|	外部リソースを埋め込む|
|param|	パラメータを指定する|
|video|	プラグインを使わずに動画を再生する|
|audio|	プラグインを使わずに音声を再生する|
|track|	動画・音声と同期するテキストトラック|
|source|	メディアリソースの候補を指定する|
|map|	イメージマップを作る|
|area|	イメージマップのリンク領域を設定する|

## テーブル
| 要素名 | 説明 |
| -- | -- |
|table|	表を作る|
|caption|	表にキャプションを付ける|
|colgroup|	表の列をグループ化する|
|col|	表の列を表す|
|tbody|	表の行をグループ化する（本体部分）|
|thead|	表の行をグループ化する（ヘッダ部分）|
|tfoot|	表の行をグループ化する（フッタ部分）|
|tr|	表の行を表す|
|td|	データセルを表す|
|th|	見出しセルを表す|

## フォーム
| 要素名 | 説明 |
| -- | -- |
|form|	入力フォームを作る|
|fieldset|	フォーム部品をグループ化する|
|legend|	部品グループにキャプションを付ける|
|label|	フォーム部品とラベルを関連付ける|
|input|	入力欄・選択肢・ボタンを作る|
|select|	プルダウンメニューを作る|
|option|	プルダウンメニューや入力候補の選択肢を作る|
|optgroup|	プルダウンメニューの選択肢をグループ化する|
|textarea|	複数行のテキスト入力欄を作る|
|button|	内容を持つボタンを作る|
|datalist|	入力候補のリストを作る|


|output|	計算結果の出力欄を作る|
|progress|	作業の進捗状況を示す|
|meter|	特定範囲内の数量や割合を示す|

## スクリプティング
| 要素名 | 説明 |
| -- | -- |
|script|	JavaScriptを埋め込む|
|noscript|	スクリプトが利用できない環境用の内容|
|template|	スクリプトで利用するHTMLコードのテンプレート|
|canvas|	スクリプト使ってグラフィックスを描く|

## インタラクティブ
| 要素名 | 説明 |
| -- | -- |
|details|	リクエストに応じて詳細情報を提供する |
|summary|	詳細情報のキャプションや要約を表す |
|menu|	ポップアップメニューを表す |
|menuitem|	ポップアップメニューの項目を表す |
[Objective-C][Socket.io]マルチバイト文字が文字化けする問題

Socket.ioを使ってiOS版トークアプリ（ネイティブ）を運用していて、以下の環境で文字化けしたので対策を実施。

[環境]
Socket.io : "Socket.IO-Client-Swift", "15.2.0"
Xcode 12.4
iOS 14.4.2
（サーバ環境については記事と関係ない為省略します。）

[現象]
メッセージをマルチバイト文字で送信した際に、サーバからアプリまでは正常に送れているもののアプリ内の処理にて文字が化ける。

```実際の文字列.txt
å¤§ä¹ä¿ è£å¹³
```

[原因]
Socket.ioのアプリ側初期化処理で必要なオプションが指定できていなかったこと。

[対処策]

```before_SocketManager.m
NSDictionary *option = @{@"log": @(NO)
                             ,@"secure": @(YES)
                             ,@"reconnectAttempts":@(0)
                             ,@"reconnects":@(NO)
                             ,@"ua":(記事と関係ないため省略)
                             };
self.manager = [[SocketManager alloc] initWithSocketURL:url config: option];
```

どうやら以下のオプションを追加する必要があった。

```after_SocketManager.m
NSDictionary *option = @{@"log": @(NO)
                             ,@"secure": @(YES)
                             ,@"reconnectAttempts":@(0)
                             ,@"reconnects":@(NO)
                             ,@"doubleEncodeUTF8":@(YES) //<--追加
                             ,@"forceWebsockets":@(YES) //<--追加
                             ,@"forcePolling":@(NO) //<--追加
                             ,@"ua":(記事と関係ないため省略)
                             };
self.manager = [[SocketManager alloc] initWithSocketURL:url config: option];
```

[経緯]
今回の不具合は、Xcodeを更新する際にpodの中身も更新したが、Socket.ioもずいぶん昔のバージョンを使用していた為更新した。その際にSwiftバージョンを上げなくてはならず、バージョンを上げた際にSocket.ioの初期化処理も修正した。オプションはこれまでのものを流用していたが、今回説明したものを正しく指定しなかったため文字化けが発生した。

そんなの知らんがな。
オフィシャルにでも明記しといてくれよ。

参考までに。
[参考文献]
https://github.com/socketio/socket.io-client-swift/issues/599
agora.io(WebRTC)と音声認識APIの連携

##概要
Web会議等で文字起こしは重要な機能になってきました。
この記事ではAI音声認識サービスにデータを投げるまでの処理について記載します。

##サンプル
[こちら](https://github.com/fu-jimoto/AgoraIO-VoiceRecognizer)をご利用ください。

##代表的なAI音声認識サービス
- Google Cloud Speech-to-Text
- Watson Speech to Text
- Microsoft Speech Service
- Amazon Transcribe

##サンプルの解説
今回のサンプルについては、いったんblobで音声データを保存してからAI音声認識サービスにポストする想定になっています。
（各サービスとのつなぎこみ部分については割愛しています。）

STEP1.音声ストリームの作成

```javascript:index.js
    //AgoraRTC.createMicrophoneAudioTrack(),*1
    navigator.mediaDevices.getUserMedia({ video: false, audio: true })
    .then((mediaStream) => {
      localAudioStream = mediaStream;
      const audioMediaStreamTrack = mediaStream.getAudioTracks()[0];
      // create custom audio track
      return AgoraRTC.createCustomAudioTrack({
        mediaStreamTrack: audioMediaStreamTrack,
      });
    }),
```

*1の部分が基本的なAgoraSDKAPIを用いた音声ストリームの作成になりますが、それだけではデータが足りないのでブラウザNativeなAPIでストリームを作成しています。
Agoraのチャンネルにはこのcustom audio trackを利用してパブリッシュします。

STEP2.録音の開始と停止

```javascript:index.js
$("#startSpeechToText").click(function (e) {
  recorder.record();
  $("#startSpeechToText").attr("disabled", true);
  $("#stopSpeechToText").attr("disabled", false);
});

$("#stopSpeechToText").click(function (e) {
  recorder.stop();
  voiceRecognize();
  recorder.clear()
  $("#startSpeechToText").attr("disabled", false);
  $("#stopSpeechToText").attr("disabled", true);
});
```

録音については[こちら(s1r-J/Recorderjs)](https://github.com/s1r-J/Recorderjs)のJavaScriptライブラリを利用しています。
録音開始して、任意のタイミングで録音を停止します。

STEP3.AI音声認識サービスへのデータポスト

```javascript:index.js
function voiceRecognize() {
  recorder.exportWAV(function(blob) {
    let reader = new FileReader()
    reader.onload = function() {
      //POST to Service
    }
    reader.readAsArrayBuffer(blob)
  })
}
```
wav形式で一旦出力し、AI音声認識サービスへポストするフローになります。
各種AI音声認識サービスとの連携についての詳細はサービス毎のドキュメントを参照ください。

レスポンス内容をログに出力したり、画面に表示させる等も独自の実装になります。


##注意事項
音声認識の精度についてはAI音声認識サービス側に依存します。


##追記
Chrome89から[自動字幕起こしが](https://support.google.com/chrome/answer/10538231?hl=ja)追加されました。
現在は英語のみ対応とのことですが、AgoraWebSDKを用いたビデオ通話でも動作は確認できました。
![スクリーンショット 0003-05-20 14.41.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/30dd5533-3516-6cc7-2987-d29ad7580ee8.png)



#最後に
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=909e3c9712d96c469df4)
[<img width="912" alt="スクリーンショット 0001-08-15 13.41.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5d815376-9b89-08fa-9434-6fa7e8e67bcf.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=909e3c9712d96c469df4)
Synology Cloud Syncでファイル破損エラーが出たので対応

# 概要

Synology Cloud Syncで同期したファイルをクラウド側で開くとファイル破損エラーが出たので、状況確認と対応してみた際のメモ

# 環境

- Synology DS216j
- Cloud Sync
- Synology Drive

Cloud Syncを使用して複数のクラウドにあるファイルをNasへと集約してSynology DriveでPCから操作する構成
※構成については他にもいいやり方があると思うが、とりあえずNASが故障してもクラウドでファイルを取り出せるし、同期アプリを一つで済ますための構成
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70171/5f581df5-e28f-bb7d-c3b1-7632d87943b1.jpeg)
# 問題発生

NASのファイル容量が逼迫してたため、構成の見直し中にクラウド側からファイルを開くとファイル破損エラーが発生していた。
※ただ、NASやPCからはファイルが開けた
![スライド2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70171/8896cb28-e081-6617-f3a2-001381a9dbae.jpeg)
# 状況確認

| 状況                                                         | ファイルを開けるか |
| ------------------------------------------------------------ | ------------------ |
| クラウドからファイルを開く(NAS→クラウド)<br />まれに開けるファイルがある(元々クラウドにあるファイルで変更履歴無) | ×                  |
| NASで開く                                                    | 〇                 |
| PCで開く                                                     | 〇                 |
| PCからクラウドへアップロードしてクラウドで開く(NAS→PC→クラウド) | 〇                 |
| クラウドからダウンロードして開く                             | ×                  |
| クラウドからダウンロードしたファイルをPCからNASへアップロード(クラウド→PC→NAS) | ×                  |
| クラウドからダウンロードしたファイルをPCからクラウドの別の場所へアップロード(クラウド→PC→クラウド) | ×                  |

上記からNASからクラウドへアップロードした場合のみファイルが破損エラーが発生している。

# 問題の原因

Cloud Syncでクラウドと連携する際に、何も考えずとりあえず暗号化しておけばいいだろうと安易に設定していたため、注意書きをよく確認していない結果起きたことが判明

暗号化キーを設定するとCloud Syncでしか開くことができないとのことでした。

![スクリーンショット 2021-05-18 155036.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70171/12ca16a8-850c-ea27-85f0-ff124015a6c0.jpeg)

# 問題の解決

Cloud Syncの暗号化キーの解除は途中で変更できないため、リンクを解除して、再度リンクの設定が必要となる。
※NASが故障した際でも問題なくファイルを利用するための構築なのにNASでしかできないのは問題がある為、暗号化を解除する
※暗号化設定した際に暗号化キーを入手できるが、即時対応できないため

1. NAS内のデータを退避してクラウド側を空にする
2. Cloud Syncのリンクを解除
3. Cloud Syncのリンク設定(暗号化キー無)
4. 退避したデータをそれぞれのクラウドに合わせて配置する



注意点

- NAS内のデータを残したまま再連携した際の注意点

  - クラウド上に残っている破損エラー判定のファイルがNASに同期される

  - 暗号化キーを設定していないため、再同期されたファイルはNASで開くことができない

  - 残したままでも退避したデータを上書きでき、ファイルを開けるが、避ける

        - NAS上で開ける正常なファイルなので問題がないように考えられたが、1万以上のファイルを上書きしてもCloud Syncログを確認すると、アップロードされたファイルが数件しかなかった

        - 破損ファイルのみアップロードされたのかどうか不明で個別のファイルを確認できない

        - 結果、新規でアップロードした方が確実に行えるし、再リンク時の同期時間が短縮できる

# 備考

元々のストレージ容量不足解消には実ファイル容量が1TBになのに3.3TBしようとなっていたため

Synology Drive Adminの「チームフォルダ」の「バージョン管理」でバージョン管理数の削減とバージョンを定期的に回転する日数を設定することで、削減可能です。

※初期はバージョンが32で古くなっても削除されないため、永遠と容量を使用していたようです。

※バージョンの削減はマニュアル上毎日00:00に実行するとなっているが、それでも即時削減されず、数日をかけて削減する。

※一気に削除したい場合はSynology Driveをアンインストール後に、再インストールが必要となる。

※アンインストールしてもバージョン履歴等が削除されて、ファイルは残る。(念の為にバックアップはとったほうがいい)
Ruby FizzBuzzプログラム　

#FizzBuzzプログラム
FizzBuzz（フィズ・バズ）は、
3で割り切れる数値を引数に渡すと、「Fizz」を返す
5で割り切れる数値を引数に渡すと、「Buzz」を返す
3と5の両方で割り切れる数値を引数に渡すと、「FizzBuzz」を返す
それ以外の数値は、その数値を文字列に変えて返す

引数として渡す値は、getsメソッドを使ってキーボードから入力できるように。

```csharp:fizz_buzz.rb
def fizz_buzz(number)
  if number % 15 == 0
    "FizzBuzz"
  elsif number % 3 == 0
    "Fizz"
  elsif number % 5 == 0
    "Buzz"
  else
    number.to_s
  end
end

puts "数字を入力してください。"

input = gets.to_i

puts '結果は...'
puts fizz_buzz(input)
```

#作り方
**①メソッドを作る fizz_buzzという動作を作る。**
数値を入れて検証を行うので、numberという引数をセットしておく。

```csharp
def fizz_buzz(number)
  # ここにFizzBuzzプログラムの処理を書いてください
end
```

**②次に処理の内容を作っていく。（def_endの間のこと）**

```
if number % 15 == 0
    "FizzBuzz"
  elsif number % 3 == 0
    "Fizz"
  elsif number % 5 == 0
    "Buzz"
```
今回は3と5での倍数に対してのアクションを起こす問題。
3つに分解しよう。

**【3と5の両方で割り切れる数値を渡すと、「FizzBuzz」を返す】**
　3と5の公倍数、2つ以上の整数に共通な倍数。最小公倍数は15。
　⇒ユーザーから数字が15で割り切れたら"FizzBuzz"となる。
　　**だから引数（number)を％15で割ったとき==0　あまり0ならturu**

**【3で割り切れる数値を引数に渡すと、「Fizz」を返す】**
  ⇒ユーザーから数字が3で割り切れたら"Fizz"となる。
  **だから引数（number)を％3で割ったとき==0　あまり0ならturu**

**【5で割り切れる数値を引数に渡すと、「Buzz」を返す】**
  ⇒ユーザーから数字が5で割り切れたら"Fizz"となる。
  **だから引数（number)を％5で割ったとき==0　あまり0ならturu**

これらのように分解して考え、余りが出るか出ないかで設定する。ここがこの問題の発想に必要なところと考える。

**上記以外の場合は、そのまま数字を返すを作る。**

```
  else
    number.to_s
```
elseはifの内容でそぐわない場合に対象となる。今回はto_sと記載し、整数を文字列型へ変更するものである。


**ユーザーからの数字を受け取る準備をする**

```csharp
puts "数字を入力してください。"

input = gets.to_i 

puts "結果は..."
puts fizz_buzz(input)
```

・input = gets.to_i　⇒相手に数字を問うことができる（gets)また整数に変換しておく(to_i)

・ puts fizz_buzz(input)　⇒メソッドにgets.to_iで得られた数字を渡して、fizz_buzzメソッドの判定を受け、putsで表記する

#まとめ
いきなり作成してくださいと言われてもイメージがつかなかった。ただ1つずつ分解して考えていくと簡単！困難は分割せよ！ですね。