OmniAICreator/Qiita-1.07M huggingface dataset:
Cache-ControlヘッダーとPragmaヘッダー概要 メモ


* キャッシュ制御用のHTTPヘッダーであるCache-ControlとPragmaについて概要をメモする。

## Cache-Control

* HTTPヘッダーの一つ。
* キャッシュをブラウザやプロキシ、CDNなどで制御するためのもの。
* HTTPレスポンスに記述することで、Webアプリからブラウザに送るコンテンツが、途中の経路上でむやみにキャッシュ記録されたり再利用されたりしないように指示する。

### 設定値

#### `private`

* 特定ユーザーのみが利用できるようにコンテンツをキャッシュさせる。
* 中間キャッシュには格納させない。ブラウザ〇、CDN×

#### `no-store`

* 返却されたコンテンツに依らず、ブラウザやすべての中間サーバーにそのコンテンツを保存させない。

#### `no-cache`

* 一度キャッシュに保存されたコンテンツはオリジンサーバーへの確認無しにキャッシュを利用させない。

#### `must-revalidate`

* キャッシュに保存されているコンテンツが現在も有効であるか否かをWebサーバに必ず確認させる。

## Pragma

* HTTP/1.1のCache-Controlヘッダーを解さない場合に備えて利用するキャッシュ制御ヘッダー。

  ```
  Pragma: no-cache
  ```

## キャッシュを制御する目的

* 機微な情報を意図せぬ形でキャッシュからレスポンスされてしまうといった事故を防ぐため。

  ![cache.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/cc0b95f4-c177-e795-67bd-6f19b2de8af7.png)


## 参考情報

* [Cache-Control](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control)
* [プロキシキャッシュ対策](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/405.html)
React-Route-Dom の使い方

## React で画面遷移
Reactで画面遷移をさせるのに想像以上に時間がかかったので
単純化したものを備忘録的に記録

### 準備
Typescript 版の React プロジェクト作成
react-router-dom のインストール
```bash
  % npx create-react-app page-sample --template typescript
  % cd page-sample
  % npm install react-router-dom @types/react-router-dom
```
### コード

index.tsx, App.tsx の２ファイルのみのコード記述で動くようにしました。

#### index.tsx
`<App />` を `<BrowserRouter>`で括る。
```react:index.tsx
import ReactDOM from "react-dom";
import App from "./App";
import {BrowserRouter} from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter> ,
  document.getElementById("root")
);
```

#### App.tsx

App.tsxは以下のコードに置き換える。
遷移先ページを別ファイルにするのが普通だが単純化してApp.tsx内に定義

```react:App.tsx
import { Routes, Route, Link } from "react-router-dom";

const Home  = () => { return (<h2>Home </h2>) }
const Page1 = () => { return (<h2>Page1</h2>) }
const Page2 = () => { return (<h2>Page2</h2>) }
const NotPG = () => { return (<h2>Error</h2>) }

function App() {
  return (
    <div className="App">
      <h1> Page Sample </h1>
        <Link to="/">      [  Home  ] </Link> <br/>
        <Link to="/page1"> [ Page 1 ] </Link> <br/>
        <Link to="/page2"> [ Page 2 ] </Link> <br/>
      <Routes>
        <Route index         element={<Home/>}/>
        <Route path="/page1" element={<Page1/>}/>
        <Route path="/page2" element={<Page2/>}/>
        <Route path="*"      element={<NotPG/>}/>
     </Routes>
    </div>
  );
}

export default App;
```

### 最後に
最も単純化したものを備忘録として記録しました。
忘れた時はまずここからですね。
Python3: Cloud Firestore のデータを ndjson に変換

こちらを参考にしました。
[コレクションのすべてのドキュメントを取得する](https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#get_all_documents_in_a_collection)

```py:get_firestore_docs.py
#! /usr/bin/python
#
#	get_firestore_docs.py
#
#						Apr/02/2022
# ------------------------------------------------------------------
import sys
import json
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
from datetime import date, datetime
# ------------------------------------------------------------------
def json_serial(obj):
	# 日付型の場合には、文字列に変換します
	if isinstance(obj, (datetime, date)):
		return obj.isoformat()
	# 上記以外はサポート対象外.
	raise TypeError ("Type %s not serializable" % type(obj))
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")

project_id='project-apr02-2022'
collection_in='sample_collection'

cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred, {
	'projectId': project_id
})

db = firestore.client()

docs = db.collection(collection_in).stream()

for doc in docs:
	json_str=json.dumps(doc.to_dict(),default=json_serial)
	print(json_str)

sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

実行スクリプト

```bash
export GOOGLE_APPLICATION_CREDENTIALS="key-file-apr02.json"
./get_firestore_docs.py
```
【Android/Kotlin】モーションセンサーを検知する

# モーションセンサーとは
ほとんどAndoridには加速度計やジャイロスコープが搭載されていて、そこで検知した内容のことを総称してモーションセンサーと言います。
端末の傾き、振動、回転、地磁気センサーなど端末周りで検知している情報をとっています。
今回はこれを検知する方法を書きます。

# SensorEvent
すべてのモーションデータは多次元配列でそれぞれ取得できます。
すべてSensorEventパラメータとfloat型の値をセットで送ってくれますので、同時に全部取得して使うものだけ取り出すということが可能です。

# サポートされているモーションセンサー

## TYPE_ACCELEROMETER
x, y, z軸の加速力

## TYPE_ACCELEROMETER_UNCALIBRATED
x, y, z軸の測定加速度 

## TYPE_GRAVITY
x, y, z軸の重力

## TYPE_GYROSCOPE
x, y, z軸の回転速度

## TYPE_GYROSCOPE_UNCALIBRATED
x, y, z軸の回転速度（ドリフト補償なし）とドリフト

## TYPE_LINEAR_ACCELERATION
x, y, z軸の加速力（重量を除く）

## TYPE_ROTATION_VECTOR
x, y, z軸の回転ベクトル成分（x * sin(θ/2)）, 回転ベクトルのスカラー成分（(cos(θ/2)）

## TYPE_STEP_COUNTER
最終再起動時以降、センサーがアクティベートされている間に、ユーザーが歩いた歩数。

# よく使われるのは
回転ベクトルセンサーと重力センサーです。
動作検知の対象として回転ベクトルを多く使うので、そこから色々な目的で検知をして利用していくみたいです。
重力センサーは端末の動きの方向性をとりたい場合に有効で使われているようです。

# 回転ベクトルセンサー取得してみた

```kotlin
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
rotationVector = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
```
上記のコードで回転ベクトルセンサーが取得できます。
多次元配列で
SensorEvent.values[0]にはx軸のデータ、
SensorEvent.values[1]にはy軸のデータ、
SensorEvent.values[2]にはz軸のデータがありますのでそれぞれ取得。
SensorEvent.values[3]には回転ベクトルのスカラー成分があるので取得。

それぞれ取得した結果を以下のように画面表示。

![Screenshot_2022-04-02-13-40-43-09_03acab2a3b1d4696a37e4ffa0cff7f8a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/63860d34-57f3-34d2-15a5-73c7c870afac.png)

以上になります。

# 参考

https://developer.android.com/guide/topics/sensors/sensors_motion?hl=ja
探索アルゴリズムと基本的なデータ構造

前回の記事でアルゴリズムってどんなものなのか、という基本的な部分をまとめました。
今回は実際にどんなアルゴリズムやデータ構造があるのかを見ていこうとおもいます。

前回の記事はこちらになります。

https://qiita.com/wangqijiangjun/items/9e0a5dfbe289f6eb3859

## 探索アルゴリズム
### 線形探索
代表的な検索アルゴリズムに線形探索があります。
このアルゴリズムはどんなときに使われるかというと、例えばこんな例を考えてみましょう。
```
7, 4, 9, 10 , 2, 3, 8, 5, 11
```
上記の数字から一番小さい数字を見つけてください、という探索アルゴリズムを考えるとしましょう。
線形探索というのは左から1個1個みていく、という至って超シンプルなアルゴリズムです。
つまり、7と4を比較して4のほうが小さい。4と9を比較して4の方が小さい。…というふうにやっていくのが線形探索です。
ですが、このデータ数が増えていくとかなり処理に時間がかかってしまいますよね。
そこで登場するのが二分探索(バイナリーサーチ)なんです。

### 二分探索(バイナリーサーチ)
```
7, 4, 9, 10 , 2, 3, 8, 5, 11
```
先ほどの数字を使って考えてみましょう。今度は5より大きい数字を見つけてください、というアルゴリズムを作成しようと考えます。
そのときに使えるのが、二分探索(バイナリーサーチ)です。
二分探索(バイナリーサーチ)とは、名前の通り、2つに分けて探索することを言います。
今回の例でいえば、まず数字を昇順もしくは降順で並び替えます。そして、ちょうど真ん中で区切るようにするんです。
例と数字は違いますが、イメージは以下の画像のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/0d9dee4c-27cc-2492-97fa-c9fb518519af.png)
※https://ufcpp.net/study/dotnet/bcl_collection_algorithm.html から引用しています。
このように中央の値から考えて探したい値が大きいか小さいかを考えるんです。今回の例でいえば、
```
2, 3, 4, 5, 7, 8, 9, 10, 11
```
このように昇順で並び替え、中央値である7を基準にして解決したい内容を考えるんです。
5より大きい数字が欲しければ、中央値の7の隣にいるのが５ですから、それ以降を抽出してあげればいいということになります。
ですが、この探索方法はソートされてないデータを扱うことができないのが、この探索方法のデメリットです。
## データ構造
これまで探索アルゴリズムを見てきましたが、より効率よいアルゴリズムを組むためにはデータをどう格納するべきか、という観点も大事になってきます。データをどう格納するべきかというのをデータ構造といいます。
このデータ構造の基本が木構造というものです。
### 木構造
木構造とは以下の画像のことを言います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/abba01c3-0b52-6eb8-8b9b-36feebaeaa3e.png)
※https://e-words.jp/w/%E6%9C%A8%E6%A7%8B%E9%80%A0.html より引用
丸の部分をノードといい、頂点にあるノードをルートノード、子ノードを持たないノードをリーフノードといいます。
木構造には多くの種類があって、子ノードの数が2つに制限されているものを**二分木**といい、それ以上の数に制限されているものを、**N分木**といいます。
### 二分木探索
二分木のうち、左側のノードの値が必ず親よりも小さく、右側のノードの値が必ず親よりも大きくなっているものを**二分木探索**といいます。
具体的にいうと、以下の画像のようなものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/ba234f4e-886a-de67-ff53-ef0f551ba903.png)
https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2%E6%9C%A8 参照
二分木探索で例えば、7をみつけたいとします。まず初めに、8から始めて、7の方が8よりも小さいので、左側である3に行きます。その次に、3と7を比較して、7の方が大きいので右に行って...というふうにしていくことで欲しいデータが取れるようになるんです。
これのメリットは今回でいえば、最大処理回数が4回で済むということです。つまり、効率よくデータを探すことができるようになるんです。
### AVL木
木構造を考えるときに注意すべきポイントは、木の高さを一定にすることが大事です。
例えば以下の木構造のような場合を考えてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/5a44e496-8862-cde5-b82c-bc519b69ac93.png)
※https://www.google.com/url?sa=i&url=https%3A%2F%2Fatmarkit.itmedia.co.jp%2Ffcoding%2Farticles%2Fdelphi%2F03%2Fdelphi03a.html&psig=AOvVaw28G28jEJIFsYHppqdcuRuK&ust=1648953247275000&source=images&cd=vfe&ved=2ahUKEwiv7drVq_T2AhVAwIsBHXt9BrkQr4kDegUIARCzAQ より引用
こうすると、最大処理回数が、欲しい値によって変わってしまうということがあります。
そこ
### B木
B木とは1つのノードが3つ以上のノードを持っていて、木の高さが一定である木のことを言います。
図解するのがむずかしかったので、[こちら](https://atmarkit.itmedia.co.jp/fcoding/articles/delphi/05/delphi05a.html)の記事が参考になりますので、こちらを参考にしてみてください。

ちなみにぼくらが何気なく使っているドメインも実は木構造のデータ構造をしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/6dea1d31-18ad-747f-fa27-7b2008e934d1.png)

以上です。何か間違いがございましたら、ご教示いただけますと幸いです。
Node.js 16 on Ubuntu 20.04 セットアップ

Ubuntu 20.04 LTS の標準では Node.js バージョン 10 がインストールされるため、
公式のレポジトリから個別に最新安定版の 16 をインストールする方法

## 参考ページ
Node.js 公式サイト
https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions

Binary for Debian/Ubuntu (公式からリンクあり)
https://github.com/nodesource/distributions/blob/master/README.md

## インストールバージョン
Ubuntu 20.04 LTS 
Node.js 16.14.2 LTS

:::note info
動作確認は、Docker上のUbuntuで行なっています
:::

## Ubuntu インストール
(省略)

## Node.js インストール
レポジトリ追加コマンド(Using Debian, as root)を発行
```shell:/bin/bash
curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
```

パッケージをインストール
```shell:/bin/bash
apt update && apt install -y nodejs
```

## 動作確認
任意の場所にエディタで ```hello.js``` を作成
```hello.js
console.log("hello world!");
```

nodeで実行
hello world!と表示されればインストール成功
```shell:/bin/bash
node hello.js
```

## 備考
インストールで使用したコマンドを ```Dockerfile``` に ```RUN``` コマンドで指定すれば、
Dockerイメージに含めてビルドすることが可能
EC2 インスタンスを削除し、新しく作ったインスタンスにログインした時にでたエラー

# はじめに
今回、本番環境にデプロイしようとEC2に色々インストールした結果環境が汚れたので、インスタンスを初期化して新たに作ろうと考えた。

コンソール画面のEC2にて、動いていたインスタンスを削除し、全く同じ設定で新たにインスタンスを作成した。
EC2にログインしようとした結果、エラーが出てすぐに解決できたがキータの記事が見当たらなかったため簡単に記載致します。

# 前提
- EC2
- ElasticIP（関連つけている）

# 起きたエラー
はじめに書いた通り、新たに作成したEC2にログインしようとした結果こんなエラーが出た。

```
$ ssh -i [pem名].pem ec2-user@[ElasticIP]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:ハッシュ〜〜〜〜〜〜〜〜〜〜〜〜.
Please contact your system administrator.
Add correct host key in /Users/〜〜〜〜/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /Users/〜〜〜〜/.ssh/known_hosts:11
Host key for [ElasticIP] has changed and you have requested strict checking.
Host key verification failed.
```
本番環境でこういうのでると焦りますよね。
とりあえず、エラーを全部コピーして、翻訳。
>警告: リモートホストの識別が変更されました!
>
>誰かが何か悪さをしている可能性があります!
>今まさに盗聴されているかもしれない（中間者攻撃）!
>ホスト鍵が変更されただけかもしれません。
>リモートホストから送信された ED25519 鍵のフィンガープリントは次のとおりです。
>SHA256:ハッシュ〜〜〜〜〜〜〜〜〜〜〜〜.
>システム管理者にお問い合わせください。
>正しいホストキーを/Users/~~~~~~/.ssh/known_hostsに追加すると、このメッセージは消えます。
>/Users/~~~~~~/.ssh/known_hosts:11にある問題のあるED25519 key
>[ElasticIP] のホスト鍵が変更されたので、厳密なチェックを要求しています。
>ホスト鍵の検証に失敗しました。

以前のホストキーと違うため、エラーが出ている。
>/Users/~~~~~~/.ssh/known_hosts

ここに。known_hostsファイルから[ElasticIP]に関する情報を取り除けば、以前のホスト鍵も削除される。
※[ElasticIP]設定していない方は、前に削除したEC2のパブリック IPv4 アドレスが記載されています。

# 解決方法
**ssh-keygenコマンド**を使用します。
ssh-keygenコマンドとは、秘密鍵と公開鍵の鍵ペアを作成できるコマンドです。
ssh-keygenコマンドにオプション**-R**をつけると、
指定したホストに属する鍵を全て取り除く事ができます。
そちらを使用いたします。

```
$ ssh-keygen -R [ElasticIP]
# Host [ElasticIP] found: line 11
/Users/~~~~~/.ssh/known_hosts updated.
Original contents retained as /Users/~~~~~~/.ssh/known_hosts.old
```
上記コマンドを打った後、
```
$ ssh -i [pem名].pem ec2-user@[ElasticIP]
The authenticity of host '[ElasticIP] ([ElasticIP])' can't be established.
ED25519 key fingerprint is SHA256:ハッシュ〜〜〜〜〜〜〜.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? 
＄ yes
```
yesと打てばログインできます。

以上。
デプロイ中に出たgemファイルに関してのエラー解決手順

アプリケーションをデプロイ中にエラーが出たので
その際の解決手順を備忘録として載せておきます。

デプロイ中に下記エラーが発生。
remote: ! Failed to install gems via Bundler.


### 解決手順
①VSコードから Gemfile.lock を削除
②ターミナルにて bundle install　を実行
③変更差分をmasterブランチにcommit/push
④ターミナルにて git push heroku master　を実行

上記実行で無事デプロイすることができました。
windowsとLinuxとでvimrcを共有する


こんな感じでOSの種類を見て分岐させる

```vimscript
if has('wsl')
	" wsl2
	let g:clipboard = {
			\   'name': 'myClipboard',
			\   'copy': {
			\      '+': 'win32yank.exe -i',
			\      '*': 'win32yank.exe -i',
			\    },
			\   'paste': {
			\      '+': 'win32yank.exe -o',
			\      '*': 'win32yank.exe -o',
			\   },
			\   'cache_enabled': 1,
			\ }
else
	" Linux
	set clipboard&
	set clipboard^=unnamedplus
endif
```
pythonにいろんな計算をさせる

## 初めに
pythonで変数に数字を代入して計算してみる
## ゴール
計算結果が出力されること
## 実践
```python:式
>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06
```
_は直前の結果を覚えている。便利

#### 2022/04/03 コメント欄でアドバイスいただいたので追記
_が前回の結果を覚えているのは対話型インタプリタのときだけで、
スクリプトファイル実行時には覚えていないので注意が必要。
Unity講座をアップデートしたって話　セールもあるよ。

どうもオオモリちゃんです。　

本日、Unity講座をアップデートしたのでそのお知らせです。


[Unity3DゲームStandard講座Vol2](https://omoritakuya.teachable.com/p/unity3dgame2-0?coupon_code=80SALE04)

現在、アップデート記念として80%OFFセールをやっています。　
こちらのセールは、4月14日までとなりますので。　

アップデート版では、2021.2の機能やアーティスト向けの機能を
中心に解説しました。　
更に新しくUnityファミリーに加わった、PlasticSCMの解説もしています。　

PlasticSCMをちゃんと解説している講座はまだ殆どないので　
これだけでも見る価値があると思います。　
【GitHubを使っている人も、Plasticに移行する人が増えるでしょうね】

VOL1と合わせると、なんと合計で約9時間あります。　
見るだけでも大変でしょうが。　これ一本見れば　
とりあえず基礎はマスター出来ると思います。

興味があれば、ぜひ御覧ください。








ＰＳ

そういえばゲーミングサービスも6月から正式リリース
されますよね。　

そっちの解説も入れたかったので
Unity講座は　
今年中にはもう一回　アップデートするかもしれません。

そのうち、VR講座も出す予定です。
Excelの各行をAPIで処理する


Excelの各行をAPIで処理するVBAスクリプトの作り方です。
A列のデータをAPIで処理してB列に書き込む簡単なスクリプトです。

# JsonConverterのセットアップ
JsonConverterをインストールします。
https://github.com/VBA-tools/VBA-JSON#installation

# Excelにボタンをつくる
適当な図形を作成し、右クリックで「マクロを登録」を選択します。

# VBAスクリプト
マクロを新規作成し、以下のVBAスクリプトを書きます。
URLやヘッダー、対象とする行数などを修正してください。
C列の日時が入っている行は再実行されないようにしています。

```
Sub API実行_Click()
    Dim i
    
    i = 2
    Do While i <= 501
        Dim s As String
        Dim dt As String
        
        Application.StatusBar = "A" & i & "を処理しています...(" & Int(i / 501 * 100) & "%)"
        
        dt = Range("C" & i).Value
        s = Range("A" & i).Value
        If dt = "" And s <> "" Then
            Dim req As Object
            Dim url As String
            Dim json As String
            Dim JsonResult As Object
            Dim JsonRequest As Object

            Set JsonRequest = New Dictionary
            JsonRequest.Add "positive", s
            
            url = "https://..."
        
            Set req = CreateObject("MSXML2.XMLHTTP")
            req.Open "POST", url, False
            req.SetRequestHeader "Content-Type", "application/json; charset=UTF-8"
            req.send JsonConverter.ConvertToJson(JsonRequest)
            json = objHttpReq.responseText
            Set JsonResult = JsonConverter.ParseJson(json)
            
            Range("B" & i).Value = JsonResult("result")
            Range("C" & i).Value = Now
        End If
        i = i + 1
    Loop
        
    Application.StatusBar = ""
End Sub
```

おしまい。
[android]TextViewの中のURLを動的にリンク化する方法

textViewの属性に
android:autoLink = "web" を追加するだけ。
このautolink属性は便利で
= の値を　"address"　"email" だったりにも設定できます

ですがどんな場合にも使えるわけではなく、例えばurlの後ろにスペースが空いてなければその後の文字列も全部リンク化されてしまいます。
その場合にはこちらの記事が参考になります
https://android.gcreate.jp/482/


autoLink公式ドキュメント
https://developer.android.com/reference/android/widget/TextView#attr_android:autoLink
エイプリールフールに使える、おかしな .htaccess 設定

# フェイクなファイルを作ってみましょう。

一例をあげていますが、どのような組み合わせでも可能です。

## PDFに偽装したHTML

.htaccess を指定するほどでもありません。

```shell
mkdir sample.pdf
cd sample.pdf
touch index.html
```

## jpg に偽装した HTML

perl のCGIスクリプトの例です。
```.htaccess
RewriteCond %{QUERY_STRING} (^|&)hash=([^&]+)($|&)
RewriteRule .* - [E=X_QUERY_HASH:%2]

RewriteRule ^(.+)\.jpg$ index.cgi?u=$1&mode=html&hash=%{ENV:X_QUERY_HASH}
RewriteRule ^(.+)\.JPG$ index.cgi?u=$1&mode=jpg&hash=%{ENV:X_QUERY_HASH}
```

小文字の拡張子 jpg は HTML生成用を仮定しています。
大文字のJPGは、動的なJPEG画像の生成を仮定しています。
もし、普通のファイルのJPEGを表示する場合、
拡張子 .jpeg を指定します。

もしくは、他の形式のファイル (.png .gif .webp等）を検討します。
Microsoft PowerBI Desktopで、データごとのグラフ表示（スライサーの使い方）

記載日：2022/4/2
PowerBI Desktopで、選択した年度ごとのデータをグラフ表示したかったため、方法を調べました。
その中で、”スライサー”という機能があると知ったので、備忘録として記載します。

#### 目標
あるデータに絞ってグラフを表示する。
例えば、以下のように、選択した年のデータを表示したい場合。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/9aa11b71-623c-8ea4-5ef0-cccd3ccbc76d.png" width=100%>

企業テーブル用データ
~~~
Name,Country,StartedAt
TAYATO,Japan,1890
HANDO,Japan,1921
VLOV,Sweden,1834
Melsdez,Germany,1836
Tasle,USA,2005
Valkswegon,Germany,1839
GanarelMotors,USA,1838
~~~

売上テーブル用データ
~~~
Year,Company,Sales,Quantity
2019,TAYATO,302256,1005
2020,TAYATO,299299,929
2021,TAYATO,232670,727
2019,HANDO,149310,430
2020,HANDO,131705,375
2021,HANDO,106770,307
2019,VLOV,33906,61
2020,VLOV,32718,60
2021,VLOV,36133,63
2019,Melsdez,130705,305
2020,Melsdez,197070,500
2021,Melsdez,217305,670
2019,Tasle,26750,59
2020,Tasle,32892,71
2021,Tasle,61660,116
2019,Valkswegon,252632,823
2020,Valkswegon,287075,889
2021,Valkswegon,168777,487
2019,GanarelMotors,150780,457
2020,GanarelMotors,128425,245
2021,GanarelMotors,145700,350
~~~

#### 作業
０．グラフ作成
　まず表示したいグラフを作成し、表示させる。
　なお、スライサーを先に用意し、そのあとグラフを作成しても良い。（順番は気にしなくて大丈夫。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/a21cc1b4-8b8a-96bc-2d67-67f88b0b295c.png" width=100%>

１．スライサーの使用
　グラフの中の”スライサー”を選び、設置する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/2cd6aa9b-85bf-efcb-54ea-97d380a8f06f.png" width=30%>

　スライサーの”視覚化”の”フィールド”欄に、分割表示させたいカラムを設定する。
　例えば、上記データのYearカラムを設定すると、以下のようになる。
　表示形式は”一覧”などがあるので、好きなものを選ぶ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/d7e7a874-a444-cf4b-7a58-75a713463e10.png" width=60%>

　表示形式によって”視覚化”の”スライサーの設定”ができるものもあるので、ここを変更することでより自分好みの表示へ変更することもできる。
　例えば、一括操作用のチェックボックスが欲しい場合は、”[すべての選択]オプションを表示する”を有効にする。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/20d41eec-f60a-5065-0030-53d395310df0.png" width=20%>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/2f5ef373-5a75-516d-58bf-a5fed7491012.png" width=20%>

２．補足：ページをまたいでスライサーを有効化
　複数のページを作成した場合、各ページのグラフを１つのスライサーで制御したい場合がある。
　この場合は、”書式”タブの”スライサーの同期”を使う。右側に設定欄が表示されるので、そこで調整する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/354fb6e1-89c6-76be-d461-5f33262b2ae6.png" width=100%>

#### 参考にさせて頂いたサイト
・スライサーについて
https://www.katalog.tokyo/?p=3067
初心者　flutter×ブロックチェーンに挑戦する　2

# クリック二回、の裏に隠されていたもの

## まずは土台づくり

　きのうは記事の完コピで、しかもRemixというなんだかとても便利なものを使ったから、いわばただの作業だったわけだけれど、今日からはもうちょっと頭を使って悩んでいくことにする。
　といったって、まずはUNCHAINの作業をおさらいするだけなのだけれど、いったん違う視点を手に入れたことで、UNCHAINで黙々と繰り返してきたことに新しい理解が生まれるんじゃなかろうか、と、ちょっと期待する。

　terminalで

- mkdir Voting-dApp
- cd Voting-dApp
- mkdir my-voting-dapp
- cd my-voting-dapp
- npm init -y
- npm install --save-dev hardhat
- npx hardhat
- npx hardhat accounts
- npx hardhat compile
- npx hardhat test

　この辺までが、きのうRemixが一回目のクリック以前にやってくれていたことかな。つまりRemixの裏側にはhardhatに相当するものが隠れていたということだろう。
　自動的に作成されたたくさんのファイルのうち、要らないものをちょっと整理して、メインのsol fileをつくる。そしてようやく４つの関数を書き込む。

　ここからはVSCodeで作業。記事を写しただけのファイルではエラーが出る。新しく加えるのは

- // SPDX-License-Indentifier: UNLICENCED
- import "hardhat/console.sol";

　１行目は、コメントアウトされているのに機能する不思議な呪文だ。SPDXライセンス識別子というらしいが、具体的に何をしているのかよくわからない。が、それはなぜ朝にはおはようというの、というぐらいの疑問だと判断して先に行く。
　２行目はもうちょとわかりやすい。

- scripts 内に　run.js　をつくる。

　これはlocal環境でテストするためのファイルだ。

　まずはUNCHAINの第一課題で使った一番シンプルなrun.jsをコピーして、しげしげと読む。当然のことながら、まずは「名前」の違ってるところを差し替える。差し替えるだけで意味をなすかどうかまだわからないけれども、そうする以外に手立てがない。実はきのうのRemixに類似のファイルができてないか探してみたけれど、見つからなかった。だからあとは、トライ＆エラーで突破するしかない。ところが

Compiling 1 file with 0.8.4
Solidity compilation finished successfully
Here is my first smart contract!
WavePortal address: 0x....

あら、できた。

　でも、これはぬか喜びでしょうね。ここから関数に合わせた各種書き換えが出てきます。
　先は長いよ〜、でも今日はここまで。

## 次回以降、理解もせずに書き殴ってきたコードに逆襲されることでしょう

　前の記事はこちら

https://qiita.com/dongri727/items/54c6c9d91246233f5473

　続きはこちら

https://qiita.com/dongri727/items/1ee036f9e1352f17aa8a
Ubuntu Server 20.04.3 LTS MySQL操作#1 データの検索

実施日:2022/03/12～2022/04/02

【今回の内容】
[MYSQLのテーブル作成～データの追加](https://qiita.com/art22/items/b3479683d87f5b9d78b7)で作成したデータを検索する。
作成したデータは以下の通り
+--------+--------+
|id 　　 +result　|
+--------|--------|
|5900 　 |123 　 |
|5901 　 |234 　 |
|5902 　 |345 　 |
|5903 　 |456 　 |
|5904 　 |567 　 |
|5905 　 |678 　 |
|5906 　 |781 　 |
+--------+--------+

# データの検索方法

```
select * from テーブル名 where カラム名 = データ ;
```

# データの部分検索

```
select * from テーブル名 where カラム名 like '%データの一部分%' ;
```
※データの一部分から検索したい場合はLIKEを使用します。
'%データの一部分%'⇒データの一部分が該当するものがあるものを抽出する。
'%データの一部分' ⇒データの一部分が末尾に含まれているものを抽出する。
'データの一部分%' ⇒データの一部分が先頭に含まれているものを抽出する。
NOTをLIKEの前につけた場合は上記を否定した抽出が可能となります。

# 今回実現したいこと
1.resultのデータ(1～8)のそれぞれ何回表示されているかをカウントしたい。
2.1.実行処理を数字ごとに都度、コマンドを書かないで実行したい。
3.1.でカウントしたものを上位3つ迄を表示したい。

# 1.resultのデータ(1～8)のそれぞれ何回表示されているかをカウントしたい。
```
select count(result) from データベース名 where result like '%1%' ;
結果:2

又は
正規表現を使用してカウントする場合
select count(result) from テーブル名 where result regexp '[1]';
※正規表現でカウントした方が条件が多様であるため、正規表現のほうが使い勝手がいいと思いました。
```
残りの2～8をAの箇所に入れ替えれば同様にカウント出来ます。

# 2.実行処理を数字ごとに都度、コマンドを書かないで実行したい。
MySQL上でも変数を使用して、代入が出来ることが可能のようです。

```
変数の部分を下記のように設定しました。
set @no := 1 ;

select count(result) from テーブル名 where result like '%@no%' ;
結果は0でした。
予想であれば、2が返ってくるはずですが0で返って来てしまいました。
select文が変数に対して、何を返してきているのか検証します。
```

# 検証
調べてみると、上記のselec文は最初にfrom句以下を判断しているようです。
となると、テーブルにあるカラムの【1】を見て、カウントしているはずが、
判定が【0】で返ってきます。

可能性①
likeは変数の部分一致検索が出来ない？
⇒concatを使用すると、部分一致の検索が可能
【like '% '@no%' ;】を【like concat('%',@no,'%') ;】とする。

どうやらlikeは変数と%や_を纏めて認識しないようなので
concatを使用して、変数と%や_がそれぞれの文字として独立しているため
合体させる必要があるようです。

変数の取り扱いについては解決したので、今度はこの変数を1～8の数字を
自動で代入して、それぞれの出現回数を調べられるようにします。

## 変数に自動で代入する
上記で作成したselect文に自動処理で数字を代入して数字ごとに出現回数を集計する方法
【CREATE PROCEDURE】を使用してあらかじめSQLの命令文を作成する。
この中にSELECT文とそれを繰り返し変数に代入する処理を記載します。
```
DELIMITER //
###CREATE PROCEDUREの中に実行する;がいくつも含まれています###
###途中で実行がされないようにDELIMITER //で；を//に変更します###
CREATE PROCEDURE procedure名(in x int, in y int)
BEGIN
###BEGINからENDまでSQL命令文を一纏めにしたものであるということを意味します###
WHILE x <= y do
###WHILEで条件式を作成します。END WHILE迄が対象です###
select * from テーブル名 where result like concat('%',x,'%') ;
set x = 0 + 1 ;
END while ;
END
//
DELIMITER ;
###命令文が作成出来たので、実行処理の//を；にして元に戻します###
call procedure名(0, 8);
###0～8迄の集計結果が処理され表示されます。
```

今回はここまでです。
[Vue]元教師が教師の業務効率化のためにランダム指名アプリを作りました

## サービス紹介
座席数を指定して、生徒を指名できる、シンプルなアプリです。

https://seat-99688.web.app

https://github.com/dirtyman69/seat

## 使い方
![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706631/772926c3-d988-fb77-fd5d-26ecb1548e32.gif)

座席数を指定して、後は指名ボタンを押すだけ！


## きっかけ
きっかけは自分自身が生徒だった時に、授業を受けている際に「あれ、なんか前の席の人ばっかり当てられてるな〜」と感じたことから始まりました。
そして、実際に２年ほど教師をしていたのですが、前の席の人はやはり当てやすいのです。
![スクリーンショット 2022-03-23 20.20.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706631/f961f947-1a94-e5e7-dddb-4e2b6829c173.png)
出典
https://www.paylessimages.jp/detail.php?id=gf1420736473

一回一回の授業ではそこまで差は出ませんが、一年も続けると席が固定の場合、とても不公平になるかと思いました。
そこで、生徒をランダムに指名してくれるアプリを作りました！

このアプリを使えば不公平がないかつ、誰が当たるかわからない緊張感もある、楽しい授業がしやすいかなと感じます。

## 使用技術
* vue.js(2.0)
* Firebase
* Vuetify


## ハマったところ
Vueのコンポーネント志向を活用しようと、トップページと、列と、一つ一つのセルという三つのコンポーネントを作成しました。
![スクリーンショット 2022-03-23 20.26.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706631/120781ee-4954-cc69-d147-5fae1a5f4e77.png)

```javascript:HelloWorld.vue
    <v-row class="row py-0 px-0" v-for="i in changeHeight" :key="i" color="blue darken-2">
      <Row :mathWidth="mathWidth" :rowId="i" :propRandomId="propRandomId"/>
    </v-row>
```

```javascript:Row.vue
      <v-col
        class="pa-0" 
        justify="center" 
        v-for="i in changeWidth"
        :key="i"
      >
        <Cell :id="rowId + '_' + i" :ref="rowId+'_'+i">
        </Cell>
      </v-col>
```

縦横の席数を受け渡して全体を表示しています。
こちらの受け渡しはpropsを使用しているのですが、うまく受け渡せなかったり、予期せぬ挙動が生まれたりと、そこそこ大変でした。

storeを使うべきか、propsで良いのかという判断がなかなか難しいです。

また、ランダムに指定した席ひとつだけ赤くするという処理も、一つ一つのCellに
1_1 1_2 1_3
2_1 2_2 2_3

とインデックスを付ける処理ですが、

```javascript:Row.vue
    watch: {
      propRandomId: function () {
        
        for(let i = 0; i < this.mathWidth; i++){
          this.$refs[this.rowId+"_"+(i+1)][0].$el.children[0].style.backgroundColor="lightgray";
        }
        
        if(!this.$refs[this.propRandomId]){
          return
        }

        this.$refs[this.propRandomId][0].$el.children[0].style.backgroundColor="red";
      
      },
      
    },
```

$refsを使ってかなりむりやりやっています。
もっとスマートにかけたらよりいいのかなーとか思ったりします。

## 今後の改良点
もし反響が大きければ
* より素敵なアニメーションの実装
* 席に生徒名を設定できる機能
* 一度当たった人は除外して再抽選

あたりの機能は実装したいなと考えています。
LGTMやツイートの拡散などしていただけると本当にありがたいです、、、

https://seat-99688.web.app/

## おまけ

Twitterで一緒に個人開発してくれる人、一緒にプログラミングを勉強してくれる人を募集しています！
結構発信しているので、是非ともフォローをお願いいたします。

https://twitter.com/js_manabitai

また、開発を手厚くサポートしてくれたメンターさんには本当に頭が上がりません、。
ありがとうございました。

https://twitter.com/second_atom

サービス

https://seat-99688.web.app

参考

https://qiita.com/krpk1900/items/22963432b62a9004717c
cscの作法 その90

# 概要
cscの作法、調べてみた。
ContextMenu使ってみた。

# 参考にしたページ

https://qiita.com/guijiu/items/199950767ef41094b387

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/4b489646-5d24-478d-6253-2e7923cc9403.png)

以上。
github

まずは登録情報を確定させる。

```
git config --global user.name "ユーザー名"
git config --global user.email "メールアドレス"

```

鍵を登録

```

mkdir .ssh

#ファイルパーミッションを700（所有者のみ、読み取り、書き込み、削除が可能）に変更
chmod 700 .ssh

#authorized_keysを作成
touch .ssh/authorized_keys

#ファイルパーミッションを600（所有者のみ、読み取りおよび書き込みが可能）に変更
chmod 600 .ssh/authorized_keys

ssh-keygen -t rsa -C "メールアドレス"

```

そのあと，yesを何回か入力。
上記のファイルにcat xxxxx.pubを入力。出力された内容を以下に登録する

```
xxxxx.pub
https://github.com/settings/keys

```


そのあと、対象のディレクトリへ「cd」で移動
リポジトリ(プロジェクト)を登録後に以下のコマンドで登録

```
git init

git add .

git commit -m 'Initial Commit'
※ git commit -m 'Initial Commit'
[main 12085d6] Initial Commit
ここで、[main 12085d6] と記載されているので、push時はコマンドが変わります

git remote add origin git@github.com:ユーザー/プロジェクト

git push -u origin main

あるいは
git push -u origin master


```

```
fatal: remote origin already exists.
→上のように出力されたら下記のコマンドで削除します
git remote rm origin


もしも、pushできない場合は下記参照です。
https://www.sejuku.net/blog/71164
※リモートの変更をマージしてから、改めてpushする等すると良いでしょう

```
Pythonでドラクエ風対戦ゲームを作ってみた

Progateを終わらせたので、そこで学んだことアウトプットするためにドラクエ風対戦ゲームを作りました。



#  概要

勇者とモンスターの名前、HP、攻撃力を設定して戦い勝敗を判定する。同じモンスターと戦っても面白くないので、クラスを使いモンスターを追加する。random.choiceを使ってドラクエと同じようにランダムにモンスターと戦うようにする。



# ドラクエ風対戦ゲーム
まずはクラスMonsterを定義します。名前とHP、攻撃力を入力できるようにします。
```python:battle
class Monster:
    def __init__(self, name, hp, attack):
        self.name = name
        self.hp = hp
        self.attack = attack
```

次に勇者のステータスを設定します。まだまだ駆け出しですね。
```python:battle
hero_hp = 30
hero_attack = 5
```

いよいよモンスターを追加します！！
```python:battle
slime = Monster('スライム', 10, 1)
drakey = Monster('ドラキー', 13, 2)
darkdream = Monster('ダークドレアム', 130000, 999)
```


モンスターとランダムに戦えるようにします。
```python:battle
import random
monsters = [slime, drakey, darkdream]
appeard_monster = random.choice(monsters)
```

モンスターと戦います。
```python:battle
print(appeard_monster.name + 'が現れた！')
while hero_hp > 0:
    print('勇者の攻撃！')

    appeard_monster.hp -= hero_attack

    if appeard_monster.hp > 0:
        print(appeard_monster.name + 'は' + str(hero_attack) + 'のダメージを受けた！')
        print(' ')

    else:
        print(appeard_monster.name + 'は' + str(hero_attack) + 'のダメージを受けた！')
        print(appeard_monster.name + 'は力尽きた')
        print(' ')
        print('勇者は戦闘に勝った！')
        break

    print(appeard_monster.name + 'の攻撃！')
    print('勇者は' + str(appeard_monster.attack) + 'のダメージを受けた！')
    print(' ')

    hero_hp -= appeard_monster.attack

else:
    print('勇者は力尽きた')
    print(' ')
    print('勇者は戦闘に負けた！')
```

実行するとこうなります。まずはドラキーが現れました。

```
ドラキーが現れた！
勇者の攻撃！
ドラキーは5のダメージを受けた！
 
ドラキーの攻撃！
勇者は2のダメージを受けた！
 
勇者の攻撃！
ドラキーは5のダメージを受けた！
 
ドラキーの攻撃！
勇者は2のダメージを受けた！
 
勇者の攻撃！
ドラキーは5のダメージを受けた！
ドラキーは力尽きた
 
勇者は戦闘に勝った！
```

次はスライム。楽勝ですね。
```
スライムが現れた！
勇者の攻撃！
スライムは5のダメージを受けた！
 
スライムの攻撃！
勇者は1のダメージを受けた！
 
勇者の攻撃！
スライムは5のダメージを受けた！
スライムは力尽きた
 
勇者は戦闘に勝った！
```

いきなり裏ボス登場。コテンパンにやられる、、、
```
ダークドレアムが現れた！
勇者の攻撃！
ダークドレアムは5のダメージを受けた！
 
ダークドレアムの攻撃！
勇者は999のダメージを受けた！
 
勇者は力尽きた
 
勇者は戦闘に負けた！
```

# 最後に
基本的な形はできたので、今後ステータスやレベルの次元を取り入れたり、特技を選択できるようにしていきたいですね。
勇者共々プログラミングスキルも成長していきたいものです！
cscの作法 その89

# 概要
cscの作法、調べてみた。
Dictionary使ってみた。

# 参考にしたページ

https://qiita.com/akiko-b/items/3f5ee4c164b9951a500a

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/3b5c30bd-14b8-b384-5892-f2e2aad2f61c.png)

# サンプルコード

```
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Form1 : Form {
	Panel panel1;
	Panel panel2;
	Panel panel3;
	Button button1,
		button2,
		button3;
	Dictionary<Button, Panel> _dic = new Dictionary<Button, Panel>();
	public Form1() {
		this.ClientSize = new Size(500, 500);
		this.Text = "panel";
		panel1 = new Panel();
		panel1.Location = new Point(10, 50);
		panel1.Width = 400;
		panel1.Height = 400;
		panel1.BackColor = Color.Gray;
		panel2 = new Panel();
		panel2.Location = new Point(10, 50);
		panel2.Width = 400;
		panel2.Height = 400;
		panel2.BackColor = Color.Red;
		panel3 = new Panel();
		panel3.Location = new Point(10, 50);
		panel3.Width = 400;
		panel3.Height = 400;
		panel3.BackColor = Color.Blue;
		button1 = new Button();
		button1.Location = new Point(10, 10);
		button1.Size = new Size(100, 30);
		button1.Text = "1";
		button2 = new Button();
		button2.Location = new Point(160, 10);
		button2.Size = new Size(100, 30);
		button2.Text = "2";
		button3 = new Button();
		button3.Location = new Point(310, 10);
		button3.Size = new Size(100, 30);
		button3.Text = "3";
		this.Controls.Add(panel1);
		this.Controls.Add(panel2);
		this.Controls.Add(panel3);
		this.Controls.Add(button1);
		this.Controls.Add(button2);
		this.Controls.Add(button3);
		_dic.Add(this.button1, this.panel1);
		_dic.Add(this.button2, this.panel2);
		_dic.Add(this.button3, this.panel3);
		AddEvent();
		SelectPanel(this.button3);
	}
	private void AddEvent() {
		foreach (KeyValuePair<Button, Panel> pair in _dic)
		{
			Button button = pair.Key;
			button.Click += new System.EventHandler(this.Button_Click);
		}
	}
	private void Button_Click(object sender, EventArgs e) {
		Button button = (Button) sender;
		SelectPanel(button);
	}
	private void SelectPanel(Button selectedButton) {
		foreach (KeyValuePair<Button, Panel> pair in _dic)
		{
			Button button = pair.Key;
			Panel panel = pair.Value;
			if (button.Equals(selectedButton))
			{
				button.BackColor = Color.DarkBlue;
				button.ForeColor = Color.White;
				panel.Visible = true;
			}
			else
			{
				button.BackColor = Color.Gray;
				button.ForeColor = Color.Black;
				panel.Visible = false;
			}
		}
	}
	[STAThread]
	public static void Main() {
		Application.Run(new Form1());
	}
}





```

以上。
見直しておくと良いMicrosoft365のメールセキュリティ設定

## この文書は？

　最近Emotetの感染リスクが急拡大している中、Microsoft365側メールセキュリティ設定の中で再確認しておくと良いポイントについて手順付きで記載したものです。


## 背景

　日本マイクロソフトさんから「Exchange Online Protection ですぐにできる対策例」を教えてもらったのですが…。

- やるべきことは分かったがどうやったらいいんだっけ？
- メール対策なのにExchange 管理センターだけで設定完結しないの…？
- Docs読んでも手順にたどり着けない…。

ということでわかりにくい。同じように困っている人が３人ぐらいいそうなのでまとめてみました。


## 見直しておくと良いポイント

### 1. パスワード付添付ファイルをブロック

 添付ファイルがパスワードで保護されていると中の確認ができずマルウェア検知ができないため、パスワード付添付ファイルをブロックするのがベストです。
　ただし、まだパスワード保護のうえメールのやり取りをしている企業も多いため、その場合はファイル交換サービスの導入とセットで行うのが良いのではないでしょうか。

　設定方法は次のとおりです。

1. [Exchange 管理センター　→　メールフロー　→　ルール](https://admin.exchange.microsoft.com/#/transportrules)を開く。
1. ＋をクリックし、新規ルールを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/ac0b9d57-c8ec-d7f9-95f4-feab7571d431.png)
1. 新規ルール作成ウィンドウの下の方に、「その他のオプション」があるのでそれをクリック
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/9e598297-2756-7256-c29e-df9df4572c55.png)
1. 添付ファイルのパスワード保護に関する設定が選べるようになるので図のように選択し、適宜その他の設定を決めて設定完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/3843c1d2-bd94-8f9d-b9d6-333e3abef44e.png)


注意：「その他のオプション」をクリックしないと、添付ファイルの暗号化に関する設定が出てきません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/4c59d349-ff7a-d1a1-5a33-3d5e4963fe34.png)



### 2. ZAP（ゼロアワー消去）の有効化

　ゼロアワーオートパージ（ZAP）は、既に配信された悪質なフィッシング・スパム・マルウェアのメッセージを**遡って**検出し、無効化する電子メール保護機能です。これまでに届いたものも無効化するため、設定しておいたほうが良いでしょう。

　こちらはExchange 管理センターではなくMicrosoft 365 Defenderから設定することになります。デフォルト設定でマルウェアのゼロアワー消去は有効となっているようですので、管理者が設定したポリシーの中でうっかり無効になっていないか確認しておくのが良さそうです。

　確認方法は次のとおりです。

1. [Microsoft 365 Defender の ポリシーとルール　→　脅威ポリシー　→　マルウェア対策](https://security.microsoft.com/antimalwarev2)と辿ってマルウェア対策のポリシー一覧を表示させます。Default (既定)のポリシーしかない場合はデフォルトでZAP（ゼロアワー消去）が有効化されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/74baaa20-e456-4810-cb07-b0ea5deb720c.png)

1. 設定したポリシーや・作成しようとするポリシーについて「マルウェアのゼロアワー自動消去を有効にする」にチェックが入っていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151198/557f192a-868b-6c24-3848-5bf8960b280d.png)




## 参考

- [Microsoft Defender で 0 時間の自動削除を実行Office 365 \- Office 365 \| Microsoft Docs](https://docs.microsoft.com/ja-jp/microsoft-365/security/office-365-security/zero-hour-auto-purge?view=o365-worldwide)
- [Exchange Online Protection （EOP）での機能と設定する項目 \| インフラ基盤の備忘録](https://infra-memorandum.com/exchange-online-protection-%EF%BC%88eop%EF%BC%89%E3%81%A7%E3%81%AE%E6%A9%9F%E8%83%BD%E3%81%A8%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E9%A0%85%E7%9B%AE/)
M5Core2とM5Atom Echoに加えて、M5Stack FireやM5StickCでもMP3再生する

前回の投稿「[M5Core2とM5Atom EchoでMP3再生](https://qiita.com/poruruba/items/3a9afd186991a0227f1d)」で、M5Core2とM5Atom EchoからMP3再生しました。この時は、I2Sの機能を使っていましたが、DAC接続のM5Stack FireやM5StickCでも同様に再生できるので実際にやってみます。

# ESP32におけるオーディオ再生

前回同様以下のライブラリを使わせていただいています。

earlephilhower/ESP8266Audio

https://github.com/earlephilhower/ESP8266Audio

〇宣言

以前の投稿の時と同じです。

```cpp:module_audio.cpp
#include <driver/i2s.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"
#include "AudioFileSourceSD.h"

static AudioOutputI2S *out = NULL;

static AudioGeneratorMP3 *mp3 = NULL;
static AudioFileSourceSD *file_sd = NULL;
static AudioFileSourceHTTPStream *file_http = NULL;
static AudioFileSourceBuffer *buff = NULL;
```

〇準備

このAudioOutputI2Sのコンストラクタでのパラメータの指定方法が違います。
I2Sを利用する場合（M5Core2やM5Atom Echoの場合）

```cpp:module_audio.cpp
  out = new AudioOutputI2S(I2S_NUM_0);
  out->SetOutputModeMono(true);
  out->SetGain(audio_gain / 100.0);
```

DACを利用する場合（M5Stack FireやM5StickCの場合）

```cpp:module_audio.cpp
  out = new AudioOutputI2S(I2S_NUM_0, AudioOutputI2S::INTERNAL_DAC);
  out->SetOutputModeMono(true);
  out->SetGain(audio_gain / 100.0);
```

DACを利用する場合、音声信号は、PIN番号26に出力されます。
M5Stack Fireの場合には本体内蔵のスピーカに接続されています。M5StickCでは、本体上側の端子から出力され、以下の周辺デバイスのスピーカを接続することで再生できます。

M5StickC Speaker Hat（PAM8303搭載）

https://www.switch-science.com/catalog/5754/

〇PIN設定

以前の投稿の時と変わりませんが、このPIN設定は、I2Sを利用する場合にのみ必要です。

```cpp:module_audio.cpp
  out->SetPinout(bclk, lrck, dout);
```

3つのPIN番号を指定します。BCLK、LRCK、DataOutです。
M5Core2では、以下の通りです。

```
BCLK：12
LRCK：0
DataOut：2
```

M5Atom Echoでは、以下の通りです。

```
BCLK：19
LRCK：33
DataOut：22
```

後は同じです。

〇URLから再生

```cpp:module_audio.cpp
  file_http = new AudioFileSourceHTTPStream(url);
  buff = new AudioFileSourceBuffer(file_http, bufsize);
  mp3 = new AudioGeneratorMP3();
  mp3->begin(buff, out);
```

〇SDからの再生

```cpp:module_audio.cpp
  file_sd = new AudioFileSourceSD(path);
  if( !file_sd->isOpen() ){
    delete file_sd;
    file_sd = NULL;
    return JS_EXCEPTION;
  }
  mp3 = new AudioGeneratorMP3();
  mp3->begin(file_sd, out);
```

そして以下を継続呼び出し

```cpp:module_audio.cpp
  if( mp3 != NULL ){
    if (mp3->isRunning()) {
      if (!mp3->loop())
        mp3->stop();
    }
  }
```

# Javascriptでの呼び出し

以下の技術書で紹介している、ESP32内部で動作するJavascript環境で動作させる場合のJavascriptコード例を示します。

M5StackとJavascriptではじめるIoTデバイス制御

https://poruruba.booth.pm/items/3735175

I2Sで再生するM5Stackモジュールの場合

```js
import * as audio from "Audio";

audio.begin();
//audio.setPinout(12, 0, 2); // for M5Core2
audio.setPinout(19, 33, 22); // for M5Atom Echo
audio.playSd("/g_07.mp3");
//audio.playUrl('http://192.168.1.16:20080/g_07.mp3');

setInterval(() =>{
	audio.update();
}, 1);
```

DACで再生するM5Stackモジュールの場合

```js
import * as audio from "Audio";

audio.begin(audio.INTERNAL_DAC);
audio.playSd("/g_07.mp3");
//audio.playUrl('http://192.168.1.16:20080/g_07.mp3');

setInterval(() =>{
	audio.update();
}, 1);
```

細かな処理はC言語で実装済みのため、Javascriptからは簡単に呼び出せるようにしています。

以上
スマートコントラクトセキュリティ講座〜リエントランシー編〜

スマートコントラクトの開発は言わずもがな、バグが命取りです。億単位のハッキング事例がたくさんありますよね。
本講座では、よくある？脆弱性をご紹介します。

今回取り上げるのはリエントランシー攻撃。実例としてかの有名な「The DAO事件」が挙げられます。

## 前提知識
コントラクトは便利なことに他のコントラクトを呼び出すことが可能です。

コントラクトアドレスはイーサの送金時に実行されるfallback関数を設定することができます。fallback関数が呼び出されるのは以下の2通り
*	呼び出しに一致する関数が存在しないとき
*	コントラクトアドレスへのイーサ送金時にreceive関数が設定されていない時

fallback関数についてより詳しく知りたい方はこちらhttps://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=fallback#fallback-function

## リエントランシー攻撃とは？

リエントランシーという単語は再入場（Re-entry）から来ているらしいです。その名の通り、コントラクトAが悪いコントラクトBを呼び出すと、悪いコントラクトBはコントラクトAを再度呼び出します。

それが、なぜ最悪な結果をもたらす可能性があるのか

例えばコントラクトAはたくさんのetherを持っていて、いろんな人に配りたい。そこで、以下のような動作を行う関数fがあるとします。
1. bool型変数 alreadyGivenが、falseであることを確認。trueなら終了
2. msg.senderに1etherを送金
3. alreadyGivenをtrueに変える。

一度この関数fを実行すると、alreadyGivenがtrueになるので、1etherしかもらえないはずです。

悪いコントラクトBを使った攻撃方法を示します。
まず、悪いコントラクトBからコントラクトAの関数fを実行する。2の処理により1etherが送金されてくると、fallback関数が動き出します。fallback関数から関数fを呼び出すとする。すると、もう一度fの1から処理が始まるのです！3の処理はまだ実行されていないため、alreadyGivenはfalseのままなので、もちろんまた1etherが送金されます。

fallback関数内からコントラクトAの残高をみていれば、そこで実行を終了してあげることで、金が尽きて送金処理に失敗してリバートするみたいなことも回避可能です。

リエントランシー攻撃のコード例 → https://solidity-by-example.org/hacks/re-entrancy/ 

## 対策
* 送金や外部コントラクト呼び出しは、ステートを変化させてから行う
今回の関数fは2と3の順を入れ替えればおkです。
* ReEntrancyGuardを使う https://solidity-by-example.org/hacks/re-entrancy/ 
modifier noReentrantをfにつけると、再実行を防ぐlockをかけることが可能。
openzeppelinの実装: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol

この講座は次回があるかはわかりません。やる気次第です。間違いがあったら指摘お願いします。
ファクトリーパターンとは

## ファクトリーパターンとは

OOPのデザインパターンの代表的なものの一つで、

- クライアントが欲しいインスタンスを直接newするのではなくファクトリークラスが代わりにインスタンスを作成する
- 作成されるクラスはインターフェースを埋め込んでおり、インスタンス後の操作も具体的な実装を読まなくてもできる

というものです。インターフェースによる抽象化をインスタンス生成時にまで敷衍したイメージです。同じインターフェースを埋め込んだり同じ抽象クラスを継承しているクラスが多いときに特に威力を発揮します。

## 原始的なファクトリークラス
これもファクトリークラスといえばファクトリークラス。作成対象のクラスが増えるたびにファクトリークラスの方も修正しなくてはいけない。
```java
public class ProductFactory{
	public Product createProduct(String ProductID){
		if (ProductID==ID1)
			return new OneProduct();
		if (ProductID==ID2)
			return new AnotherProduct();
		... // so on for the other Ids
		
        return null; //if the id doesn't have any of the expected values
    }
    ...
}
```

## IDからインスタンスを自動生成するファクトリークラス
たぶん一般的に使われている方法がこれ。コード量は増えるけど、
- クライアントはファクトリーに欲しいクラスのIDだけ渡せばいい
- ファクトリーは作成対象クラスにどんな種類があるか知らなくていい

等、三者がうまく疎結合になっている。

```java
// 作成対象クラスの抽象クラス.
abstract class Product
{
	public abstract Product createProduct();
	...
}

// 作成対象クラス.
class OneProduct extends Product
{
	...
    // クラスがロードされた時にIDを登録しておく.
	static
	{
		ProductFactory.instance().registerProduct("ID1", new OneProduct());
	}

    // 自身をインスタンス化する.
	public OneProduct createProduct()
	{
		return new OneProduct();
	}
	...
}

class ProductFactory
{
    // 作成対象クラスのIDを登録する.
	public void registerProduct(String productID, Product p)    {
		m_RegisteredProducts.put(productID, p);
	}

    // クライアントから渡されたIDを元に作成対象クラスのインスタンスを作成する.
	public Product createProduct(String productID){
		((Product)m_RegisteredProducts.get(productID)).createProduct();
	}
}
```
alias ..="cd ../"  で楽に上の階層に遷移できる

# 知っている人は知っている？上の階層に遷移できるalias

下記を`home`直下にある`.bashrc`に記述し、反映させることで上の階層に簡単に上の階層に遷移できます。

```bash
alias ..="cd ../"
```

下記を実行
```
source .bashrc
```

使い方
```
$: ..
```

使い方は、`..` <-左記を実行するだけで、上の階層に遷移できます。
Apexテストコードを書くときのtips（随時更新）

## はじめに

最近プログラムを書くときはテストコードから書くようにしているのですが、
ちょいちょい忘れるApexテストコードの癖があるので記事にしました。

## インサート後に再セレクトが必要な項目

以下の項目はインサート後に再セレクトしてあげないと、
値が入らず「あれ、なんでnullなんだろう..」ってなります。

- 数式
- 積み上げ集計項目


**（2022/04/10　追記）**
```recalculateFormulas()```を使うと、オブジェクトごとセレクトしなくても、数式項目が更新されます。
※積み上げ集計は更新されません

ただ、積み上げ集計を数式項目化すれば更新できたり、
FormulaクラスとSObjectクラスで挙動が違ったりするので、使う前に癖を掴んでおくと後々便利かと思います。

```recalculateFormulas()```の解説は[こちら](https://qiita.com/t_yano/items/aad83844fededa5951d8)の記事が分かりやすかったです。

## ユニーク項目の重複

カスタムで作ったユニーク項目は、自分でユニークにしてあげる必要があります。

乱数を入れてあげるのが楽なのですが、
もっとスマートな方法があったら教えていただきたいです🙇‍♂️

乱数を返す関数サンプル

```
public static String createRandomNumStr(){
    String result = '';
    Decimal randomNum = Decimal.valueOf(Math.random()) * 10000;
    Decimal randomNumRounded = randomNum.round(System.RoundingMode.HALF_EVEN);
    result = String.valueOf(randomNumRounded);
    return result;
}
```
使用しているブラウザを判定

## 使用用途
- ユーザーが使用しているブラウザを判定する

## 使用方法
```php:ユーザーエージェント判定
$hoge = $_SERVER['HTTP_USER_AGENT']; // ユーザーエージェントを取得
if (strpos($hoge, 'ここに各ブラウザを判定する文言を入力') !== false) {
  指定したブラウザだった場合に行いたい処理を入力
}
```
strpos は判定したい文言が見つからない場合に false を返す。

## IEサポート終了
2022 年 6 月 16 日（日本時間）をもって、Microsoft 社より Internet Explorer のサポートが終了となることが告知されています。

IEを判定するには
`MSIE`　か　`Trident`という文字が入っているかを確認する。
（古いIEは「Trident」が含まれないので、2つを確かめる）

## その他のブラウザ
「Chrome」は
`Chrome` が入っているか判定する。
`Safari` の文字列が含まれる場合もあるため注意。
`Chrome` が入っていたら「Chrome」。

「Safari」は
`Safari` が入っているか判定する。
`Chrome` の文字列が含まれる場合は「Chrome」。

「Edge」は
`Edge` が入っているか判定する。
「Edge」は `Chrome` `Safari` の文字列も含まれる場合がため判定順に注意。
「Edge」のユーザーエージェントの例
`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586`

「Firefox」は	
`Firefox` が入っているか判定する。
　
　
　
この記事は以下の情報を参考にして執筆しました。
- https://htaccess.cman.jp/useragent/browser/
Crystalにさわり始めてから半年ぐらいの人が感じたRubyとCrystalの違い

こんにちは。

初心者の所感というのは意外と大切で、初心者でなくなってしまうと得られた知識が空気のようになってしまい、忘れられてしまうことがあります。なので、Crystalにさわりはじめてから半年ぐらいの段階で、Rubyとここが違うなと感じたことをメモしておこうと思います。基本的に**ポエム**です。

:::note info
半年ぐらいと銘打ってますが、その後も更新しています。
:::

# Crystalにさわり始めてから半年ぐらい

## 大クラス主義の配列とHashがないけどなんとかなる

Rubyだと、大クラス主義の配列がいろいろな役目を果たしてくれます。配列に異なるクラスのデータを詰め込んで、あとから簡単に追加したり削除することができます。Crystalだとできません。

これは実はそこまで大きな問題ではなくて、記述量は多くなりますが、雑多なクラスを配列やHashに詰め込む変わりに、自作のクラスを定義したり、構造体を使ったり、タプルを使うことでだいたい解決できると感じます。

## evalが使えない

わたし、よくわかってないんですが、直感的にはこれがRubyとCrystalの本質的な違いではないかという予感がしています。CrystalでEvalを実行するためには、Crsytal本体を実行ファイルに含めて、都度コンパイルするような必要があると思います。それは、今後どれだけCrystal言語が発展しても困難でしょう。

https://stackoverflow.com/questions/31036160/how-to-execute-code-from-a-string-variable-in-crystal/31038320#31038320

Crystal言語は実行速度はたいへん高速ですが、コンパイル時間が短いとは言い難い言語です。ですから、Crsytalコンパイラを実行ファイルに含めて、その都度コンパイルするようにすると相当遅くなりそうな気がします。実際どうなんだろう？　ひょっとするとRubyと
同じ速度に収束していくかもしれません。あと、Ruby言語ではevalの多用はバッドプラクティスとして嫌われていますが、プログラミング学習のためにはむしろ、そういう難しいevalを多用するコードを書いてみた方がいいのかも知れないなと思いました。自分自身の出力をコードとして実行するquineとかもそういった性質を持つコードの一つかもしれません。

https://qiita.com/kojix2/items/31d512eacb58cd667543

## 戻り値の型を１種類にするか、複数にするか

メソッドを実装する時、Rubyの場合は戻り値は多くの型を取るようにするのが親切です。ところがCrystalでは1つの型だけ返す方が便利だと感じます。例えば、`alignment.tag("TAG")` のようなメソッドを考えたとき、Rubyの場合、戻り値の型はタグに応じて `Integer`、`Float`、`String`、`Nil` を取るとしても、Crystalでは `alignment.tag_int( "TAG1")` `alignment.tag_float("TAG2")` `alignment.tag_string("TAG3")` といったように、それぞれの型を返すメソッドを別に用意した方が親切であると感じます。

利用者は自分が取りたい型を知っているので、使わないとわかってる型のために余計な記述（たとえばオーバーロードなど）をしないとコンパイルが通らないのは苦痛です。とすると、CrystalとRubyでは提供するべきメソッドの粒度は違うということになると思います。

## nilを許容するかしないか

上記のようにRubyとCrystalでは戻り値の単位（したがって作成するべきメソッドの粒度）が異なります。ここに輪をかけて難しくなるのが、`nil` の扱い方です。CrystalはNil許容型を作ることができます。しかし、nilを許容してしまうと、その都度オーバーロードが発生してしまうので、基本的にはnilを含まないようにするのが得策だと思います。

## Makefile文化

Rubyでは作業の自動化にRakefileを用いますが、CrystalではRakeに相当する共通ツールが存在せず、一般的にMakefileが用いられています。（これは必ずしもCrystalのユーザーが`make`が好きだということを意味しているのではなく、単純にコミュニティのサイズが小さくて`rake`のようなコマンドを作成できていないだけだと思います）個人的にはこれがストレスで、CrystalにもRakeみたいな便利なツールがあるといいなと思います。

## Ruby-FFI と Crystal のCバインディングではメモリ解放用メソッドの記述場所が違う

私はRuby-FFIを使ったバインディングを多く作成しているのですが、Ruby-FFIとCrystalのCバインディングでは、ガーベジコレクション（メモリの自動解放）の実装単位が異なることに気が付きました。Ruby-FFIでは中レベルのAPIであるFFi::Structに対して、`self.release` を実装しますが、Crystalでは高レベルのAPIで用いるクラスに直接 `finalize` を記述します。これはほとんどの人にとっては使わない情報だと思いますが、私はCのバインディングをたくさん作るのでRubyとCrystalの違いとして印象深く感じました。

## 今の段階でよくわかっていないと感じること

* ClassとStructの使い分け
* マクロでどこまでできるか
    * マクロでは each ではなく for 文であるという事実

この記事は以上です。
Ubuntu ローカルのgit設定~SSHキー取得~GitHub,Gitlab連携まで

## 環境
- Ubuntu18.04以降

## 目的
Ubuntuを入れたときにGitHubやGitLabからSSHキーを利用してcloneできるようにしたい。

## ローカルのgit設定
```
$ sudo apt install git
$ git config --global user.name [名前]
$ git config --global user.email [メールアドレス]
```
## SSHキーの作成
```
$ ssh-keygen -t rsa -b 4096 -C [メールアドレス]
Enter a file in which to save the key (/home/you/.ssh/id_rsa): [Press enter]
（略）
Enter passphrase (empty for no passphrase): 
（略）
Enter same passphrase again:
（略）
```
メッセージが出てきたらEnterを押します。合計3回Enterを押すだけで良いです。
四角の中に暗号のようなものが入った表示がされたらOK。

## SSHキーをコピー
直接コピーできないので以下のコマンドよりコピー。
xclipを最初にインストールしてください。
```
$ sudo apt install xclip
$ xclip -sel clip < ~/.ssh/id_rsa.pub
```

## GitHub,GitLabにSSHキーを登録
いずれもプロフィール画面をクリックし、
GitHub：`Settings`→`SSH and GPG keys`→`new SSH key`
GitLab：`Edit Profile`→`SSH keys`
を押します。
`key`の欄に先ほどコピーしたSSH keyを貼り付けます。`title`は適当（PCの区別がつくような名前とか）で大丈夫です。

`add`のボタンを押したらOKです。

## conig設定
.sshディレクトリに任意のエディターでconfigファイルを新規作成。
```
$ cd .ssh
$ gedit config
```
configファイルに以下を記述。

```config
Host github
    HostName github.com
    IdentityFile ~/.ssh/id_rsa
    User git
    IdentitiesOnly yes
Host gitlab.com
    HostName gitlab.com
    IdentityFile ~/.ssh/id_rsa
    User git
    IdentitiesOnly yes
```

## SSH接続確認
ネットワークに注意してください。学校や会社で管理されているネットワーク内ではSSH接続ができない可能性があります。
SSH接続する際は、外部ネットに接続してください。
以下のように表示されたら完了です。

GitHub
```
$ ssh -T git@github.com
Are you sure you want to continue connecting (yes/no)? yes
Hi [username]! You've successfully authenticated, but GitHub does not provide shell access.
```
GitLab
```
$ ssh -T git@gitlab.com
Are you sure you want to continue connecting (yes/no)? yes
Welcom to Gitlab, [username]!
```
Railsでlink_to deleteが出来ない

某オンラインスクールで学習4ヶ月目、
Railsにてオリジナルwebアプリを作成していて
link_toの"削除"が実行できなくてぐるぐるしたので備忘録として。
　

dpテーブルに投稿したレコードを削除するため実装、削除出来ず。
```ruby
<%= link_to "報告の削除", dp_path(@dp.id), method: :delete %>
```
　
エラー画面も出ず、元の画面に戻ってきてしまう。
ログを見るとどうやらGETで動いている模様。
　

参考にした記事
https://qiita.com/mami3sansan/items/6a9710b3ffdc937aa5f3
https://nabelog.org/695/
https://takayuki-inoue.hatenablog.com/entry/2018/01/12/105846
　

その他いくつか読んでみたが、解決に至る糸口が見つからず。
上記記事等から得た知識を元に思い当たる節を探ってみたところ、
作成初期に放り込んでいた、bootstrap絡みの
cssやらjavascriptあたりに要因がありそうな気配が。
（bootstrapは全然使いこなせず放置しておりました）

で、その中のjavascriptが要因だった模様。
該当と思しきファイルをエイっと削除（これもイケナイような気もする）したところ、
無事にアプリ内のデータ削除が実行出来るようになりました。

訳も分からないまま適当に放り込まない、
ということを念頭に地道に勉強続けます。。。

※ここの理解が違いますよ、等など教えていただけましたら有り難いです
Reactの公式チュートリアルで学んだことメモ②（ステートとライフサイクル）

こちらの記事のメモです。
- https://reactjs.org/docs/state-and-lifecycle.html

現在の時刻を表示するコードは、`React.Component`の継承クラスを使って以下のようにカプセル化できる。ポイントは以下
- `constructor`で`props`を引数として受け取る。
- `render()`でJSXを返す
- 現在の時刻をクラス内で生成する場合は`this.state`を定義する。`this.state`はローカルな**ステート**と呼ばれる。
- マウント時（最初に`Clock`クラスがレンダリングされたとき）とアンマウント時（`Clock`が取り除かれるとき）にコードを実行したい場合は`componentDidMount()`と`componentWillUnmount()`を使う。

```jsx
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
```
[CodePen](https://codepen.io/gaearon/pen/amqdNr?editors=0010)

ステートの値を変更する場合は`setStage()`メソッドを使う。
```javascript
// Wrong
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});
```
パフォーマンス上の理由で複数の`setState()`がまとめて処理される場合がある。`this.props` と`this.state`の値が非同期に変更される可能性があるため（？ここはよくわからなかった）、`setState()`内で直接両者を参照するのではなく引数で渡すべし
```javascript
// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
```
Reactコンポーネントが通常のOOPのクラスと違う点
- 呼び出し側（JSX側）でコンポーネントがクラスか関数か知ることもできない
- 外部からプロパティを操作することができない