OmniAICreator/Qiita-1.07M huggingface dataset:
ServerlessFrameworkでLambda + puppeteer

[Uzabase Advent Calendar 2021](https://qiita.com/advent-calendar/2021/uzabase) 15日目は @overgoro56 が担当します。

今年の夏くらいにブラウザ操作の作業で自動化したいことがあってLambda + puppeteerをServerlessFramework使って構築してみたので紹介します。

# Lambda + puppeteer で気をつける点
Lambdaデプロイパッケージのサイズ制限とpuppeteerのサイズとの戦いです。
250MBの制限とコンソールエディタ3MBの制限に気をつけます。

[Lambda クォータ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html) の一部抜粋

``` 
50 MB (zip 圧縮済み、直接アップロード)
250 MB (解凍後)
このクォータは、レイヤーやカスタムランタイムなど、アップロードするすべてのファイルに適用されます。
3 MB (コンソールエディタ)
```

# 今回やったこと
## puppeteerをChromeのバイナリなしで利用する
PuppeteerのChromeは容量が大きので使わない設定を行う

```:.npmrc
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=TRUE
```

`puppeteer-core` のインストールでやる方法でもよさそうですが、 `PUPPETEER_DOWNLOAD_PATH` の環境変数の設定が必要そうだったりしたので今回は上記を使用しました。

## chrome-aws-lambda を使う
Lambda用に軽量化されたchromiumを使って容量削減します。
[chrome-aws-lambda](https://github.com/alixaxel/chrome-aws-lambda)

## Lambdaを軽量化し、コンソールでコード編集できるようにする
折角スクリプト言語でLambda書くんだから、簡単に編集してデバッグできるようにしておきたいです。

### node_modulesはLambdaレイヤーに退避
`serverless-layers` のプラグインを導入します。

```:serverless.yml
custom:
    serverless-layers:
        functions: # optional
            - sampleHandler
        dependenciesPath: ./package.json
        layersDeploymentBucket: ${self:custom.envfile.environment.${self:provider.stage}.layersDeploymentBucket}
```

### lambdaのコード以外をアップロードしないように除外する
以下のような形でソース以外除外する設定をします。
3MB制限に引っかかることは少ないかと思いますが念のため。

```:serverless.yml
package:
    patterns:
        - '!**'
        - src/**
```

## `serverless-prune-plugin` を入れておく
デプロイするごとにLambdaの各バージョンのコードを保持するため、容量が大きくなることを防いでおきます。
以下は直近10バージョンを残す設定。

```:serverless.yml
custom:
    prune:
        automatic: true
        number: 10
```

# 最後に
短いですが、上記の点を気をつけるだけでLambda + puppeteerが簡単にできます。
さすがServerlessFrameworkといったところです。
ローカル実行も簡単にできちゃいます。

今回紹介した内容のサンプルコードを以下のレポジトリにアップしておきました。
環境ごとに変数を切り替える設定も入れてるので実運用に用いやすいと思います。
よかったら参考にしていただけると嬉しいです。
eslint入れてないなど気になるところはありますがご容赦ください。
https://github.com/overgoro56/serverless-lambda-puppeteer
サーバーレスについて


# 0. 背景
サーバレスアーキテクチャという言葉を聞いたことがあった。

英語のサーバレス（Severless）という言葉から、サーバが存在しないと勝手に思ったが。
本当に？サーバが存在しないって、そんなのできる？という疑問もあったので、どういう意味が調べてみた。

# 1.サーバは存在する
サーバレスというのは、サーバのバックエンドが存在しないという意味ではない。

バックエンドは存在する。

しかし、**直接サーバを管理する必要がない**ということ。

# 2.なぜサーバレスというものが生まれたか
仮に、ウェブアプリを開発していると想像してみる。

サーバが必要であれば、サーバを買うしかない、という時があった。

サーバ用のパソコンを買って、社内のどこか、家のどこかに置く。

そして、電源を入れて、動かせる。

もし、ウェブアプリに接続が急に増えて、サーバが耐える通信トラフィックを超えそうだったら？

早く、メモリを注文してサーバに追加しなければならない。

つまり、ハードウェアも、ソフトウェアも管理しないといけない。

その中で、AWSのEC２というサービスが現れ、サーバを借りて使用することができるようになった。

それにより、急なトラフィックの増加にも、簡単に対応できるようになった。

直接メモリを追加する必要なく、お金さえ払えば、AWSの方で容量など簡単に追加できるから。

この時点、サーバのハードウェアを直接管理するリスクは、ウェブアプリ開発側ではなく、AWSサービス提供会社が負うことになったはずだ。

しかし、サーバのソフトウェアを直接管理する際の責任は、まだウェブアプリ開発側が持っている。

サーバのセキュリティー、アップデート、バックアップなど。

サーバのソフトウェア管理も誰かに任せて、本当の仕事、アプリ開発に集中したいならどうしたらいいか？

# 3.バックエンドを小さい関数に分ける
バックエンドを小さい関数に分け、直接管理しないサーバにアップする。

例えば、AWSのLamda。

サーバレスではない場合、サーバは24時間リクエストに応答するため、待機している。

サーバレスの場合、アップロードした関数は、基本的に寝ている。リクエストがあった場合のみ、動作することになる。

# 4.メリット
## リーズナブルな費用

サーバレスではない場合、リクエストがあっても、なくても、かかる費用は変わらない。

24時間リクエストに応答するため、待機しているから。

サーバレスの場合、リクエストがあって、関数が実行されたら、お金を払う。

もし急にユーザーが1000名増えたら？AWSは関数のコピーを作成して実行する。

ユーザーがいなくなったら？また寝る。

## サーバの管理を気にせず、本質的な作業に集中できる

サーバのセキュリティー、アップデート、バックアップなどから開放できる。

本当に必要な作業、アプリ開発に集中できる。

# 5.デメリット・懸念
## 関数の開始までかかる時間

リクエストが来るまで、サーバの関数は寝ている。

リクエストが来たら、関数を起こして、実行する準備が必要。

大きな差はないかも知れないが、サーバが24時間待機していた時に比べると、より時間がかかるはずだ。

1msの遅れも許されない場合は、サーバレスはいい選択ではないかも知れない。

(よく使われる関数は、眠らせずに待機しておく設定があるかも？)

## サーバ提供者に依存しすぎでは？

まず、アプリをサーバレスに相応しい仕組みに変更するか、最初からサーバレスを考慮して設計する必要がある。

また、サーバに対して直接コントロールできなくなる。

そして、もし他のクラウドサービスに引っ越し（マイグレーション）が必要になったら、簡単にできそうかどうかもわからない。
アナログ時計

Scratchでアナログ時計を作ってみましょう。アナログ時計を作るには、二つのテクニックを使います。

* １秒ごとにイベントを発生させる
* 針のアニメーション

# １秒ごとにイベントを発生させる

シンプルに書くとこんなプログラムになります。ただ実際には、１秒といっても誤差が発生してしまい、例えば"0.99秒"の次に"2.01秒"のように1.02秒しかたっていないのに、秒以下が切り捨てられて２秒進んでしまう時があります。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/ae7240d6-952c-1431-7e44-d512a203090d.png">
図：誤差の出る例

そこで、一つ前の状態を管理する変数を用意して、違いが発生したらイベントを発生させるようにします。

<img width="75%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/b2937088-250f-2b88-fcd6-5b877c5406ee.png">

# 針のアニメーション

秒針と60秒で360度になるので、1秒=360/60=6度となるように角度を調節します。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/bbfd67be-6e5c-18c3-afa8-2e7f692fe853.png">

長針も秒針と同様に、1分=360/60=6度となるように角度を調節します。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/a75eb9aa-e9df-e319-369c-50673d380292.png">

短針は１時の２時の間の状態もないと不自然なので、時間だけでなく分も考慮して角度を決定します。「現在の"時"を12で割った余り」は、１時と１３時をどちらも１時とするようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/4498b672-b6f3-3315-b171-0c540138d2f5.png)

針のコスチュームは、文字盤の中心になる位置が真ん中になるようにデザインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/e609309a-c4e7-720d-e7b6-4bff117efd26.png)

サンプルプロジェクト
https://scratch.mit.edu/projects/525271061

![スクリーンショット](https://cdn2.scratch.mit.edu/get_image/project/525271061_480x320.png)
ワールド紹介動画の撮影について

この記事は[クラスター Advent Calendar 2021](https://qiita.com/advent-calendar/2021/cluster)、15日目の記事です。前回（12/14）は@ulaphさんの「iOS開発から見るcluster」でした。clusterモバイルアプリは入室前と入室後で独立しているおかげで、この一年で大幅に進化できたんですね！

## はじめに

こんにちは！クラスター株式会社でインターンをしているあらーとです。主にUnityでの制作やモデリング、動画作成のお手伝いをしています。よりわかりやすい形で伝えると、アバターマーケット会場やクラスターロビーの作成、今週の人気ワールドの動画作成を担当しています。

さて、今回は「[今週の人気ワールドの動画](https://note.com/cluster_official/m/m4faeb0cc72a8)」の撮影時に考えていることを書き起こしたものです。cluster では日々魅力に溢れたワールドが、クリエイターの皆様の手から公開されています。ワールドの魅力は実際に訪れて体験してもらうことが一番なのですが、まずワールドについて知ってもらう事が重要だと考えています。出来る限りそのワールドの意図を汲み取ってその魅力を伝えられるように撮影しています！

また、使っているものはイベントでの[プロカメラマンモード](https://note.com/cluster_official/n/n3e7afe7cae22)と同等のものを用いているので、イベントでのカメラワークの参考になれば幸いです😉

https://note.com/cluster_official/m/m4faeb0cc72a8



## よく用いるカメラワーク3選
交流がメインであったり、アートや建築などワールドのジャンルは多種多様です。今回紹介するカメラワークはどのタイプのワールドにも対応できるものを紹介していきます💪

実際のコントローラーの操作は動画内のワイプを御覧ください。

<iframe width="400"  height="450"src="https://www.youtube.com/embed/D1I3vD0qKRo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

まず1つ目はそのワールドの特徴となるものがある時に用いるカメラワークです。
左スティック(LS)を注目したいものに視点を合わせて、右スティック(RS)を動かしていきます。この時にLSを倒しての水平移動だけだと、ワールドの広がりを伝えづらいため、左右のトリガーをほんのわずか引いてカメラの高さを変化させていきます。このとき上昇しているならRSを下に、下降しているならRSを上にゆっくりと倒して行くのがポイントです。
このカメラワークは特に屋内を紹介するときや、ワールドがシンメトリーになっている時によく使用しています。目線の高さから天井付近までゆっくりと上昇していくことで、カメラを動かしづらい室内においても広く映せるのでおすすめです！

2つ目は建物など少々大きめのものを撮影する時に用いるカメラワークです。
動画では、LSを10時の方向から8時の方向に徐々に倒し、RSは被写体を画面中央に映るように動かします。トリガーを最大に引いた時を1とすると、トリガーを引く強さは1/3ぐらいを目安に引きます。
建物斜め前から撮影を始め正面を通り、斜め前に抜けていく構図です。カメラ自体は中央の溝とほぼ平行に動いているので、狭い通路の多いビル街や渓谷など、高いものに囲まれたワールドでよく使用しています。被写体のスケール感やワールド全体の雰囲気を伝えるのに向いているので是非参考にしてみてください🙌


3つ目は門や入り口など外から内へ撮影する時に用いるカメラワークです。
高めの位置から入口に向かって、高度を下げながら被写体に近づいていきます。
カメラの高さを下げながらRSをゆっくりと上に倒していきます。地面に近づくにつれ、より空を映すようにすることで開放感が得られ、ダイナミックな映像になります。トリガーの引く強さは1/3くらいを目安に引いて、風船が地面に落ちるくらいの速度で降りていきます。映像の終わりにカメラが被写体を、画面の右1/3くらいに映せると被写体に視線を誘導しやすくなるのでおすすめです。
建物の中に入っていくときや、くぐり抜けられる物がある時にこのカメラワークを使用しています。バーチャルなカメラなので、壁にぶつかっても壊れたりしません、思う存分練習して良い映像が撮れるまで繰り返しましょう！


## カメラ操作のコツ
私がカメラの移動速度について気をつけていることについて書きます。プロカメラマンモードを起動した初期状態では少し速度が早いため、左ボタンを押して速度を落としています。便宜的に初期状態の速度を5としましょう。屋内であればその半分の2.5、屋外を撮影するのであれば3、乗り物などを追いかけるときは7.5ぐらいを目安に調節しています。これは私個人が気に入っているカメラの速さなので、各々でちょうどいい速度を見つけてみてください。

次にコントローラーのスティック操作について書きます。上記の動画を見ていただくとコントローラーのトリガーやスティックはほとんど初期位置からわずかにしか動かしていません。親指で緻密な操作をするのは結構慣れが必要になります。その慣れを得るまでは、下記のようなコントローラーのスティックにつけるアシスタントリングを付けることをおすすめします。これをつけることでスティックの可動域が制限され、細かな操作ができるようになります。全国の100円均一ショップなどで入手が可能なので、是非試してみてください！
<img width="300" alt="PXL_20211214_135803982.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2254237/e4b3cdb5-ddb1-13ad-98e9-4e988e7c47f2.jpeg">

## おわりに

一昨年、昨年はいちユーザーとして Cluster Advent Calendar に参加していましたが、縁があり今年はインターンとしてクラスター Advent Calendar 2021 に参加することができました。これからも魅力的なワールドを紹介していきたいと思います！
最後まで読んでいただきありがとうございました！

明日（12/16）は、@izumiさんの「UnityネイティブプラグインのM1 mac対応」です。
このおかげで M1 Mac でも cluster をベータ版として使えるようになっています。どういった実装がされているのが解説が楽しみですね！
WindowsからUbuntuへリモートデスクトップ接続する(二番煎じ)

#環境
今回実行したUbuntuのバージョンは**``ver.20.04.3``**で行いました。
Ubuntuのバージョンは以下のコマンドで確認できます。

```
$ cat /etc/os-release
```
#参考にしたサイト
・[Windowsのリモートデスクトップ接続アプリからUbuntuに接続する方法](https://linux.just4fun.biz/?Ubuntu/Windows%E3%81%8B%E3%82%89Ubuntu%E3%81%AB%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B)
・[[windows + Linux]RDP + Ubuntuを使った開発環境構築](https://orenda.co.jp/blog/1874/)
・[Ubuntu20.04でリモートデスクトップ](https://note.com/ogs_digilife/n/n7310daa4252b)

~~割と上記サイトを参考にすれば普通に接続できるようになるのでざっくり記事書きますね。~~
#作業
###XRDPのインストール
ではまず、用意したUbuntuにWindowsからリモートデスクトップできるようにするためのXRDPをインストールします。

```
$ sudo apt install xrdp
```
###XRDPの設定
以下のコマンドをコピペして実行するか、.shファイルにして実行してください。

```
cat <<EOF > ~/.xsessionrc
export GNOME_SHELL_SESSION_MODE=ubuntu
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop
export XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
EOF
```

先程同様、下記コマンドも実行してください。

```
sudo sed -e 's/^new_cursors=true/new_cursors=false/g' -i /etc/xrdp/xrdp.ini

cat <<EOF | \
sudo tee /etc/polkit-1/localauthority/50-local.d/xrdp-color-manager.pkla
[Netowrkmanager]
Identity=unix-user:*
Action=org.freedesktop.color-manager.create-device
ResultAny=no
ResultInactive=no
ResultActive=yes
EOF

sudo systemctl restart xrdp
sudo systemctl restart polkit
```
これでxrdpの設定は終わりです。
#IPアドレスの確認
もちろん接続するためにはIPアドレスを必要とするのでUbuntuのIPを確認しましょう。

```
ip -4 a
```

そうすると

```
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.1.120/24 brd 192.168.1.255 scope global noprefixroute enp2s0
       valid_lft forever preferred_lft forever
```

と出ます。この値をメモを取るなりなんなりしておきましょう。
ほんとLinuxのIPコマンドって見づらいですよね（#＾ω＾）
これでUbuntu側の設定は終わりです。
#Windowsから接続してみる
Windowsに搭載されている**リモートデスクトップ接続**を起動します。
![rd.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2364626/55026023-6a76-4981-5859-946f71e402c0.png)
ここに先程のIPアドレスとUbuntuのユーザー名とパスワードを入力します。画像では一度ログインしてるので資格情報が記録されています。
接続がうまくいくと以下の画面になります。
![dd.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2364626/585e50f6-2a1e-7228-c010-be37962c0ff7.png)
ここでまた同じユーザー名とパスワードを入力すればUbuntuの画面に入れます。
#最後に
**ログイン後真っ黒の画面になった場合**、ほかの場所からログオンしっぱなしの時に起こる現象なのでログオフするか、SSHでログオフするコマンド送ってください。
Ubuntu触りたての初心者ですと何か所か躓くところがありますが、それも勉強なので自分で調べて解決する能力をつけましょう。自分も最初RDPするときはこんなことでも躓きました。頑張ってね☆
クラスチャットを作る #1

# はじめに
クラスチャットを作りたい方、いないとは思いますが、一応備忘録としてここに書いておきます。

:::note warn
必ずしも動くとは限らないので自己責任でお願いします。
:::

# 使用言語・ライブラリ等
言語は

- PHP
- JavaScript
- HTML
- CSS

てな感じで、ライブラリは

- jQuery

コレだけ。

# サービスの形態

- マネージャシステム(管理・運用)
- リアルタイムチャット
- 課題・アンケート機能
- チャネル機能
- ログイン機能(クラスの人一人一人にアカウントを付与)
- タイムライン機能

みたいな感じです。
クラスの人に好評だったらこれを元にSNSサービスでも作ろうかと。

:::note warn
いかにもできそうな感じで書いてはいますが、全然できないかもしれません。
:::

# おわりに

クラスチャットの記事は一旦寝るので中断。また明日出します。
好評であればGitHubで公開でもしようかな。
NotionでAsanaのプレビューを表示する

アルプ株式会社でエンジニアをしています、集約のエンティティ@pictinyです。
これは[Notion Advent Calendar 2021](https://qiita.com/advent-calendar/2021/notion) 14日目のエントリです。
13日目は[NotionでSlackのプレビューを表示する](https://qiita.com/pictiny/items/7e7c6e19801fc5cbcd6e)でした。

# Asana連携
Notionでは認証が必要なコンテンツのURLでブックマークを作ると、faviconやドメインが展開されるだけでコンテンツの中身が展開されません。
NotionとAsanaを連携すればブックマークの作成時に認証を突破してプレビューを表示することができます。

AsanaのURLをペーストすると表示される `Connect to Asana to update` のボタンを押すと、Asanaの認証画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/a4ad60d6-885b-2ffa-8947-e74ce7fd8401.png)

認証したいアカウントでログインするとAsanaと連携でき、連携後にAsanaのURLをペーストするとプレビューが表示されます。

# まとめ
Asanaのプレビューも便利なので、使っていれば連携を済ませておきましょう！
作業時間合計算出 Excel作成


2つシートを作成します。
一つは「作業時刻入力」、もう一つは「作業別合計時間」のシートとなります。

###§1　「作業時刻入力」シート作成
◆手順◆
1.シート名を「作業時刻入力」に変更する
2.B2に作業内容、C2に開始時刻、D2に終了時刻、E2に時間
　と入力する。
3.セルをB2からE22を選択後、上部の「挿入」タブにある「テーブル」を押下
　→「先頭行をテーブルの見出しとして使用する」にチェックを入れる


###§2　「作業別合計時間」シート作成
◆手順◆
1.シート名を「作業別合計時間」に変更する
2.B2にタスク一覧、C2に合計時間
　と入力する。
3.セルをB2からC20を選択後、上部の「挿入」タブにある「テーブル」を押下
　→「先頭行をテーブルの見出しとして使用する」にチェックを入れる


###§3　「作業時刻入力」シート：プルダウン
業務内容の列をプルダウンにし、作業別合計時間のタスク一覧の列の値を選択できるようにする
◆手順◆
1.B2からB22を選択後、上部の「データ」タブにある「入力規則」を押下。
　　許可：リスト
　　元の値：```=作業別合計時間!$B$3:$B$20```
　と入力し、OKを押下

###§4　「作業時刻入力」シート：作業時間算出
1.C3に9:00、D3に10:00と入力する
　（自動的に時刻表示のセルになるはず。。。ならない場合は手順3を参考）
2.E3に以下の数式を入力
　　```=D3-C3```
3.E3が時刻表示(hh:mm)にならない場合は、
  「ホーム」タブにある[セルの書式設定]ダイアログボックスで「時刻」を選択することで時刻表示になる 
　参考：https://support.microsoft.com/ja-jp/office/日付または時刻として数値の書式を設定する-418bd3fe-0577-47c8-8caa-b4d30c528309


###§5　「作業時刻入力」シート：コピペ作業〜完成
1.B3からE3を選択してコピー
2.B4からE22を選択してペースト
！！！！！「作業時刻入力」シートは完成！！！！！


###§6　「作業別合計時間」シート：合計時間算出〜完成
1.C3に以下の数式を入力
```=SUMIF(作業時刻入力!$B$3:$B$22,B3,作業時刻入力!$E$3:$E$22)```
2.（§3の3と同じ手順）
　C3が時刻表示(hh:mm)になっていない場合は、
　ホーム＞[セルの書式設定]ダイアログボックスで「時刻」
2.C3をコピー
3.C4からC20を選択してペースト
！！！！！「作業別合計時間」シートは完成！！！！！

＜補足＞```$B$3```の```$```は固定値となるのでコピペをして他のセルに貼り付けても変わらない
　　　　逆に、貼り付けた列に合わせてB4のように値が変わってほしい場合には$をつけない

##使い方
①「作業別合計時間」シートの「タスク一覧」の列に、作業内容を列挙してしてください
②「作業時刻入力」シートの「作業内容」をプルダウンから選択して、開始時刻と終了時刻を入力してください。
　★Windowsの場合は「Alt」＋「↓」、Macの場合は「Option」＋「↓」でプルダウンの内容が表示されます。
③合計時間は自動的に算出されます！！


##やっとくといいこと

作業別合計時間のシートにある合計を計算したい場合には、
1.「作業別合計時間」のC22に```=SUM(C2:C20)```と入れる
2.（§3の3と同じ手順）
　C22が時刻表示(hh:mm)になっていない場合は、
　ホーム＞[セルの書式設定]ダイアログボックスで「時刻」

<img width="330" alt="スクリーンショット 2021-12-15 1.12.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950877/b2d971e1-24d2-881a-8df8-374c6cfa0452.png">
<img width="400" alt="スクリーンショット 2021-12-15 1.13.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950877/70e5d519-a295-b93e-e248-b27b305c4c0a.png">
Ruby で  Thread.new してるコードを RSpec 実行時だけ同じスレッドで実行させる

```ruby:some_klass.rb
class SomeKlass
  def main
    do_something

    Thread.new do
      do_something_heavy
    end
  end
end
```

こんな風に `Thread.new` してるコードがある時、テストではスレッドを分けずに同じスレッドで処理をさせたくなることがある。そんなときは `Thread.new` を何もしないように `allow` してしまう。

```ruby:some_klass_spec.rb
describe SomeKlass do
  describe '#main' do
    subject { described_class.new.main }

    # Thread.new をただブロック実行するだけにする
    before { allow(Thread).to receive(:new) { |&block| block.call }
    
    it { is_expected.to eq 'something' }
  end
end
```
NotionでSlackのプレビューを表示する

アルプ株式会社でエンジニアをしています、集約のエンティティ@pictinyです。
これは[Notion Advent Calendar 2021](https://qiita.com/advent-calendar/2021/notion) 13日目のエントリです。
12日目は[NotionでGitHubのプレビューを表示する](https://qiita.com/pictiny/items/0d228f163bddcdda7430)でした。

# Slack連携
Notionでは認証が必要なコンテンツのURLでブックマークを作ると、faviconやドメインが展開されるだけでコンテンツの中身が展開されません。
NotionとSlackを連携すればブックマークの作成時に認証を突破してプレビューを表示することができます。

SlackのURLをペーストすると表示される `Connect to Slack to update` のボタンを押すと、Slackの認証画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/2a4c0965-e578-5240-7188-4bb68933dd10.png)

認証したいアカウントでログインするとSlackと連携でき、連携後にSlackのURLをペーストするとプレビューが表示されます。

# まとめ
Slackのプレビューもとても便利なので連携を済ませておきましょう！
メールデータのアラート化 (Mail Integration)

概要
---
OpsRampでは、メールデータをアラート化して取り込む機能”Email Alerts”が用意されています。
他の監視ツールからのアラートメールや、運用監視センター等からの障害通知メールを
OpsRampにアラートとして取り込み一元管理するといったことが出来るようなります。

”Email Alerts”機能の設定
---
まずは、”Email Alerts”を有効化します。

　≪インストール≫
　１．OpsRampにログイン後、[Setup] > [インテグレーション] の画面に移動します。
　２．”利用可能なインテグレーション”の一覧の中から、”EmailAlert” を選択します。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/47a425aa-a438-3089-0ae3-02c11bce018c.png)
　３．”Install Email Alerts Integration”が表示されるので、任意の名称を入力して[Install]を選択します。
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/ea61f324-eef6-af93-45fe-2fceedd132a1.png)
　４．インストールされるとメール受信用のメールアドレスが生成されます。
　　　(このアドレスに対して取り込みたいメールを送信する形となります。)
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/44d261ed-4dc5-5b1a-7121-3968de6dca80.png)

　≪取り込み条件のルール作成≫
　メールデータのどの部分をアラートとして取り込むかのルールを作成します。
　ルールの条件には、正規表現も利用することが可能です。

　１．[Create Email parsing Conditions] を選択します。
　２．設定メニュー(Parsing Conditions)が表示されます。
　３．下記項目を入力します。

　　　　　Parsing Rule Name：任意の名称
　　　　　Rule Starts With：ルールが適用される条件を設定
　　　　　Alert State：アラート化した際のステータス項目の値を指定
　　　　　Device Host Name：アラート化した際のホスト名を設定
　　　　　Service Name：アラート化した際のメトリクス値を設定

　　　今回は必要最低限の設定とします。

　　　例）Parsing Rule Name：Sample Parsing Rule
　　　　　Rule Starts With：Email Subject Contains TEST
　　　　　　　　　　　(→ 件名に”TEST”の文字列が含まれている場合)
　　　　　Alert State：Default Value Critical
　　　　　　　　　　　(→ このルールに一致した場合、Criticalを設定)
　　　　　Device Host Name：Email Content Between ホスト名: / 内容
　　　　　　　　　　　(→ 本文中の”ホスト名:”～”内容”の間の文字列をホスト名とする)
　　　　　Service Name：アラート化した際のメトリクス値を設定
　　　　　　　　　　　(→ 本文中の”()”の間の文字列をメトリクス名とする)
![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/0fce4d64-3970-f068-0480-9312e280e25d.png)
　４．条件に一致しなかった場合用のデフォルトセットを設定します。
![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/305afd48-a6e8-2639-cca3-ea98b4035401.png)
　５．”Validation”にて、Sample Subject(件名)とSample Content(本文)にテスト用の文字列を入力し、
　　　[Validate]を選択することで条件の動作確認をすることが出来ます。
![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/ffe0bc0a-eb13-018d-c791-2c4abefe2630.png)
　６．[保存]を選択し、設定を保存します。

以上で”Email Alerts”機能の設定終了です。

Email Alerts機能の設定 
---
では、実際にメールを送信してアラート化されるか試してみます。

　1．確認用にアラート化したいメールを用意します。
　2．送信先メールアドレスは、”Email Alerts”設定時に生成されたものを指定します。
![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/9a437e1f-35ab-1186-0916-e9abc81525a3.png)
　3．メールを送信します。
　4．アラート画面にて、取り込んだメールがアラートされていることを確認できます。
![009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/a6666751-9b94-ac46-ae37-f86d186d788c.png)

ログ確認
---
”Email Alerts”の設定画面の”Audit Logs”タブを選択することで、
メールの取り込み時のログを確認することも出来ます。
![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345641/c705d61d-0ec1-e447-de2f-5f2d58c0dc4c.png)


以上となります。
簡単に他からアラートを取り込むことが出来る反面、送信されているメール文のフォーマットが変更されてしまったりすると、
条件に一致せずに取り込めなくなることもあるので、注意が必要です。
sudoで実行したいnpmと開発で使いたいnpmを分ける

考え方の話なので、rubyでも同様にできます。
ここでは書いてませんが、**github_changelog_generator**と開発のrubyを分けてます。

## やりたいこと
- **github-changes**を実行するnpmはsudoを使う
- 開発中のnpmのバージョンは最新にしたい
- WSLで完結させる

## 結論
- `sudo su -`でrootになってからnpmをインストール
  - **/root/.nodenv**などにインストールしても良いし、拘らなければ`apt install npm`でも良い
- 別途nodenv等を使って入れたnpmにパスを通す
- `sudo which npm`と`which npm`の結果が異なっている

### 注意
コマンドに直接`npm`と掛ける場合は、`which npm`の結果がsudoとそうでない場合でパスを分ければよい、つまりrootで使えるnpmのフルパスと、ユーザーや環境ごとに使いたいnpmのフルパスを明示的に書いて分けるべきです。
なので、シンプルに`npm install パッケージ`とするなら`$(sudo which npm) install`か`$(which npm) install`で書くべきです。[^1]
[^1]: 【`$(sudo which npm)`と`$(which npm)`】説明のために端折ってますが、正しくフルパスで書きます

今回の要件はgithub-changes`内で使われているnpmを指定したいのと、開発で使っているnpmのバージョンを分ける必要があったので、運用中の`sudo npm`専用のコンテナや開発のための専用コンテナなりを用意するのがベストですが、個人開発なのでわがままセットな環境を作りたかったのでこういうやり方をしてます。

## 前提
いつもどおり、WSLを使ってます。
開発で使うnodeと運用で使うnodeを分ける必要があったのですが、

- 運用で使用するnodeにsudo権限が必要なパッケージがあった
- 開発で使うためのnodeのバージョンは管理しておきたい

という要件を満たす必要があったので、これを解決する方法を探していました。

## やり方
結論にも書いてます。**nodenv**をrootでインストールとuserでインストールの-２回分でやればいいです。
ただし、インストールする場所は当然変える必要があります。

nodenvはそれぞれの`~/.nodenv`に作成されるので、
ちょっと検証してみたんですが、anyenv自体のバージョンを分ける必要はないので**/opt**に置いて検証したところ共通のanyenvでそれぞれのnodenvが作られるので、anyenvを分ける必要はなさそうです。[^2]
[^2]: 【anyenvを分ける必要はない】よく考えれば、linuxbrewを入れた時は**/home/linuxbrew**に作られ、ユーザー問わずlinuxbrew以下を参照するので、共通のanyenvを使っている事になりますね。

## コマンド例
思いついたらちょこちょこ直しているので、Githubを貼り付けておきます

https://github.com/shimajima-eiji/Settings_Environment/blob/main/for_clean_install/WSL_setup.sh

### 注釈
transformersのTrainerでもにょった

# はじめに
payanottyです。
transformersのTrainerを使ってて軽くもにょったことがあったので書いてみたいと思います。

# もにょったこと

transformersはHugging Face社が提供している深層学習ライブラリです。
自然言語処理に特化していて、pythonで自然言語処理をするなら定番のライブラリになっています。

[model hub](https://huggingface.co/models)には無数に事前学習済みモデルが登録されており、これらの中から好きなモデルをロードしてきてファインチューニングすることができます。

ファインチューニングの際に自分で学習処理を書くのもよいのですが、transformersの中で提供されているTrainerクラスを使うととても簡単です。

```python
from transformers import TrainingArguments
from transformers import Trainer

training_args = TrainingArguments()

trainer = Trainer(
    model=model, args=training_args, train_dataset=small_train_dataset, eval_dataset=small_eval_dataset
)
```

Trainerクラスで便利な機能として、Callbackというものがあります。
これは学習中に行いたい処理をCallbackクラスとして実装しておき、Trainerの引数にそのCallbackのインスタンスを指定することができるというものです。

```python
class MyCallback(TrainerCallback):
    "A callback that prints a message at the beginning of training"

    def on_train_begin(self, args, state, control, **kwargs):
        print("Starting training")

trainer = Trainer(
    model,
    args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    callbacks=[MyCallback]  # We can either pass the callback class this way or an instance of it (MyCallback())
)
```

筆者はTrainerクラスを使って事前学習を回そうとしていて、学習の経過をMLflow serverに記録したいと考えていました。
少し調べてみるとMLflowCallbackはすでにあるということで、それを継承して少し処理を書き換えたCallbackを実装すれば良さそうだと思いました。

```python
class MyMLflowCallback(MLflowCallback):
    """
    なんかしら処理
    """
```

こんな感じでCallbackを入れて実験を回してみました。
MLflow UIで実験経過を確認してみると、なるほど、Callbackに実装したとおり、きちんとメトリクスが記録され、アーティファクト保存も問題なさそうです。
でもよく見ると、experimentが2つできてました。
そのうちの1つは自分で設定したものなので良いのですが、もう1つの方は何故か勝手にできていて、自分が設定した覚えのないメトリクスが記録され続けていました。

なんで勝手にexperimentできてんのきもきもきもきも、もにょ～～～～？ってなりました。

# なんだったのか

実はTrainerにはデフォルトでMLflowCallbackが指定されており、MLflowがインストールされている場合は勝手にこのMLflowCallbackの処理が行われる、という仕様になっていました。
なので、デフォルトのCallbackが作ったexperimentと、自作のCallbackが作ったexperimentとで、それぞれが独立して走っていたというわけでした。
今回のように自作のMLflowCallbackを使いたい場合は、以下のようにしてデフォルトのCallbackをTrainerから削除することを推奨します。

```python
# デフォルト消して
trainer.remove_callback(MLflowCallback)
# 自作のを入れる
trainer.add_callback(MyMLflowCallback)
```

MLflow serverにアーティファクトを送りたかったり、モデルレジストリにモデル登録したかったり、デフォルトのMLflowCallbackではちょっと足りないことをしたいことはあると思うので、このことは覚えておくともにょらなくて済むと思います。

# もにょるってなんだよ
**わた天を見ましょう。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488057/02f6206d-cacf-aaf5-f700-506cc6ffca42.png)
©椋木ななつ・一迅社／わたてん製作委員会
前へ次へと移動できる動画再生機能を実装した

この記事は [驚異のFANZA女優検索](https://fanza-actress-bust-search.azurewebsites.net/) Advent Calendar 2021 の 15 日目の記事です。

# 動画再生
FANZAと言えば動画。動画再生に関しては本家にもない機能が欲しくて自分で作った。このサービスの中で最も頑張った所の一つだ。

# iframeで動画再生
女優APIの中に動画のリンクがある。htmlでiframeを使って動画のリンクをsrcに書けば、これでもう動画は再生出来る。簡単。動画をHPに置いて再生出来るようにしたいだけならこれで十分。実際作品ページはこれで実装している。

```cs
<iframe src="@(Model.SampleMovieSpURL)"></iframe>
```

# 沢山の動画がある場合は再生時に実体化する
女優ページのように作品一覧があって、その作品毎に動画がある場合、全部iframeで置くのは問題がある。iframeを複数に設定すると単純に重い。作品数が多ければもう激重ぷんぷんまるだ。[複数iframe 重い](https://www.google.com/search?client=firefox-b-d&q=%E8%A4%87%E6%95%B0iframe+%E9%87%8D%E3%81%84)でググると一杯出てくる。ポピュラーな方法はiframeをsrcからdata_srcに変えてクリック時にjavascriptでsrcに戻す方法だ。これで一覧で並んでいても実体はクリックした時だけになる。

```cs
<iframe data-src="@(Model.SampleMovieSpURL)"></iframe>
```

```js
//javascriptでdata-srcをsrcに変える
element.setAttribute('src', attribute);
```
# lityでポップアップ
どうせクリック時に再生する仕組みにするなら、再生ボタンを置いてそれをクリックするとポップアップして再生したいと思った。調べた感じだと、[Lity](https://sorgalla.com/lity/)を使うと良い感じにポップアップ再生してくれそうなので使ってみた。まあ悪くない感じになった。

# 動画再生でポップアップで次へ
ただ静止画はクリックするだけで次の画像に行くけれど、動画はクリックして次の動画に行く機能がない。これは本家FANZAにもないけれど、個人的にはポップアップ再生時に次へ行く機能が欲しいと思った。思ったので作った。Lityを改造するよりも、もうポップアップ部分を全部書いた方が良さそうだと思ったので、ゼロから作ることにした。

# 次へ前へ移動できる部分を自作
実際に次の動画をクリックする外見はhtmlで次へと前への hrefを作るだけで良い。ただクリック時の実装のデータの仕込みはサーバー側でC#でやっている。先ず次へを実装するには、1動画ずつ前への動画と次の動画を設定する必要がある。全ての作品に動画があればprevに1個前、nextに1個後を足せば良い。後は最初の動画の前は最後の動画、最後の動画の次は最初の動画とすれば完成だ。ただ、全作品に動画があるわけではないので、prevが1個前とは限らない。動画があるものだけで前へ次へを作らればならない。後は実際にクリックされた時にjsで新しい動画を読み直している。先読みすると不安定になるのでクリック時に今の動画は破棄して、クリックされた動画を作っている。

# 動画サイズが複数ある
さて、ポップアップ再生の仕組みは、これ以外にも問題がある。DMM.comの[商品検索API](https://affiliate.dmm.com/api/v3/itemlist.html)を見ると、動画は4サイズ分用意されている。つまり画面サイズによって動画サイズは変える方が望ましい。特にスマホは大きい動画だと横が切れてしまう。そこでサイズを変えたらそのサイズに最適な動画になるようにした。CSSを使うことによってレスポンシブに出来る。この方法はYoutubeのように動画サイズが決まっている場合にうまくいく。これを作る時まで気づいていなかったがDMMが提供する動画のサイズは結構マチマチだ。

# iframeレスポンシブ
色々cssを設定して試してみたが、どんなサイズでも良い感じにやるのは無理だった。とりあえず大きい動画サイズ、横720と560はそのまま出して、それ以下の場合は、positionを使う方法が一番マシにはなった。これでも元々も動画のサイズによって、またブラウザのサイズによって黒い帯が広かったりしてイマイチだったりするが、これ以上は分岐で個別対応していくしかないかなーと言う感じだ。

```css
.iframeMovieL {
    width: 720px;
    height: 480px;
}

.iframeMovieM {
    width: 560px;
    height: 360px;
}

.iframeMovieS {
    position: relative;
    width: 100%;
    padding-top: 64.28%;
}

.iframeMovieS iframe {
    position: absolute;
    top: 0;
    right: 0;
    width: 100%;
    height: 100%;
}
```
# 他の画像読み込みなどがあると動画再生は不安定になる
また動画は様々な原因で不安定になる。例えば同一ページに画像が沢山ある場合、全画像を読み込むまで動画再生は安定しない、再生出来たとしても止まるみたいなこともある。これを回避する方法は画像を減らす以外はないと思う。実際にこのために一覧表示の最大表示数を減らした。ネットワークが遅いとさらに問題があるかもしれない。

# 一度に複数の動画を実体化すると不安定になる
またブラウザの画面サイズを変えた場合に動画もそのサイズ合わせたものに変わるようにしていたが、これをするには複数のサイズの動画を実体化する必要がある。しかし2個以上実体化すると不安定になる。なのでサイズ変更時は動画がなくなるのを許容した。

# 次の動画のビジュアルが欲しくなった
で、一応完成して、動画も安定化してきたので、実際に使ってみたら、やっぱり次へ前へ移動は便利だった。ただ不満も出てきた。折角前や次の動画に行けるのに、その動画がどんな内容か分からない。つまり文字だけでなくビジュアルが欲しくなった。そこで前へ次へと表記するだけでなくパッケージも置くようにした。さらに前へ次へだけでなく10個ぐらいから選べるようにもしたくなったが、今回はそこまではやっていない。

# まとめ
動画はかなりデリケートで大量の女優を相手にしたり、複数同時に相手にしたりするとすぐに機嫌が悪くなる。やはり動画は一人だけに注力してその女優のことだけを考えないと怒られるようだ。というわけで前へ次へと移動できる動画再生機能を是非使ってみてほしい。くれぐれも浮気はしないように。
なでしこでアナログ時計を作る

人はなぜ「アナログ時計」を作りたくなるのでしょうか？
それは、そこに円や線を描画できるプログラミング言語があるからです！

# タートルグラフィックスでアナログ時計を描画する

既に、アナログ時計の作り方は、いろいろなところで紹介しています。
例えば、以下はタートルグラフィックスの機能を使った、アナログ時計の作り方を紹介しています。

- [マイナビなでしこ連載26回目](https://news.mynavi.jp/article/nadeshiko-26/)

そこで、今回は、上記を改良しつつ、タートルグラフィックスを使わないで作ってみましょう。

# SIN/COSで時計の盤を描く

アナログ時計作りに欠かせないのが、SIN関数とCOS関数です。
SINとCOSは三角関数です。でも、時計を描画するだけなら、あまり難しいことを考える必要はありません。これを使うと、針の角度に応じた座標を調べることができることが分かればOKです。

まずは、以下のようなアナログ時計の盤を描画してみましょう。

![スクリーンショット 2021-12-15 0.03.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33180/973d617d-f2be-1e83-8654-e44147680542.png)

上記の盤を描画するのが、以下のプログラムです。

```盤を描く.nako3
# 時計のサイズを指定 
CW=300 # サイズ
CX=CW÷2 # 中心点
CY=CW÷2
盤描画。

●盤描画とは
　　# 盤背景
　　全描画クリア。
　　5に線太設定。
　　白色に塗色設定。
　　黒色に線色設定。
　　[CX,CY]へCXの円描画。
　　# グリッド線
　　長さ＝CX×0.99
　　12回
　　　　角度=(回数-1)/12×360
　　　　赤色の1を長さと角度で針描画
　　ここまで。
ここまで。

●（色のサイズを長さと角度で）針描画
　　角度＝(角度-90)%360;
　　色に線色設定。
　　サイズに線太設定。
　　X=長さ×COS(角度÷180*PI)
　　Y=長さ×SIN(角度÷180*PI)
　　[CX,CY]から[X+CX,Y+CY]へ線描画。
ここまで。
```

# 時計を完成させる

あとは、1秒おきに針の角度を指定して針描画を呼べば時計の完成です！

```アナログ時計.nako3
# 時計のサイズを指定 
CW=400 # サイズ
CX=CW÷2 # 中心点
CY=CW÷2

時計更新。
「時計更新」を1秒毎。

●盤描画とは
　　# 盤背景
　　全描画クリア。
　　1に線太設定。
　　白色に塗色設定。
　　黒色に線色設定。
　　[CX,CY]へCXの円描画。
　　# グリッド線
　　長さ＝CX×0.99
　　12回
　　　　角度=(回数-1)/12×360
　　　　灰色の1を長さと角度で針描画
　　ここまで。
ここまで。

●（色のサイズを長さと角度で）針描画とは
　　角度＝(角度-90)%360;
　　色に線色設定。
　　サイズに線太設定。
　　X=長さ×COS(角度÷180*PI)
　　Y=長さ×SIN(角度÷180*PI)
 [CX,CY]から[X+CX,Y+CY]へ線描画。
ここまで。

●時計更新とは
　　盤描画。
　　# 現在時刻を得る
　　今を「:」で区切る。
　　変数[時,分,秒]=それ
　　# 角度計算
　　時角度＝(時%12)÷12×360+((分/60)×(360÷12))
　　分角度＝分÷60×360
　　秒角度＝秒÷60×360
　　# 針を描画
　　黒色の12を(CX×0.5)と時角度で針描画。# 時
　　青色の8を(CX×0.8)と分角度で針描画。# 分
　　赤色の4を(CX×0.95)と秒角度で針描画。# 秒
ここまで。
```

上記をなでしこ貯蔵庫で実行すると、次のような時計が表示されます。

![スクリーンショット 2021-12-15 0.16.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33180/29b25662-ee90-c31c-ee4a-730e867c5105.png)

[こちらですぐに試せます](https://n3s.nadesi.com/id.php?871)

楽しいですね！
針の描画方法をもう少し工夫すれば豪華な時計になります。

ぜひ、挑戦してみてください！
github初回のコミット

# github初回コミット時



githubコミット時にSSHの設定やらで困ったので記載しておきます。

## はじめに

ブラウザのgitHub上で確認したところ、下記のようなコミット手段があるようです。

![ssh_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67358/4dd24ed2-7b0b-edd5-c6e5-b3eb00eee717.png)

- HTTPS
- SSH
- GitHub CLI

GitHub CLIは何かしら特別なツールをインストールしなければいけない雰囲気が漂っているので避けました。

まずは、HTTPSでCloneを試してみようと思いましたが・・・

## HTTPS

データを取得するために、下記のコマンドを実行します。

`git clone https://github.com/<ユーザ名>/<リポジトリ名>.git`



ユーザ名とパスワードを聞かれるため、ユーザ名とパスワードを入力しました。

#### エラー発生

```
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/<ユーザ名>/<リポジトリ名>.git/'
```

最初の箇所のみ訳すると、パスワード認証については2021年8月13日からできなくなりました。
代わりにアクセストークンを取得してくださいとのこと。

下記のgitHubのドキュメントからアクセストークンについては取得できるようです。
[アクセストークンの取得方法](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)

画面としては「右上のアカウントマーク -> Developer settings -> Personal access tokens」から
アクセストークンを取得することが出来ます。
![アクセストークン取得画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67358/e416b058-5080-14bd-827a-195af4490fd6.png)
設定項目を見ると、**有効期限**があります。

設定値として「No expiration」(有効期限なし)を設定することも可能ですが、

**注意事項**が出てきて、有効期限を設定することを強く推奨する。と訴えてきます。

``` 
GitHub strongly recommends that you set an expiration date for your token to help keep your information secure. Learn more
```



有効期限が気にならない人は、上記の手順でHTTPSを利用してください。



## SSH

SSＨを利用して、通信を行います。

### SSH鍵の作成
※Windowsの人はsshがデフォルトだとインストールされていません。
※そのため、「設定 -> アプリ -> アプリと機能 -> オプション機能 -> 機能の追加」からOpenSSHクライアントにチェックを入れてインストールしてください。


まず、[SSH鍵の作成](https://docs.github.com/ja/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)を参考にしつつ、SSH用の鍵を作成します。
Cオプションはただのコメントの為省略しています。

`ssh-keygen -t ed25519`

**ed25519をサポートしていない場合**は下記のコマンドを実行。

`ssh-keygen -t rsa -b 4096`



**ssh-keygenのオプション**

| オプション | 説明                                                       |
| ---------- | ---------------------------------------------------------- |
| t          | 暗号化の方式(デフォルトはrsa)<br />rsaやdsa、ecdsa等が選択可能。 |
| b          | bit数。長いほど強固な鍵となる。                            |
| C          | コメント                                                   |
| f          | ファイル名（オプションの組み合わせで意味合いが変わるので注意が必要    |

コマンドを実行するといくつか質問が聞かれるので、適宜回答していきます。
※過去に鍵を作成したことがある場合、fオプションを利用して鍵名を変更して作成してください。
　(鍵が存在するかどうかは、`<ユーザフォルダ>¥.ssh`配下に鍵ファイルがあるか否かで判断してください。)

### SSH鍵のGitHubへの登録

説明を書こうと思っていましたが、公式が優秀なのでリンクだけ張り付けておきます。

[鍵の登録](https://docs.github.com/ja/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)
![ssh_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67358/4dd24ed2-7b0b-edd5-c6e5-b3eb00eee717.png)
あとは、自分のリポジトリに移動し上記のSSHを確認し、下記のコマンドを実行します。

`git clone <SSHの内容>`

上手くいけば問題無くCloneされます。



# 備考

通信速度自体はhttpsの方がsshより早いため、大きなリポジトリからcloneする場合、
一時的にアクセストークンを取得してhttpsを利用したほうが良さそうです。
かいはつ小話「モックをくださいな」

この記事は [株式会社サイバー・バズ Advent calendar 2021](https://qiita.com/advent-calendar/2021/cyberbuzz) 15日目の記事です。

むかし、むかしあるところに...

フロントエンドおばあさんとバックエンドおじいさんがおりました。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/d7a81439-57b5-b4f8-3167-ffa1bd21e20f.png)




今回の開発は郵便番号でリクエストを投げると、住所が返ってくるというものです。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/f25b220c-e7fa-efcd-4459-c9d95e9bf8c7.png)



**おばあさん**「フロントのフォーム部分の実装終わりましたよ...」

**おじいさん**「すまんな、ばあさん。まだAPIの実装終わっとらんのじゃ...」

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/245bcce1-0780-5c9f-ad26-23f78ee07ed9.png)


このままではフロントエンドおばあさんの開発が止まってしまいます。

**おじいさん**「ばあさんからのリクエストとレスポンスの型だけは決まっとるんじゃよ...」

**おばあさん**「じゃあとりあえずモックだけ返してくださいな...」

**おじいさん**「はいよ」

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/0f06bcb6-aa66-1a5f-d9d2-a662fd0ab19b.png)


**おばあさん**「hogeしか入ってないじゃありませんか...もう少し本物っぽいものを返してくださいな...」

**おじいさん**「...」

**おじいさん**「はいよ」

**おばあさん**「これですよ、これ...」

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/dc14e199-df80-97da-113f-f60a10c8cded.png)


**おばあさん**「しかも毎回違うデータが返ってくるではありませんか...」

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/77401bea-acba-86f9-7f36-156d851e5828.png)

**おばあさん・おじいさん**「.....」

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620439/56e43f6a-efe8-7e30-364f-054c6cca672d.png)

# 振り返り
おばあさん、おじいさん良かったですね。

今回の使用技術は以下です。

- Node.js
- Apollo
- GraphQL

Apolloについてはこちらの記事が参考になります。
https://qiita.com/jintz/items/4ddc6bf4f95238eff5e9

`ApolloServer`を新しく立てるところで、`mocks`を与えています。

`mocks`を与えてあげることでモックデータを返すことができるようになります。

```typescript:index.ts
export const apolloServer = new ApolloServer({
  schema: schemaWithResolver,
  mocks
})
```

実際に`mocks`を作成している箇所がこちらです。

```typescript:mocks.ts
export const mocks = {
    Address: () => ({
        zipCode: faker.address.zipCode(),
        zipCodeByState: faker.address.zipCodeByState(faker.address.state()),
        city: faker.address.city(),
        cityPrefix: faker.address.cityPrefix(),
        citySuffix: faker.address.citySuffix(),
        streetName: faker.address.streetName(),
        streetAddress: faker.address.streetAddress(),
        streetSuffix: faker.address.streetSuffix(),
    })
}
```

`mocks`のところでは、`faker.js`というものを使っています。
https://www.npmjs.com/package/faker

住所、会社情報、日付、クレジットカード情報など、様々なモックデータを作成してくれます。

また、使うたびにそのモック内容を変更してくれるので、様々なデータでテストすることができます。

# まとめ
リクエストとレスポンスの型さえ決まっていれば、先にモックだけでも返しておくのが良いかもしれません。

そうすればフロント開発が止まることが少なく、全体的な開発速度の向上も期待できます。

そこに`faker.js`を使うことで、モックデータを用意する手間が省けてより実データに近いものが準備できるというお話でした。

最後まで読んでいただきありがとうございました。

[リポジトリ](https://github.com/YuyaIida/graphql-server-faker)
【Excel】【ショートカット】表内の移動 [ctr] [shift]


事務職の友人から質問があったので、誰でも知っていると便利なエクセルの表内のショートカットをまとめる

①矢印キー[↑][↓][←][→]での移動
　矢印を使えば表内をマウスを使わず移動ができる
　マウスよりも細かく移動ができるため慣れると便利

②[ctr]と矢印キーの合わせ技
　[ctr]を押しながら矢印キーを押すと、データの最後まで移動をする。
　
③[shift]と矢印キーの合わせ技
　[shift]を押しながら矢印キーを押すと、複数セルを選択できる。
【Tensorflow】VSCodeのPylanceコード補完がTensorflowのKerasに効かない【VSCode】

# 2022/3/12追記
下記WorkAroundがうまく行かない場合があります。
僕の環境でもなんか気づいたらうまくいかなくなってました。2.7とかそのへんからかもしれません。
その場合は
```python
from keras.api._v2 import keras
```

とするとうまく回避できます。
見た目が気持ち悪いのでうまくpylanceに適合できるようにTensorflow側にPRが立っています。
進行中の議論の内容は[TensorflowのIssue](https://github.com/tensorflow/tensorflow/issues/54435)でご確認ください。

# KerasがTensorflowから独立したらしい
>Keras been split into a separate PIP package (keras), and its code has been moved to the GitHub repositorykeras-team/keras. The API endpoints for tf.keras stay unchanged, but are now backed by the keras PIP package. The existing code in tensorflow/python/keras is a staled copy and will be removed in future release (2.7). Please remove any imports to tensorflow.python.keras and replace them with public tf.keras API instead.

[Tensorflow Release Note Version 2.6.0](https://github.com/tensorflow/tensorflow/blob/master/RELEASE.md)

こうしたいろいろの関係があって内部処理ではTensorflowはKerasをLazyにLoadすることにしました。
これはPylanceなどによる静的型チェックの対象外です。

その結果VSCodeにPython/Pylance拡張を入れてもコード補完などのサポートが受けられなくなってしまうのでした。

# Solution: tf.kerasにimport文をかける

```python
# pylance works!
import tensorflow.keras as keras
layer = keras.layers.Dense(128)

# pylance does not work!
import tensorflow as tf
layer = tf.keras.layers.Dense(128)
```


![Screenshot from 2021-12-14 22-25-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641788/109a7887-38e7-2302-d952-c79b82c2e7a0.png)
![Screenshot from 2021-12-14 22-26-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641788/5e43ba5a-4a08-8485-c127-7d151e61efb3.png)


LazyLoaderによってロードされた`keras`モジュールをPylanceに解析させるには`import`文を`tensorflow.keras`に対して実行する必要があります。

そうするとLazyLoaderが処理されてPylanceは今まで通りのkerasモジュールと解釈してくれるようになるのでした。

## 最後に
最近PythonでもTypeScriptでもTypeHintingアツい！PylanceによるCode補完を使うとVSCodeの画面がカラフルになって楽しい！
LazyLoaderをやっつけてよいTypeHintingライフを〜

それでは最後にLGTMとフォローも忘れずにお願いします！
この世のすべてを手にするプログラム


# はじめに
誰もが一度は考えたことがあるのではないだろうか。
そう。この世のすべてを手にしたいと


#富・名声・力。
この世のすべてを手に入れた男、海賊王ゴールド・ロジャー。
彼の死に際に放った一言は、人々を海へ駆り立てた。


```python
s = '「おれの財宝か？欲しけりゃくれてやる。探せ！この世のすべてをそこに置いてきた！」'
print(s.replace('すべて', '手'))
```

出力結果

```txt
「おれの財宝か？欲しけりゃくれてやる。探せ！この世の手をそこに置いてきた！」
```

# おわりに
この世のすべてを手にしたのに、記事はスベってるって？やかましいわ！

# 参考文献
- [ウィーアー!](https://dic.pixiv.net/a/%E3%82%A6%E3%82%A3%E3%83%BC%E3%82%A2%E3%83%BC%21)
Scalaで一番easyな構成を考えた(autowireの紹介)

この記事は [Scala Advent Calendar 2021](https://qiita.com/advent-calendar/2021/scala) 14日目の記事です。

# Scalaで一番easyな構成を考えたい
Scalaでマスタメンテや運用補助の簡単なアプリを手軽に作りたいときどうしましょう？

### requirements
 - マスタメンテ画面など手軽に作りたい
 - フロントとサーバーが分かれて**ない**ほうがいい
 - フロントとサーバーを統合するのでOpenAPIやTapirはむしろ不要！


### 個人的コンテキスト
しばらくvueのプロジェクトの改善をやっていて、大きく育ったSFCをコンポーネントに切り出すのがつらかった。誰でも安全確実に高速でリファクタリングできないと意味がない！
playframework + twirlは楽だけど **そもそもhttpすらいらない** よねという話をAirframe RPCの話題で聞いていて、もっとシンプルなRPCないかなと探してみた。。。

すると、さすがlihaoyiさんがすでに作ってた！

https://github.com/lihaoyi/autowire

```scala
// shared interface
trait Api{
  def add(x: Int, y: Int, z: Int): Int 
}
```

traitがそのままAPIの定義になる

```scala

// server-side router and implementation 
object Server extends autowire.Server...

```

一行でサーバーになるらしい。すごい。

```scala

object ApiImpl extends Api
  def add(x: Int, y: Int, z: Int) = x + y + z 
}

```

traitを実装するとAPIが完成!

こちらは↓はクライアントサイド

```scala

// client-side callsite

import autowire._ // needed for correct macro application

object Client extends autowire.Client...
Client[Api].add(1, 2, 3).call(): Future[Int]
//         |    |             |
//         |    |             The T is pickled and wrapped in a Future[T]
//         |    The arguments to that method are pickled automatically
//         Call a method on the `Api` trait
```

これだけでRPC(リモートプロシージャーコール)ができる！

#もうサーバーこれでよくないですか？

動くサンプルを用意したかったんですが、先週Twitterで炎上していて準備する時間が足りなくなりました。

ちょうど昨日110416さんがlaminarの紹介してたのでフロントはそちらに譲ります。
https://zenn.dev/110416/articles/997e1932fa0010

LaminarとAutowireの連携については、[Gitterで聞いてみたら](https://gitter.im/Laminar_/Lobby)、
「AutowireはLaminarとうまく連動します。RPCでも何も特別なことはなくてFutureで返ってくるならこんなふうに簡単にできます」
と、コードも共有してくれました

```scala
div(
  Signal.fromFuture(makeRpcCall()) --> myResponseObserver
)
```

or

```scala
val responseSignal = Signal.fromFuture(makeRpcCall())
div(
  child <-- responseSignal.map(...)
)
```
とのことでした。

Laminarを使ったときにデザインってどうしてますか？という感じの質問には
> LaminarはWeb Componentsとの統合を内蔵しているので、とても簡単にWeb Componentsを利用できます。
> また、Laminar用のMaterial UI Webコンポーネントのライブラリがあります。https://github.com/uosis/laminar-web-components 正確には、このライブラリはGoogleのMaterial UI Web Componentsへのインターフェースのセットです。これらのインターフェースは100％完全ではありませんが、良いスタートです。
> 
> CSSライブラリについては、そのほとんどがCSSクラスに基づいており、それはLaminarでも問題なく動作します。
> IuriiはTailwind CSSが好きで、そのライブラリを使ったLaminarコンポーネントのサンプルをいくつか作りました。https://laminext.dev/tailwind 。決して完全なセットではありませんが、どのように実装されているかを見て、良いテクニックを学ぶことができるでしょう。

デザインのオプションが用意されてるのも安心ですね！

今回は時間がなく動作する完全なサンプルを用意できませんでしたが、出来次第共有したいと思ってます。
それではみなさんよいScalaライフを！
Xcode 13 で Vim Mode (Editor Menu for Source Code) を設定してみた

# 背景
Xcode 13 からついに標準で Vim mode がサポートされたので設定の仕方をまとめました。

> Vim mode
> Many common key combinations and editing modes familiar to Vim users are supported directly within the code editor, using the new bottom bar to show mode indicators.

> https://developer.apple.com/xcode/

Xcode 12 までは XVim2 をどうぞ。

> XVim2 is a Vim plugin for Xcode intending to offer a compelling Vim experience without the need to give up any Xcode features.

> https://github.com/XVimProject/XVim2

# 環境

- Xcode Version 13.2 (13C90)
- macOS Monterey 12.1 (21C52)

# 設定
- Xcode 起動後に `command + ,` のショートカットキーで Preference を表示
- Key Bindings メニューを選択して `vim` で絞り込み
    - Key の下の箇所をダブルクリックして `control + [` を入力
- Preference を閉じる
![スクリーンショット 2021-12-14 23.16.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38822/90f998b2-2403-2445-78d4-a381f52d9e55.png)

- 適当にソースコードを表示して、 `control + [`　のショートカットキーで Vim Mode に切り替わる(以下は Vim Mode 中)
![スクリーンショット 2021-12-14 23.17.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38822/ed1c14cb-aada-e139-5810-684303c7dba3.png)
- Vim Mode を抜けるには再度 `control + [` (以下は Vim Mode 解除中)
![スクリーンショット 2021-12-14 23.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38822/11cf620e-4ec6-520f-f08a-8adb3343c116.png)

- Editor のメニューからも Vim Mode の切り替えは可能
![スクリーンショット 2021-12-14 23.42.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38822/5888ebc5-39bb-effa-2135-836cd598bea5.png)
qiita-cliで新規記事作成

 
      
ここから本文を書く
# 🐥 qiita cliによる自動生成です.

上記ハイフンで囲まれた部分(フロント・マター)より下のこちらの部分を編集して記事本文を作成してください.
フロント・マター以下の本文含めた自動生成された部分は削除して構いません. 
自動生成に倣い,フロント・マター以下に1行改行を加え、markdown形式で記載してください

## 記事へのタグ付け

フロント・マターのtagsに以下のように配列形式でtagを記載することが可能です.
投稿するには、一つ以上のtagが設定されている必要があります.
フロント・マターへのtag追記例
```
tags: [{"name":"C++","versions":[]},{"name":"AtCoder","versions":[]}]
```

## ファイル名は変更しないで！

qiita cliはローカル上で新規記事/修正記事かどうかはファイル名により判断します.
`not_uploaded.md`というファイル名はそのままに 🙇‍♂️
【Rails】PATCHとPUTの違い【初学者の疑問点を簡潔に説明】

## はじめに
　本記事は、プログラミングの学習を始めて1ヶ月の初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## 今回の疑問点
　今回の疑問点は、

　　_HTTPメソッドのPATCHとPUTの違いについて_
　
　です。　

 　以前、テーブル定義を考えていた際に疑問を抱きました。

## 疑問点についての解説

### 結論

`PATCH`：既にあるリソースの一部を上書きする。

`PUT`：既にあるリソースを全て上書きする。既存リソースがない場合には作成する。


### PATCH
既にあるリソースについて一部分のみ更新するメソッドです。
例えば、記事のタイトルだけを更新する、記事の本文だけを更新する等の際に使用します。

### PUT
リソースが存在しているときは更新、リソースが存在しない場合は新規作成を行います。

### PATCHとPUTで迷ったら
4.0より前のバージョンのRailsでは`PATCH`ではなく`PUT`が使用されていたようですが、`PATCH`の方が意図したHTTPの使用法により適しているので、新しいアプリケーションでは`PATCH`が推奨されているようです。
Rails4.0以降を使用している場合には、`PATCH`を使用しておけば良さそうです。


## まとめ
最後にポイントをまとめます。

- `PATCH`：既にあるリソースの一部を上書き、`PUT`：既にあるリソースを全て上書き、既存リソースがない場合には作成
- Rails4.0以降では、より意図したHTTPの使用法に適している`PATCH`を使用することが推奨されている。
[Elixir] Phoenix LiveViewでGoogle Analyticsを使う

この記事は [elixir Advent Calendar 2021](https://qiita.com/advent-calendar/2021/elixir) の20日目です。昨日は @piacerex さんでした！

https://qiita.com/piacerex/items/96efdb1377417f942531

---

Phoenix 1.6.0から、 `mix phx.new` でPhoenixプロジェクトを作成すると **LiveViewがデフォルトで有効** になりました :tada: 

LiveViewを知らない方のためにひとことで説明すると、「JSをほぼ書かずElixirだけでSPAを開発できるイカしたやつ」でしょうか。[公式サイト](https://phoenixframework.org/)で紹介されている動画（↓と同じもの）を見ると、LiveViewがどんなものなのか分かりやすいと思います。

https://youtu.be/MZvmYaFkNJI

私は普段LiveViewを使った開発に携わっていますが、LiveView界隈をウォッチしていてもGA（Google Analytics）に関する情報は特に流れてきません。Webアプリを世に出すならいろいろと計測したいものじゃないの…？

まあ、当たり前すぎて誰も話題にしないだけかもしれませんが、LiveViewは [History API](https://developer.mozilla.org/ja/docs/Web/API/History_API) でページ遷移を行うので、GAを使うときは注意が必要です。というわけで、LiveViewでGAを使ってページビューを計測する方法を紹介します。

なお、LiveViewのバージョンは0.16.0以上を想定しています。

# GA4（Google Analytics 4）を使う場合

GA4はSPAでもページビューを計測してくれるため、単にタグを埋め込むだけでOKです。

https://developers.google.cn/analytics/devguides/collection/ga4?hl=ja

# UA（ユニバーサルアナリティクス、従来のGA）を使う場合

新規プロジェクトならGA4を使えばOKですが、そうもいかないケースは多々あります。UAは単にタグを埋め込むだけだとSPAではページビューを計測してくれないため、ページ遷移を捕捉して明示的にイベントを送ってあげる必要があります。

まずはタグを埋め込みますが、その際にデフォルトのページビュー測定を無効化するようにします。

```diff_javascript
- gtag('config', 'UA-XXXXXXXXX-X');
+ gtag('config', 'UA-XXXXXXXXX-X', {
+   send_page_view: false
+ });
```

続いて、ページ遷移を捕捉してページビューのイベントをGAに送信します。LiveViewでは、ページのロード開始時に `phx:page-loading-start` 、ロード終了時に `phx:page-loading-stop` というイベントが発生しますが、Phoenixプロジェクトを作成した時点でこれらのイベントのイベントハンドラが設定されているので、その部分を書き換えます。

```diff_javascript:assets/js/app.js
- window.addEventListener('phx:page-loading-stop', info => topbar.hide())
+ window.addEventListener('phx:page-loading-stop', info => {
+   if (['redirect', 'patch'].includes(info.detail.kind)) {
+     gtag('event', 'page_view', {
+       page_title: document.title,
+       page_location: location.href,
+       page_path: location.pathname
+     })
+   }
+   topbar.hide()
+ })
```

イベントハンドラが受け取る `info.detail` が持つ `kind` には4種類の値がありますが、ページ遷移を捕捉したい場合は `redirect` `patch` のみページビューのイベントをGAに送信すればOKです。

https://hexdocs.pm/phoenix_live_view/0.16.0/js-interop.html#loading-state-and-errors

https://developers.google.com/analytics/devguides/collection/gtagjs/pages

# UA＋Googleタグマネージャを使う場合

タグマネージャを使いこなせる方なら分かりますよね、タグマネージャで設定しましょう（丸投げ）。

https://goodpatch.com/blog/gtm-spa-pageviews

# 最後に

LiveViewはまだ発展途上なので、情報も豊富ではなく、特に本記事のようなLiveViewの実装に直結しない内容はまだあまり発信されていない印象です。これからLiveViewの本格的な採用事例も増えてくるでしょうし、LiveViewに限らずElixirコミュニティはものすごく盛り上がっているので、興味がある方は一緒に盛り上げていきましょう :raised_hands:

elixir.jp Slackの招待リンクです :point_down: たぶん入れると思いますが、入れなかったら教えてください…

https://join.slack.com/t/elixirjp/shared_invite/zt-ae8m5bad-WW69GH1w4iuafm1tKNgd~w

---

明日の「elixir Advent Calendar 2021」21日目は @MzRyuKa さんです！
お楽しみに :rocket:
第2回個人開発プロダクト供養~ogpkatex~

# はじめに
この記事は[LIGTHzアドベントカレンダー 2021](https://qiita.com/advent-calendar/2021/lightz)の14日目の記事です。

個人開発プロダクト供養第２回です。第１回は[こちら](https://qiita.com/KTaskn/items/717ef44eea4e7ce54d2c)

# [OGPKaTeX](https://github.com/KTaskn/ogpkatex) <- GitHubにある残骸はこちら
私自身が、Twitter や Slack で、TeX使って数式を書きたいなという気持ちで作り始めたサービスです。
手元のローカルでMarkdownなどで数式書いて画像ペタリでもいいのですが、どうしても面倒臭いので、OGP使って、リンク画像で数式表示できればいいのでは？？と思って作っちゃいました。

結構なチカラをいれて開発したのですが、実際稼働しはじめると、自分自身でもあまり使い所がなく、サービス停止となりました。。うーん、もっと使えるはずなのですが、これも再度練り直したいサービスです。

# 構成と要素技術
* [OGP](https://e-words.jp/w/OGP.html)
    * ニュース記事などのURLをSNSにはりつけると、タイトル画像やサムネイルが表示されるあの機能です。この画像部分をTeXで自動生成しちゃいます。
* [KaTeX](https://github.com/KaTeX/KaTeX)
    * WebページにTeXをさっくり導入できます。
* [pageres](https://github.com/sindresorhus/pageres)
    * Headlessブラウザです。
    * コマンドでKaTeXで作成したWebページ上の数式を、これまた楽にスクリーンショットしてくれます。
* Next.js
    * Reactの勉強がてらNextでつくってみました。

# 処理フロー
1. Nextで表示されたWebページのテキストエリアに、ユーザさんがTeXで数式を書く
1. ユーザはSNSにリンクを貼る
1. OGPに動的に画像リンクが生成される
1. 画像は動的に生成され、読み込まれたタイミングでpageresがKaTeXの記載されたページを見て画像を生成する

動的OGPのすごい勉強になったので、その部分だけでも他のサービスに転用できる非常に勉強になった開発でした。
railsチュートリアル第十四章 Relationshipのバリデーション

###Relationshipのバリデーション
####Relationshipモデルのバリデーションをテストする 
test/models/relationship_test.rb

```rb
require 'test_helper'

class RelationshipTest < ActiveSupport::TestCase

  def setup
    @relationship = Relationship.new(follower_id: users(:michael).id,
    # 新しくデータモデルからインスタンスを生成する。
    # テストユーザーidをフォロワーidにする
                                     followed_id: users(:archer).id)
                                     # テストユーザーを当てる
                                     # フォローされているidに与える
  end

  test "should be valid" do
    assert @relationship.valid?
    # インスタンスは有効か？（２項目）
  end

  test "should require a follower_id" do
    @relationship.follower_id = nil
    # follower_idを空にする
    assert_not @relationship.valid?
    # 有効ではないことを確認
  end

  test "should require a followed_id" do
    @relationship.followed_id = nil
    assert_not @relationship.valid?
  end
end
```

####Relationshipモデルに対してバリデーションを追加する
app/models/relationship.rb

```rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  # belongs_to  他方のモデルのインスタンスに「従属(belongs to)」します。
  # :followerモデルはUserに従属するのか？
  
  belongs_to :followed, class_name: "User"
  # followedモデルは従属する 
  validates :follower_id, presence: true
  # 有効、存在する
  validates :followed_id, presence: true
end
```

####Relationship用のfixtureを空にする
test/fixtures/relationships.yml

```yml
# 空にする
```

```
ubuntu:~/environment/sample_app (following-users) $ rails t
Running via Spring preloader in process 8278
Started with run options --seed 47075

  62/62: [=============================] 100% Time: 00:00:07, Time: 00:00:07

Finished in 7.40609s
62 tests, 316 assertions, 0 failures, 0 errors, 0 skips
```

###演習
1.
リスト 14.5のバリデーションをコメントアウトしても、テストが成功したままになっていることを確認してみましょう。（以前のRailsのバージョンでは、このバリデーションが必須でしたが、Rails 5から必須ではなくなりました。今回はフォロー機能の実装を優先しますが、この手のバリデーションが省略されている可能性があることを頭の片隅で覚えておくと良いでしょう。）

確認
railsチュートリアル第十四章 User/Relationshipの関連付け

###User/Relationshipの関連付け

####能動的関係に対して1対多（has_many）の関連付けを実装する
app/models/user.rb

```rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  # 他のモデルとの間に「1対多」のつながり
  # has_many関連付けが使われている場合、
  #   「反対側」のモデルでは多くの場合belongs_toが使われます。
  # dependent: :destroy
  # ユーザーが破棄された場合
  #   、ユーザーのマイクロポストも同様に破棄される。
  has_many :active_relationships, class_name:  "Relationship",
  # :active_relationsshitps 能動的関係(フォローしているがフォローはされていない関係)
  # class_name: テーブル名 "Relationship"
                                  foreign_key: "follower_id",
                                  # foreign_key: 外部キー
                                  #   データベースの二つのテーブルを繋げる
                                  # Micropostモデルとrelationshipsモデルを繋げる
                                  dependent:   :destroy
                                  # ユーザーが削除されるとマイクロポストも削除される
.
.
.
end
```

####リレーションシップ/フォロワーに対してbelongs_toの関連付けを追加する
app/models/relationship.rb

```rb

```

###演習
1.
コンソールを開き、表 14.1のcreateメソッドを使ってActiveRelationshipを作ってみましょう。データベース上に２人以上のユーザーを用意し、最初のユーザーが２人目のユーザーをフォローしている状態を作ってみてください。

```rb
>> user.active_relationships.create!(followed_id: User.second.id)
  User Load (0.1ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ?  [["LIMIT", 1], ["OFFSET", 1]]
   (0.1ms)  begin transaction
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  Relationship Create (7.0ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 1], ["followed_id", 2], ["created_at", "2021-10-28 06:05:06.051991"], ["updated_at", "2021-10-28 06:05:06.051991"]]
   (7.1ms)  commit transaction
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2021-10-28 06:05:06", updated_at: "2021-10-28 06:05:06">
```
2.
先ほどの演習を終えたら、active_relationship.followedの値とactive_relationship.followerの値を確認し、それぞれの値が正しいことを確認してみましょう。

わかんない。