OmniAICreator/Qiita-1.07M huggingface dataset:
Rails コマンドでモデルとテーブル作成・カラム追加


## モデル作成＋テーブル作成

`rails g model <モデル名アッパーキャメル単数形> [カラム名:型]...`

`rails g model HighScore game:string score:integer`


## カラム追加

`rails g model Add<any>To<モデル名アッパーキャメル単数形>`

`rails g model AddColumnToTable game_add:string`
Cloud Functions for Firebaseでリクエストbodyが消失する問題でハマった。

# Cloud Functions for Firebaseでリクエストbodyが消失する問題でハマった。

Cloud Functions for Firebaseでリクエストbodyが消失する問題でハマったので、同じ轍を踏む人がいない様に共有。

## 結論

クライアント側のContent-typeが重複して定義されていた。

`Content-Type: application/json,application/json`

## 現象

Cloud Functions for Firebaseでリクエストbodyが消失した。
消失したとは、functions.https.Requestのbodyプロパティが空のオブジェクトになっている。

## 状況

* JSONをInputとして受け付けるAPIをCloud Functions for Firebase上で運用していた。
* 内部のサービスから突然リクエストbodyが空のリクエストが来る(起きたり起きなかったりする。)
* クライアント側のコードを見ても確実にリクエストbodyは付与している。
* Cloud Functions for FirebaseはNode.js 16で動かしており、expressなどのFWは利用していない。


## 対策

クライアント側で `Content-Type` の重複を取り除いてもらった。

## なぜ

[body-parser](https://www.npmjs.com/package/body-parser)の仕様だから。
Cloud Functions for Firebaseではbody-parserを利用している。[^1]
body-parserでは、`application/json,application/json` はjsonとしてみなされないためこの様になる。

具体的にはbody-parser内で利用しているtype-isが以下の様に重複している場合はfalse扱いになる。
そのため、jsonとしてパースされず、リクエストbodyが空扱いとなる。

```javascript
const typeis = require('type-is');

const contentType = "application/json,application/json";
const request = {headers: {"content-type": contentType, "content-length": 1}};

console.log(typeis(request, ['application/json'])); // false
```

[^1]: https://firebase.google.com/docs/functions/http-events#read_values_from_the_request

## 蛇足

### 発生したりしなかったりするのはなぜ？

GCPのサポートに聞いたところ、Cloud Functionsの内部バックエンドに依存して古いバージョンであればその問題は発生しないけど、新しくなると発生する。
全てのインスタンスが自動的に新しいバージョンになるわけではないので、発生したり発生しなかったりしたということでした。

# 宣伝

株式会社オプティマインドでは、一緒に働く仲間を大募集中です。
カジュアル面談も大歓迎ですので、気軽にお声がけください。

【エンジニア領域の募集職種】
●[ソフトウェアエンジニア](https://hrmos.co/pages/optimind/jobs/0002012)
●[QAエンジニア](https://hrmos.co/pages/optimind/jobs/0002011)
●[Androidアプリエンジニア](https://hrmos.co/pages/optimind/jobs/0002008)
●[組合せ最適化アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002013)
●[経路探索アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002014)
●[バックエンドエンジニア](https://hrmos.co/pages/optimind/jobs/0002015)
●[インフラエンジニア](https://hrmos.co/pages/optimind/jobs/0002016)
●[UXUIデザイナー](https://hrmos.co/pages/optimind/jobs/0002010)

【ビジネス領域の募集職種】
●[セールスコンサルタント](https://hrmos.co/pages/optimind/jobs/0002001)
●[オペレーションコンサルタント](https://hrmos.co/pages/optimind/jobs/0002027)



『オプティマインドってどんな会社？』については、[こちら](https://www.optimind.tech/recruit/)から
Wantedlyでも[こちらで募集中](https://www.wantedly.com/companies/optimind/projects)
複数のファイルに分割されて定義された prototype の function をまとめて import する

# 何がしたいか

JavaScript (ES6) で `Obj.prototype.func = function(){}` みたいな定義を、複数のファイルに分割して書いてあるときに、分割された `Obj` の定義をまとめて import します。

具体例：

```bar.js
export function Foo(){
    console.log("foo");
}

Foo.prototype.bar = function()
{
    console.log("bar");
}
```

```baz.js
Foo.prototype.baz = function()
{
    console.log("baz");
}
```

こんな二つのファイルがあるとき、

```app.js
import { Foo } from './bar.js';
import { Foo } from './baz.js';

const foo = new Foo();
foo.bar();
foo.baz();
```

これで、出力が下記のようになってほしい。

```
foo
bar
baz
```

ちなみに、上の `app.js` は普通にエラーになります。（Foo が重複宣言されているということで）

# 結論

下記のいずれかで想定通りの結果になります。

## 解決法1

`export {}` を使う。

```bar.js
export function Foo(){
    console.log("foo");
}

Foo.prototype.bar = function()
{
    console.log("bar");
}
```

```baz.js
import { Foo } from './bar.js';

Foo.prototype.baz = function()
{
    console.log("baz");
}

export { Foo };
```

```app.js
import { Foo } from './baz.js';

const foo = new Foo();
foo.bar();
foo.baz();
```

`bar.js` は `baz.js` 経由で `import` します。 

``bar.js` は下記のように書いてもOKです。

```bar.js
function Foo(){
    console.log("foo");
}

Foo.prototype.bar = function()
{
    console.log("bar");
}

export { Foo };
```

## 解決法2

`export default` を使う。

```bar.js
export function Foo(){
    console.log("foo");
}

Foo.prototype.bar = function()
{
    console.log("bar");
}
```

```baz.js
import { Foo } from './bar.js';

Foo.prototype.baz = function()
{
    console.log("baz");
}

export default Foo;
```

```app.js
import Foo from './baz.js';

const foo = new Foo();
foo.bar();
foo.baz();
```

## node で実行する場合の注意

node で実行する場合は、下記のような `package.json` を作って、`npm run start` なり `yarn start` なりで実行する必要があります。`node` は v12 以上でないとダメかも。

```package.json
{
    "type": "module",
    "scripts": {
        "dev": "node --experimental-modules app.js"
    }
}
```

実行結果：

```
$ npm run start

> start
> node --experimental-modules app.js

foo
bar
baz
```

# おわり

prototype を使って書かれた古いコードをリファインしないといけない、みたいなときに使え・・・ますかね？？
ZabbixとWeTTYを共存させるためにnginxとapache2の設定した

# ZabbixのWEB UIとWeTTYを共存させるために、nginxとApache2の設定をした
[Zabbix](https://www.zabbix.com)と[WeTTY](https://github.com/butlerx/wetty)ってサービスのWEBフロントエンドを一つのサーバで共存させたかったので、[nginx](
https://github.com/nginx/nginx-releases)のconfファイルやApache2のconfファイル、WeTTYのsystemdファイル等を編集しました。

<img width="500" alt="代替テキスト"src="https://www.nginx.com/wp-content/uploads/2021/08/NGINX-Part-of-F5-horiz-black-type-1.svg">
まずnginxのconfでzabbixのWEBフロントエンドとwettyのポートへのリバースプロキシを設定
/etc/nginx/nginx.conf

```conf:/etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
        worker_connections 768;}
http{
  server {
    server_name         yourservername.com;
    listen                  80;

    location ^~ /wetty{
      proxy_pass http://127.0.0.1:3000/wetty;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_read_timeout 43200000;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
    }
    location ^~ /zabbix{
      proxy_pass http://127.0.0.1:8010/zabbix;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_read_timeout 43200000;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

    }
  }
}
```

<img width="500" alt="代替テキスト"src="https://assets.zabbix.com/img/logo/zabbix_logo_313x82.png">
次にZabbixのフロントエンドを届けてるApache2のポートを上で設定しているポート(8010)に変更
/etc/apache2/ports.conf
```conf:/etc/apache2/ports.conf
Listen 8010
<IfModule ssl_module>
  Listen 8010
</IfModule>
<IfModule mod_gnutls.c>
  Listen 8010
</IfModule>
```
WeTTYをsystemdのサービス化。ついでにポート番号も固める
/etc/systemd/system/wetty.service
```systemd:/etc/systemd/system/wetty.service
[Unit]
Description=wetty service

[Service]
After=network.target
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/local/bin/wetty -p 3000

[Install]
WantedBy=multi-user.target
```

これ全部AWSの一つのEC2インスタンスでやってるんですよね・・・
せっかくAWS触るならもっとAWSらしい事やりたいものです。
ネタとか頂けたら幸いです。
取り敢えずLambda、DynamoDB、Amplifyとかはハンズオン動画に沿って触ってみました。
[AWS 初心者向けハンズオン](https://aws.amazon.com/jp/aws-jp-introduction/aws-jp-webinar-hands-on/?trk=aws_blog)
Go言語で作るインタプリタ go test ./lexer で import "monkey/token" ができない

非常に具体的な問題を解決するための記事です。

## バージョン

```
$ go version
go version go1.18.3 darwin/amd64
```

## 問題点

- `~/SOME/PATH/monkey/token` の用にフォルダを作成する(自分の場合は`~/program/go/interpreter/monkey/token`としている)

- `go mod init github.com.....`としたらダメだった

```
$ go test ./lexer
# github.com/yojiyama7/go_interpreter/lexer
lexer/lexer_test.go:5:2: package monkey/token is not in GOROOT (/usr/local/go/src/monkey/token)
FAIL	github.com/yojiyama7/go_interpreter/lexer [setup failed]
FAIL
```

## 解決策

- `go.mod`ファイルを削除して、
- `monkey`ディレクトリで `go mod init monkey` とする
- このような内容の`go.mod`ができる

```
module monkey

go 1.18
```

## DONE

- `monkey`ディレクトリで`go test ./lexer`が動いた

```
$ go test ./lexer
# monkey/lexer [monkey/lexer.test]
lexer/lexer_test.go:12:25: undefined: token.TokenType
lexer/lexer_test.go:26:7: undefined: New
FAIL	monkey/lexer [build failed]
FAIL
```
Linuxのコマンドで最新のファイルだけ取得する方法


Linuxのコマンドで最新のファイルだけ取得するコマンドのメモ。

## コマンド
```
ls -rt | tail -n 1
```


## lsコマンド

- 機能
ディレクトリの内容を標準出力に出力する

- オプション

|  オプション  |  内容  |
| ---- | ---- |
|  -r  |  並び順を反転させる  |
|  -t  |  ファイルを更新日が新しい順に表示する  |

これで、ファイルが更新日が古い順（古い→新しい）に並び替えられる

## tailコマンド
- 機能
ファイルや標準入力の最後の部分を標準出力に出力する

- オプション

|  オプション  |  内容  |
| ---- | ---- |
|  -n |  出力する行数を指定する  |

`-n 1`で最新のファイルが1つ取得できる

## 使い方

データを復旧するコマンドのうち、「バックアップ用サーバーにSSH接続し、最新のdumpファイルを取得する」コマンドを作成する際に使用しました。

```
$command = '(ssh -i .ssh/id_rsa -o StrictHostKeyChecking=no -o ConnectTimeout=30 -p ' . $backupServerPort . ' ' .  $backupServerUser . '@' . $backupServerHost . ' ls -rt ' . self::BACKUP_SERVER_PATH . ' | tail -n 1) 2>&1';
```
（今回の内容とはズレるけど、`~/.ssh/config`に接続情報を登録しておくと、ssh接続部分はもっと簡潔に書ける。参考：https://qiita.com/passol78/items/2ad123e39efeb1a5286b ）


## 参考
https://lanchesters.site/linux-ls-new/
https://atmarkit.itmedia.co.jp/ait/articles/1606/27/news018.html
https://eng-entrance.com/linux-command-tail
基本情報（主記憶装置と高速化手法）

記憶装置ごとにアクセス速度を比較すると、下記のような並びとなります。

```
   CPUのレジスタ　>（超えられない壁） メモリ（主記憶装置）　>（超えられない壁） ハードディスクなどの磁気ディスク装置
速い　--------------------------------------------------------------------------------- 遅い

右に行くほど安価で大容量
```
上の図を見ると、高速なレジスタとメモリにすれば良いと言いたくなりますが、記憶装置は高速になればなるほど１ビットあたりの単価が高くなっていきます。そのため全てレジスタにしたり、メモリにすることは現実的ではないようです。

記憶装置間の速度ギャップを埋めて、待ち時間によるロスを防ぐための手法が
**キャッシュ**です。

キャッシュも種類があり、レジスタとメモリの間に設ける**キャッシュメモリ**や、メモリとハードディスクの間に設ける**ディスクキャッシュ**などがあります。

本当にキャッシュって言葉は色んなところで出てきますね。

ちなみにキャッシュの意味は
> 英語で「cache」と表記し、「貯蔵所」「隠し場所」という意味を持ちます。

らしいです。キャッシュって言葉が出てきたら、一時的に保存するんだなーとイメージしておけばオッケーですね。

## キャッシュメモリ
CPUはコンピュータの動作に必要なデータやプログラムをメモリ（主記憶装置）との間でやりとりします。
CPUに比べるとメモリは非常に遅いので、読み書きするたびにメモリへアクセスしていると、待ち時間ばかりが発生してしまいます。

メモリとCPUの間に、より高速に読み書きできるメモリを置いて、温度差によるロスを吸収させます。
これを**キャッシュメモリ**と呼びます。

```
CPU ← キャッシュメモリ ←  メモリ
// 主記憶装置（メモリ）から読み込んだデータは、キャッシュメモリにも保持される。
CPU ← キャッシュメモリ メモリ
// 同じデータを読む時は、高速なキャッシュメモリから取得することができる。
```
CPUの中にはキャッシュメモリが入っていて、処理の高速化が図られています。
キャッシュは１次キャッシュ、２次キャッシュと重ねて設置することが可能です。
```
CPU① → SRAM② 
　　　    →　　　DRAM③

①１次キャッシュ
③2次キャッシュ
```
このキャッシュメモリと同じ役割を主記憶装置と磁気ディスク装置の間で担うのが
**ディスクキャッシュ**です。
ディスクキャッシュは専用に半導体メモリを搭載したり、主記憶装置の一部を間借り
したりして実装します。

```
　　高速(小容量)|レジスタ
　　　　　　　　　       |キャッシュメモリ
　　　　　　　　　       |主記憶装置
　　　　　　　　　       |ディスクキャッシュ
　　低速(大容量)|磁気ディスク装置
// 記憶階層と呼びます
```

## 主記憶装置への書き込み方式
キャッシュメモリは読み出しだけじゃなく、書き込みでも使われます。
ただし、読み出しと違って書き込みの場合は「書いて終わり」とはなりません。

更新した内容をどこかのタイミングで主記憶装置にも反映しなければなりません。

主記憶装置を書き換える方式は、**ライトスルー方式**と**ライトバック方式**の2つがあります。

```
ライトスルー方式
キャッシュメモリへの書き込みを行う際に、主記憶装置へも同時に書き込みを行います。

CPU → キャッシュメモリ
　　　　　　　　→ DRAM(主記憶装置)

ライトバック方式
この方式では、普段はキャッシュメモリしか書き込みを行いません。
キャッシュメモリから追い出されるデータが発生した際に、その内容を主記憶装置へと書き戻して
更新内容を反映させます。

CPU　→ キャッシュメモリ
キャッシュメモリ（追い出されたデータ）　→ DRAM(主記憶装置)
```

## ヒット率と実行アクセス時間
キャッシュメモリの容量は小さいため、目的とするデータが必ずそこに入っているわけではありません。
目的とするデータがキャッシュメモリに入っている確率を**ヒット率**と呼びます。

```
キャッシュメモリに目的のデータが存在する確率　＝　ヒット率
キャッシュメモリになくて、主記憶装置を読みに行く確率　＝　１ーヒット率
```

例えばキャッシュメモリのヒット率が70%なら、主記憶装置を読みに行く確率は30%となります。
計算式にすると

①キャッシュメモリのアクセス時間　✖️　ヒット率
②主記憶装置のアクセス時間　✖️　（１ーヒット率）

① ＋ ② ＝　実行アクセス時間
となります。

## メモリインタリーブ
主記憶装置へのアクセスを高速化する手法として、キャッシュメモリ以外にあげられるのが
**メモリインタリーブ**です

主記憶装置の中を複数の区画（バンク）に分割します。
```
バンク0 バンク1 バンク2
0番地  1番地  2番地
３番地  4番地  5番地
6番地　　　　7番地　　　　8番地
```
上記の区画はそれぞれ並行してアクセスできます。
複数バンクに同時にアクセスすることで、連続した番地のデータを一気に
読み出すことができます。
```
バンク0 バンク1 バンク2
------------------ 0~2番地のデータを一気にとる
３番地  4番地  5番地
6番地　　　　7番地　　　　8番地
```
ただ、ランダムアクセスなどの連続しないデータの読み出しでは
効力を発揮できないようです。

## まとめ
さすが精密機械。ちゃんと仕組みは理解しよう。
Django の Form 内の Field に対して、装飾・グループ化を行う

# はじめに

Django の Form は (デフォルトの) テンプレートエンジン内で `{{ form }}` のようにする[^1] と、全てのフィールドを HTML 出力してくれる。 これらの実体は Widget を用いることで変更できるのだが、Widget は単純に input や select タグとのマッチング用途で作られているため、例えば Label + (必須属性なら必須を示す `*` をつける) + Inputフィールド、というような組み合わせを作りづらい。
また、１つのフォームで取り扱う項目が多い場合、これをグループに分け、グループ単位で一括で取り扱いたいということもある。

このように、Form の出力をカスタマイズする方法についてを考察する。

[^1]: context に `{'form': form}` として Form のインスタンスを渡している場合。 以後、これを前提とする。


## 検証バージョン

- Python: 3.10.2
- Django: 4.0.5


# Form 内 Field のグループ化

Form 内に引数0の関数を定義し、そこで自身のフィールドの一部を返すようにすることで実現できる。
例えば、以下のように定義すれば、テンプレート内で `{% for field in form.group1 %}` のようにしてグループ化したフィールドを呼び出せる。

```python
class MyForm(Form):
    # 中略
    def field_groups(self, keys: list) -> list:
        return [self[k] for k in keys]

    def group1(self):
        return self.field_groups(['key1', 'key2'])
```

https://stackoverflow.com/questions/10366745/django-form-field-grouping


# グループ化をまとめてレンダリングする

最初に示した "Label + (必須属性なら必須を示す `*` をつける) + Inputフィールド、というような組み合わせを一括出力するような場合は、以下のように記述すれば良い。　レイアウト上必要な場合、適切な class を付与すること。

```jinja:fieldgroup.jinja
{% for field in fields %}
<div>
  <label for="{{ field.id_for_label }}">
    {{ field.help_text|default:field.label }}
    {% if field.field.widget.is_required %}
    <span class="required">*</span>
    {% endif %}: 
  </label>
  {{ field }}
</div>
{% endfor %}
```

これを別のファイル (fieldgroup.jinja) に切り出し、別のテンプレートで include して読み込むときは以下のようにすれば良い。

```html:index.html
{% include '<中略>/fieldgroup.jinja' with fields=form.group1 %}
```

なお、記載していないが、`fieldgroup.jinja` 内に更に記載することで、エラー時の表記をフィールドに併せて行わせるといったことも可能になる。

# 補足

実際の Form を作成する場合は特定の要素を特別に扱いたい場合もあると思うが、それらは共通化せず、`{{ form.field }}` (field は実際のフィールド名) のように直接扱う方が良いと思う。

あくまで共通化して処理できる部分を一括解決するために、グループ化と、グループに対して一括で処理する方法を示した。
Salesforce B2C Commerceのコントローラでイベントを扱ってみる


※ これから記載する事項は、私が所属する会社とは一切関係のない事柄です。


本記事ではSFRAのコントローラで利用できるイベントを紹介したいと思います。


## イベントの使い方

イベントの使い方には２種類あります。

#### ルート作成メソッド内に書く

下記のようなコードをルート作成メソッドのロジック内またはミドルウェア内に記載して利用します。
onceは一度実行されたらそれ以降は実行されません。

```js
=====OMIT=====
server.get('Show', function (req, res, next) {
    =====OMIT=====
    this.on("route:BeforeComplete", function (req, res) {
        // ロジック
    });
    // または、
    this.once("route:BeforeComplete", function (req, res) {
        // ロジック
    });
    =====OMIT=====
})
=====OMIT=====
```

#### ルート作成メソッド外に書く

```js
=====OMIT=====
var show = server.get("Show", function (req, res, next) {
    res.render("sample/test");
    next();
})

show.on("route:Start", function (req, res) {
    // ロジック
});
// または、
show.once("route:Start", function (req, res) {
    // ロジック
});
=====OMIT=====
```




## イベントの種類

### route:BeforeComplete 

コントローラ内のロジック含むミドルウェアのロジックが全て完了した後に実行されます。ユーザがサブミットしたデータをデータベースに登録するときによく利用されます。特にformを利用した場合に利用されます。


### route:Complete

route:BeforeCompleteも含む全てのロジックが完了した後でクライアントにレスポンスが返される前に実行されます。ここではレスポンスされるISMLへのレンダーやJSONの生成は終わっているので、それらに対する修正はできません。


### route:Redirect

res.redirect でリダイレクトされる前に実行されます。


### route:Start

ミドルウェアのチェーンが始まる前に実行されます。ルート作成メソッド外に書かないと使用できないです。


### route:Step

各ミドルウェアの実行前に実行されます。各ミドルウェアの実行前に実行されるので、onceを使ってしまうと、一回しか実行されないので注意。全てのミドルウェア終了後に実行したい場合はルート作成メソッド外に書く方が良いかと思います。


## prependやappendとの組み合わせ


以前の [Qiita](https://qiita.com/syunyama/items/8b31eb1c3337efeed1fd)で紹介した内容ですが、append, prependとミドルウェアを合わせた場合の処理の順番を記載します。

1. prependに設定された左からの関数（ミドルウェア）が順に実行される
2. get/post/useに設定された左からの関数（ミドルウェア）が順に実行される
3. appendに設定された左からの関数（ミドルウェア）が順に実行される


イベントはonまたはonceで登録された順番で実行されます。
例えば`controller_sample_custom1:controller_sample_custom2:controller_sample_base`
のようにカートリッジパスが設定されていた場合、下記の１から順番にミドルウェア内でonでroute:BeforeCompleteとroute:Completeのイベントがそれぞれ登録されたとします。


1. custom1のprependの1番目のミドルウェア
2. custom1のprependの2番目のミドルウェア
3. custom2のprependの1番目のミドルウェア
4. baseの(get/post/use)の1番目のミドルウェア
5. custom2のappendの1番目のミドルウェア
6. custom1のappendの1番目のミドルウェア


route:BeforeCompleteとroute:Completeの動きは下記のようになります。


1. custom1のprependの1番目のミドルウェアの実行(onでイベント定義)
2. custom1のprependの2番目のミドルウェアの実行(onでイベント定義)
3. custom2のprependの1番目のミドルウェアの実行(onでイベント定義)
4. baseの(get/post/use)の1番目のミドルウェアの実行(onでイベント定義)
5. custom2のappendの1番目のミドルウェアの実行(onでイベント定義)
6. custom1のappendの1番目のミドルウェアの実行(onでイベント定義)
7. custom1のprependの1番目のミドルウェアで定義したroute:BeforeCompleteの実行
8. custom1のprependの2番目のミドルウェアで定義したroute:BeforeCompleteの実行
9. custom2のprependの1番目のミドルウェアで定義したroute:BeforeCompleteの実行
10. baseの(get/post/use)の1番目のミドルウェアで定義したroute:BeforeCompleteの実行
11. custom2のappendの1番目のミドルウェアで定義したroute:BeforeCompleteの実行
12. custom1のappendの1番目のミドルウェアで定義したroute:BeforeCompleteの実行
13. custom1のprependの1番目のミドルウェアで定義したroute:Completeの実行
14. custom1のprependの2番目のミドルウェアで定義したroute:Completeの実行
15. custom2のprependの1番目のミドルウェアで定義したroute:Completeの実行
16. baseの(get/post/use)の1番目のミドルウェアで定義したroute:Completeの実行
17. custom2のappendの1番目のミドルウェアで定義したroute:Completeの実行
18. custom1のappendの1番目のミドルウェアで定義したroute:Completeの実行


## 意図的にイベントを発生させたい場合
下記のコマンドをルート作成メソッドのロジック内またはミドルウェア内に記載して利用します。

```js
this.emit('route:Complete', req, res);
```
【Laravel】Base table or view not found: Table '(サービス名).(テーブル名)' doesn't exist 

## 環境
Laravel v9.5.1 (PHP v8.1.3)

## 状況

テスト実行時に下記のエラー。

```
Base table or view not found: 1146 Table '(サービス名).(テーブル名)' doesn't exist 
```

## 解決法

公式ドキュメントには
>どのデータベーステーブルがFlightモデルに対応するかをEloquentに知らせていないことにお気づきかもしれません。別の名前を明示的に指定しない限り、クラスの複数形の「スネークケース」をテーブル名として使用します。
モデルの対応するデータベーステーブルがこの規約に適合しない場合は、モデルにtableプロパティを定義してモデルのテーブル名を自分で指定できます。

今回のエラーが例えば下記だった場合、
```
Base table or view not found: 1146 Table '(サービス名).flight' doesn't exist
```
モデルでテーブル名を指定しなかったのでEloquentが「flights」を使用しており、エラーが出た。

下記のようにテーブル名を指定してエラー解決！

```php
class Flight extends Model
{
    /**
     * モデルに関連付けるテーブル
     *
     * @var string
     */
    protected $table = 'flights';
}
```

## 参考
https://readouble.com/laravel/9.x/ja/eloquent.html

## Railsは...

Railsにも命名ルールを上書きするときの書き方があった。
使ったことなかったので知らなかった…

```ruby
class Flight < ApplicationRecord
  self.table_name = "flights"
end
```
[Gatsby.js] gatsby-plugin-sitemapで作るサイトマップで、特定のディレクトリ配下をサイトマップから除外する

## gatsby.jsでのサイトマップ
gatsbyを使用したweb開発でのサイトマップは公式のプラグインである、```gatsby-plugin-sitemap``` を使用します。
導入方法・使用方法は以下を参照
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/

## サイトマップから除きたいページがある場合
生成されるサイトマップから除きたいページがある場合、以下のように該当するページのパスを文字列で指定します。

```javascript:gatsby-config.js
    {
      resolve: "gatsby-plugin-sitemap",
      options: {
        excludes: [`/contact`, `/thanks`]      
      }
    }
```

## 特定のディレクトリパス配下のページを全て除外したい場合
次は、特定のディレクトリ名の配下のページを全てサイトマップから除きたい場合です。
これの記述は公式ドキュメントにはありませんでした。
また、```/hoge/*```こんな感じで指定すればいけるのかなと思っていました。

しかし、
gatsby-plugin-sitemapのexcludesで指定されたパスは[minimatch](https://github.com/isaacs/minimatch "minimatch")で全ページのパスと照合され一致しているパスが除外される形なので、minimatchで配下のパスを指定する必要がありました。（これが公式に記載がないから全然わからなかった。）


例えば、```/posts/pokemon/1``` ```/posts/pokemon/2``` のようにあるページのpokemon配下のこの２ページをサイトマップから削除したい場合はこうます。

```javascript:gatsby-config.js
    {
      resolve: "gatsby-plugin-sitemap",
      options: {
        excludes: [`/pokemon/**`]      
      }
    }

```
これで、```pokemon```配下のページを全てサイトマップから除くことができました。

## 手元でのサイトマップの確認方法
修正後、開発環境でサイトマップの動作確認をしたい場合、まずpublic配下にsitemapを生成する必要があるので```gatsby build```をします。
その後、```gatsby develop```で開発サーバーを起動します。

```http://localhost:8000/sitemap/sitemap-index.xml ```を開くと

![スクリーンショット 2022-06-28 18.26.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364026/aaadd86b-51bd-cb18-2150-4bf2083eff73.png)

こんな感じでホストURL+サイトマップの形でサイトマップの置き場所を教えてくれます。

```http://localhost:8000/sitemap/sitemap-0.xml```
ホストをローカルホストに書き換えてアクセスするとサイトマップページにアクセスできます。

![スクリーンショット 2022-06-28 18.28.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364026/aefb0dec-e4c1-2949-330c-9a72f55ef219.png)

## 終わりに
以上です。gatsby-plugin-sitemapを使用した時に特定のディレクトリ配下をサイトマップから除きたい場合の実装でした。
LaTeXでEPS画像をPDFファイルに出力したら真っ白になっちゃう問題の解決法

## 概要
論文投稿の直前に泣きをみるあの問題...そう，Latexで原稿を書いてPDFにコンパイルしたらEPSの図が全部真っ白になってて自分の頭も真っ白！というあの現象の解決方法をご紹介します．

## 解決方法

ざっくりいうとGostScriptをダウンロードしてgsviewをインストールすればOKです．

### 1. GostScriptのダウンロード

- 公式サイトからGostScriptをダウンロードしてきます．
    - Ghostscript : Downloads
        - https://ghostscript.com/releases/gsdnld.html
    - 今回はGhostscript 9.55.0 for Windows (64 bit)のGhostscript AGPL Releaseをダウンロードします．
- ダウンロードしてきたexeファイルをどこかに格納します．（この記事ではCドライブの直下（C:\gs9.55.0）に格納した前提で説明します)．
- Pathを通します
    - やり方：Windowsキー→設定→システム→詳細情報→システムの詳細設定でシステムのプロパティを開きます．→「環境変数」の「Path」に「C:\gs9.55.0\bin」と「C:\gs9.55.0\lib」を追加します．

### 2. gsviewのインストール

- はじめに：gsviewについては以下のwikiに情報がまとまっているので詳細は以下のwikiを確認することをお勧めします．
    - GSview - TeX Wiki
        - https://texwiki.texjp.org/?GSview
- 現時点では最新版のGSview 5.01betaはバイナリ配布されていないので自分でビルドする必要があるそうです．
- Visual Studio Comumuityをインストールします．以下からダウンロードしてきます．（注意：Visual Ctudio Codeではできませんでした．）
    - Visual Studio 2022 コミュニティ エディション – 最新の無料バージョンをダウンロードする
        - https://visualstudio.microsoft.com/ja/vs/community/
    - 途中，ワークロードを選択する画面がありますが，ここで「C++ デスクトップ開発」を選択します．
        - 選択し忘れたら，Visual Studio Installer（スタートから起動できる）を立ち上げてワークロードを追加．Visual Studio Community 2022の「変更」をクリックし，「C++ デスクトップ開発」を選択し「設定」を押すと，後からでもインストールできるので大丈夫です．
- ソースコードを以下からダウンロードしてきます．以下より「gsview-09e7545.tar.gz」をダウンロードしてきて解凍します．
    - https://git.ghostscript.com/?p=user/ghostgum/gsview.git;a=snapshot;h=09e7545912ad1439e38d2b56516f50b150ff50eb;sf=tgz
    - ファイルとフォルダが2つできるので1つのフォルダにまとめておくとスッキリしますよ．
- 回答したファイルのgsview-09e7545\src\gvcver.hの「#define BETA_YEAR 2020」(使用期限)を「#define BETA_YEAR 2023」など未来日付に変更します．
- gsview-09e7545\srcwin\win.mak を gsview-09e7545フォルダ直下にコピーし「makefile」にファイル名を変更します．
- スタートから x64 Native Tools Command Prompt for VS を起動してターミナルで gsview-09e7545 ディレクトリ移動後に以下を実行します．

    ``` 
    nmake WIN64=1
    ```

- gsview-09e7545\bin に実行ファイル gsview64.exeが作成されます．（私がビルドしたときは色々エラーが出たけれどとりあえず動いてくれました．）

### 3. Texソースのコンパイルを再開

- 無事にPDFにEPSファイルを表示することができるようになります．ではでは，論文投稿，頑張ってください！！！

### おまけ

- もし許されるのであれば，EPSの図をPDFに変換してパッケージ`\usepackage{graphicx}`でなはく`\usepackage[dvipdfmx]{graphicx}`（PDFの図等々も埋め込めるようにしてくれるパッケージ）に変更してPDFの図を使ってしまうという手もありますね．
    - 注意：上記の二つのパッケージは競合するので必ずどちらか一方を使うこと！

## 参考文献
以下のサイトを参考にさせて頂きました．ありがとうございます．

- Ghostscript : Downloads
    - https://ghostscript.com/releases/gsdnld.html
- GSview - TeX Wiki
    - https://texwiki.texjp.org/?GSview
- LaTeXで図が真っ白になる問題について（texlive/2013）（Windows） - nano_exit
    - https://koideforest.hatenadiary.com/entry/2017/05/28/162703
- LaTeXを書いていてeps画像がPDFファイルに出力されない問題を解決 - 透明の練習 第2刷
    - https://betat.hatenablog.com/entry/2015/01/07/190911
【ServiceNow】添付されたエクセルファイルの入力値を取得するフローの作成方法

リクエストアイテムに添付したExcelファイルの入力値を取得する方法をまとめます。
使用するAPIはGlideExcelParserです。

https://developer.servicenow.com/dev.do#!/reference/api/quebec/server/GEPS-getRow

本記事ではFlowDesignerでGlideExcelParserを使用するアクションを作成し、メインフローを作成していきます。

## アクションを作成する
GlideExcelParserを使用するアクションを作成します。

### Input
添付ファイルのsysIDを取得したいので、StringタイプのInputを用意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/0100bafd-4b9f-d455-00d0-4fff22507df3.png)

### Script step
具体的な処理を記載していきます。

①変数にInputで取得した添付ファイルのsysIDをセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/196432bd-708b-1ac5-5afa-982013364be9.png)

➁Script欄にGlideExcelParserを使用する処理を記載します。
以下はサンプルスクリプトです。

```javascript:Script step
(function execute(inputs, outputs) {

 var parser = new sn_impex.GlideExcelParser();
 var attachment = new GlideSysAttachment();

//添付ファイルのsysIDを指定
 var attachmentStream = attachment.getContentStream(inputs.attachment);

//読み込むソースを指定
 parser.setSource(attachmentStream);

//ヘッダー情報を取得
//配列で各列の情報が格納される
 var headers = parser.getColumnHeaders();
 var num= headers[0];
 var companyName = headers[1];

 gs.info('Header num:'+ num);
 gs.info('Header companyName:'+ companyName);

//行単位で入力値を取得
 while (parser.next()) {
  var row = parser.getRow();
  gs.info('Value row:' + row[num] + ' + ' + row[companyName]);
 }

})(request, response);
```



## サンプルフローを作成する
作成したアクションを使用して、
リクエストアイテムに添付したファイルを読み込むフローを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/6030c288-a7ca-a339-43f3-b991c4a9a500.png)



＜解説＞
TRIGGER）カタログアイテムからレコードが作成されると本フローが処理されます。
ACTION１）リクエストアイテムに添付されたファイルのsysIDを取得します。　※OOTBアクション
ACTION２）ACTION１で取得した添付ファイルの入力値を取得します。　※カスタムアクション





## 各アクションの設定内容
### Action１　-　Look Up Attachmnet

Source Recordにトリガーとなるレコード（リクエストアイテム）を指定します。
※このアクションのOutputは添付ファイルのsysIDです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/22fe4a8d-4a98-1cef-7d52-33d8f545b95a.png)


### Action２　-　カスタムアクション
Action１のOutput（取得した添付ファイルのsysID）をInputに指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/81d558b5-e20b-c2cb-256e-ed4d4396756e.png)



## 動作確認
テストしてみます。
カタログアイテムにこのエクセルを添付して送信すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/e1a4fe34-2c4d-c6fc-93ff-2f33333529b3.png)



下図のようなログが出力されました。
Excelの入力値を取得できていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600769/4fd892ee-5f81-a055-2686-0d22401561d9.png)

## 終わりに
大量なデータをExcelのまま申請し、承認後に別システムへ自動登録してほしといった要望を実現するために本APIを使用しました。

とても便利な機能ですが、Excelのセルに付与されているフリガナも取得してしまう欠点もあります。
※フリガナは漢字を入力した際に自動で付与されます。

私たちはExcel保存時にフリガナを削除するマクロを実装することでフリガナごと連携されないよう対応しました。
GlideExcelParserを使用する際はお気をつけください。
MySQLで値の入れ替えをしたい

## カラムXとカラムYの値を入れ替えたい
ひょんなことからあるテーブルについてカラムXとカラムYに入っている値を入れ替えなければならなくなったとします。
そんなとき、安易に
```sql
UPDATE `table`
SET `X` = `Y`
  , `Y` = `X`;
```
とやろうとすると…地獄を見ます。
MySQLでは記述された順番に処理を実行するので、どちらも元の```Y```の値で更新されてしまいます。
そこでちょっと一工夫必要です。

## 対応方法
### 1. ユーザー変数を使う
ググると解決策として多く出てきますね。（主観）
以下のサイトを参考にさせていただきました。
[MySQLでフィールドの値を交換する](http://icoctech.icoc.co.jp/blog/?p=1033)
```sql
SET @temp = 0;
UPDATE `table`
SET `X` = (@temp:=`X`) * 0 + `Y`
  , `Y` = @temp;
```
数値型の場合ですね。
```X```に代入するところで、変数```@temp```にも格納しつつ、ゼロ倍してそのまま```Y```の値が入るようにすると。
これはなかなか自力でたどり着けない解法でした。

### 2. 加減算/結合分割で処理する
以下のサイトを参考にさせていただきました。
[MySQLで値の交換](https://paqalex.blog.fc2.com/blog-entry-4.html)
```sql
#数値の場合
UPDATE `table`
SET `X` = `X` + `Y`
  , `Y` = `X` - `Y`
  , `X` = `X` - `Y`;

#文字列の場合
UPDATE `table`
SET `X` = CONCAT(`X`, ':', `Y`)
  , `Y` = SUBSTRING_INDEX(`X`, ':', 1)
  , `X` = SUBSTRING_INDEX(`X`, ':', -1);
```
①まず```X```に合計（結合）した値を格納し、
②```Y```には合計から```Y```を差し引いて元の```X```の値を格納、
③そして改めて```X```には合計から入れ替え後の```Y```=元の```X```を差し引き、元の```Y```の値を格納すると。
プログラムチックで、ある意味馴染みやすい解法でした。

### 3. 自己結合を使う
他であまり見つからなかったのですが、こんな方法も。
```sql
UPDATE `table` t1, `table` t2
SET t1.`X` = t2.`Y`
  , t1.`Y` = t2.`X`
WHERE t1.id = t2.id;
```
自己結合を使うと、冒頭の安易なSQLに近い形で実現できます。
結構シンプルですね。
【Laravel】Block Kitを使ったSlack通知機能

## 環境
Laravel v9.5.1 (PHP v8.1.3)


## Block Kitを使った通知機能
```Notification```を使ってSlack通知機能を実装した記事はたくさん見かけたが、```Notification```に加え、リッチなメッセージを作成できる```Block Kit```を使った通知機能は見なかったので仕組みを理解し実装するのに時間がかかってしまった。

https://api.slack.com/block-kit

## 実装
大体はドキュメント通りに実装し、```toSlack()```の中にデフォルトで書いてある```new SlackMessage->to()```は使わずに下記のように変更。

```php
<?php

namespace App\Notifications;

...
class Slack extends Notification
{
  use Queueable;

  private string $message;

  public function __construct(string $message)
  {
    $this->message = $message;
  }

  public function via(): array
  {
    return ['slack'];
  }

  public function toSlack(): string
  {
    $client = new Client();
    $response = $client->request(
      "POST",
      'https://slack.com/api/chat.postMessage',
      [
        'form_params' => [
          'token'   => config('slack.token'),
          'channel' => config('slack.channel'),
          'attachments'  => json_encode($this->attachments()),
        ],
      ]);

    return $response->getBody()->getContents();
  }

  private function attachments(): array
  {
    return [
      [
        "color"=> "#f2c744",
        "blocks"=> [
          [
            "type"=> "section",
            "text"=> [
              "type"=> "mrkdwn",
              "text": "This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and <https://google.com|this is a link>"
            ]
          ],
          [
            "type"=> "image",
            "image_url"=> "https://i1.wp.com/thetempest.co/wp-content/uploads/2017/08/The-wise-words-of-Michael-Scott-Imgur-2.jpg?w=1024&ssl=1",
            "alt_text"=> "inspiration"
          ]
        ]
      ]
    ];
  }
}
```

通知を送りたいタイミングに下記を記述。
```php
User::first()->notify(new Slack($message));
```

## 実際の通知がこんな感じになる

<img width="698" alt="スクリーンショット 2022-06-28 17 54 18" src="https://user-images.githubusercontent.com/76086661/176138592-5779af0a-34fb-406a-a170-1520d1e753fe.png">


## 参考

https://readouble.com/laravel/9.x/ja/notifications.html
PDFに署名があるかどうかを判定する方法（電子帳簿保存法のため）

コロナの影響もあって世の中テレワークや電子化だと良くなった事も色々ありますね！

さて、今回はそんな中ちょっとニッチな話題「電子帳簿保存法（略して電帳法！）」のためのチップスになります。
この情報を開示する事でダメ出しとか賛同とかもらえたらほんっと助かりますOrz

#### 電帳法とはざっくりなんだ？
知ってる人はここ読み飛ばしてね。電帳法とは本当にざっくりいうと、こんな感じ
①PDFファイル化した領収書とかをハッシュ化する
②そのハッシュ化した文字列が今現在存在した事を第三者機関に証明書（タイムスタンプ）を発行してもらう
③そのPDFファイルにその証明書を内蔵させる（10年とか有効だよ）
④紙は不要！さらば！


#### 何が課題か？
理屈とか経緯どうでもいい方はここも飛ばしてね！
証明書は10年とか有効な訳ですが、証明書の延長処理というのがあります。その時に他所で勝手に付与されてたタイムスタンプが邪魔になる事があるんです。具体的には「A社がB社に領収書をPDFで送ってきて、既にタイムスタンプが付与されてました」というパターンです。
この時B社は電帳法において、そのPDFをどうしたらいいのか？がふわっとしてるんですね。（国が決めるべきと思いますが）

ちなみにそのA社のタイムスタンプが有効期限切れてから延長処理をした場合はとある第三者機関のライブラリだとエラーが発生したりします。
そのため、考えた案は「A社のPDFにタイムスタンプがついていたら、B社が用意したPDFの添付ファイルとして保存し、それにタイムスタンプ押しちゃおうよ」っていう話です。

#### 本題：どうやってタイムスタンプの存在を検知するか？

PDFBoxの実装を追い切れてないけど、PDFBox使って検知するなら

```java
// 1つでもSignatureFieldsに何かあるならタイムスタンプある！
PDDocument.load(new File("A社領収書.pdf")).getSignatureFields().size() > 0
```
とかで判定出来そうですね。

PDFをテキストデータとして考えたら以下のような方法もいけそう。
```bash
// 参考リンク：[なんとなく分かった気になるPDF電子署名仕様入門2]のP12　署名辞書を参考にね！
$ grep -a -e "Type/DocTimeStamp" -e "Type/Sig" A社領収書.pdf | wc -l
1  ←１つ署名があるって意味です。
```

bashのコマンドサンプルちょっと載せておきます。参考までに！
バイナリーの部分が可読性悪くするので適度な場所に改行入れて見たい所だけ見てるサンプルです。
```bash
$ cat A社領収書 | sed 's/\(ByteRange\)/\n\1/g' | sed 's/\(\/Type\)/\n\1/g' | grep -a -e "Type/DocTime
Stamp" -e "/Type/Sig" -e "/Type/DSS"
/Type/DocTimeStamp/SubFilter/ETSI.RFC3161/M(D:20140912135930+09'00')/
/Type/DocTimeStamp/SubFilter/ETSI.RFC3161/Filter/XXXXXX第三者業者XXXX/
/Type/DocTimeStamp/SubFilter/ETSI.RFC3161/Filter/XXXXXX第三者業者XXXX/
/Type/DSS/VRI 80 0 R/Certs 81 0 R/CRLs 86 0 R>>
```
ByteRangeと/Typeの手前で改行を入れて、TypeがDocTimestampとSigとDSSだけ出力してます。
ちなみにDSS出力してるのはLTV対応とか確認するのに使えるかなと。

でわでは。

##### 参考リンク
なんとなく分かった気になるPDF電子署名仕様入門2
http://www.langedge.jp/download/jnsa/20131030-SUTF-4-pdfsign.pdf
JavaScript: ドラッグとクリックの識別

JavaScript でドラッグとクリックをどうやって識別するのか、 調べたことをまとめました。

## ポイント

いずれも `mousedown`, `mouseup` のイベントが発生するが、 `mousemove` はドラッグの時にしか発生しないので、 `mousemove` の有無で区別する。

## イベント発生の順序

### クリックの場合

`mousedown` → `mouseup`

### ドラッグ(アンド ドロップ)の場合

`mousedown` → `mousemove` → `mouseup`

## 簡単なサンプルコード

```js
const target = document.getElementById('target')
let isClick = true

target.onmousedown(() => {
  isClick = true
})

target.onmousemove(() => {
  isClick = false
  if (!isClick) {
    // ドラッグ（マウス移動）中の処理
  }
})

target.onmouseup(() ==> {
  if (isClick) {
    console.log('CLICKED')
  } else {
    console.log('DRAGGED')
  }
})
```

`mousedownn`, `mousemove` イベント発生時にフラグを変化させることで、 `mouseup` イベント発生時にクリックだったのかドラッグだったのかがわかるようになります。

ドラッグが始まると 即座にフラグが反映されるので、 ドラッグ中の処理を行うことも可能です。
【Shopify Liquid】メタフィールドに登録した画像を表示する

メタフィールドを設定すると通常では管理画面で取り込めない情報を登録することができます。
コンテンツタイプでファイルを選択すると画像ファイルも登録することができます。
Liquidでメタフィールドに保存した画像ファイルの表示方法をまとめます。

## メタフィールドの定義
管理画面の メタフィールドページで、メタフィールド定義を追加します。
コンテンツファイルはファイルを選択します。
詳しくは以下を参照
https://help.shopify.com/ja/manual/metafields/metafield-definitions

## 画像ファイルの表示
定義したメタフィールドに画像を追加したら、Liquid でメタフィールドに登録した画像を表示します。

```liquid:
<img src="{{ blog.metafields.custom.picture | img_url: 'master' }}" alt="picture">
```

オブジェクト部分の書き方は以下のような形式です。
- 構成部分　（商品product、ブログblog など）
- ネームスペース　デフォルトは `custom`
- キー　任意の文字列
```
構成部分.metafields.ネームスペース.キー
```

また、src属性に埋め込むために`img_url`フィルターを適用し、サイズを指定します。
今回、`master` を指定しましたが、これは元画像のサイズを取得します。
サイズの調整方法は以下の記事が参考になります。
https://ec-penguin.com/blogs/shopify/modify-img-url-size-liquid
PowerShell で LINE WORKS にメッセージ送信 (LINE WORKS API 2.0)

## PowerShell で LINE WORKS にメッセージ送信

PowerShell のコマンドレット操作で LINE WORKS のトーク BOT API を用いて、任意のメンバーやトークルームにメッセージを送る方法です。

以下の旧 API 対応記事の API 2.0 対応版です。
https://qiita.com/iwaohig/items/92a3b5b4af288c22a96a

## 事前作業

LINE WORKS でトークボット API を利用するため、LINE WORKS の Developer Console と管理画面で API 2.0 や Bot の設定を行ってください。

## トーク Bot API によるメッセージ送信

LINE WORKS の API 2.0 の トーク Bot API の解説はこちらのページです。
https://developers.worksmobile.com/jp/reference/bot?lang=ja

PowerShell の Invoke-RestMethod コマンドレットで、トーク Bot API を実行します。

{Server Token} → トークンを入力
{botId} → botId を入力
{userId} → 送信先の userId を入力

```
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json")
$headers.Add("Authorization", "Bearer {Server Token}")

$payload = @"
{
    "content": {
        "type": "text",
        "text": "$args"
    }
}
"@
$Body = [System.Text.Encoding]::UTF8.GetBytes($payload)
Invoke-RestMethod 'https://www.worksapis.com/v1.0/bots/{botId}/users/{userId}/messages' -Method 'POST' -Headers $headers -Body $body
```

PowerShell で実行。
![l_1517737_1983_1a9b726e4b0641cb930b568180f41c08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/9a7efae7-ddd7-ef67-4a75-ec61494c4483.png)

指定した Bot からメッセージが届く。
![l_1517737_1984_1c48813f403b385983f8b86a32b5a293.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396107/67db074d-bc0d-58dd-97bf-932249280abf.png)

以上です。
【Python】WebスクレイピングでHTML内のscriptタグにあるJavaScriptの変数（JSON）を抽出したい

Webスクレイピングでは通常、クローラーで取得したHTMLの中から、metaタグにあるページのメタ情報やtableタグにある表形式のデータなどを抽出する。
scriptタグの中に埋め込まれた動的スクリプト内で、varで宣言されたJavaScriptの変数（JSON）に欲しい情報が入っている場合、下記のような方法で抽出できる。

# 環境

Python 3.9
macOS

# コード

```sample.py
import json
import re
from bs4 import BeautifulSoup as bs


def extract_js_var(soup, js_var):
    script = soup.find('script', text=re.compile(js_var, re.DOTALL))
    regex = '(?:var ' + js_var + ' = )({.*?})(?:;)'
    json_str = re.search(regex, script.string).group(1)
    return json.loads(json_str)


if __name__ == "__main__":
    html = '''
    <script>var initialResponse = {"publishDate":"2022-06-28","author":"cgshimo","category":"Python"};</script>
    '''
    soup = bs(html, 'html.parser')
    initial_response = extract_js_var(soup, 'initialResponse')
    print(initial_response)
```

## 実行結果

```zsh
$ python3 sample.py
{'publishDate': '2022-06-28', 'author': 'cgshimo', 'category': 'Python'}
```

# extract_js_var 関数をちょこっと解説

## 1行目

Beautiful Soupのfindメソッドは、第一引数（name）で指定されたタグ名「script」を検索し、最初に見つけた要素を返す。
textで検索対象の正規表現を渡している。ここでは、JavaScriptの変数名を正規表現オブジェクトにコンパイルしている。

## 2行目

varで宣言された変数（JSON）を抽出するための正規表現を定義している。
抽出（キャプチャ）対象は、2番目に丸括弧で囲われた`({.*?})`にマッチする文字列。
丸括弧内の冒頭に`?:`をつけることでキャプチャ対象としないことができ、余計な文字列を簡単に除ける。

この正規表現は、実際のHTMLのscriptタグ内の変数の定義のされ方によって適宜修正する。

## 3行目

2行目で定義した正規表現パターンで、1行目で見つけたscriptタグ要素内の文字列からJSONの抽出を試みる。
末尾の`group`メソッドで、正規表現内の`({.*?})`でキャプチャした文字列を選択している。

## 4行目

3行目で抽出したJSON文字列をPythonの辞書型にパースして、関数の返り値とする。

# このコードの懸念点

ここで使用している正規表現では、下記のようなJSONをパースするときにエラーが出てしまう。
key2のvalueに、};が入っており、これにマッチしてしまう。
JSONにデコードできない文字列として抽出してしまう。
このようなパターンは、JSONの中にスクリプト言語のソースコードが文字列として入ってきた場合などが考えられる。

```
# エラーとなるケース
var initialResponse = {"key1": "value1", "key2": "val};ue2"};
```

正規表現をもう少し工夫して、汎用的な関数にできそう。
form_withがlocal: trueなのにviewが再描画されずにはまった件 

viewで`form_with`メソッドを利用してログインフォームを作ったが、`local: true`にしているにも拘らず、`render 'new'`でログインフォームを再描画できずにはまったのでメモ。

# TL;DR
Rails 7では、form_withに対して`local: true`の代わりにオプションで`data: { turbo: false }`と指定する。

# 環境
```bash
$ ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
$ rails -v
Rails 7.0.3
```
```ruby:routes.rb
Rails.application.routes.draw do
  ...
  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create'
  ...
end
```

# 説明
以下ログインフォームのviewとセッション用のcontroller。

```erb:new.html.erb
...
  <div class="login__forms">
    <%= form_with(url: login_path, scope: :session, local: true) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.submit %>
    <% end %>
  </div>
...
```

```ruby:sessions_controller.rb
class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.find_by(name: params[:session][:name])
    if user&.authenticate(params[:session][:password])
      ...
    else
      flash.now[:danger] = 'ログイン失敗やで'
      render 'new'
    end
  end
end
```
Railsのログを見ると/loginにPOSTしてレンダリングまではできているらしいが、flashの内容などはブラウザには反映されず。
```bash
  Rendering sessions/new.html.erb within layouts/application
  Rendered sessions/new.html.erb within layouts/application (Duration: 5.5ms | Allocations: 1070)
```
`form_with`はRails5以降、デフォルトでAjaxを使おうとするから無効にしたいなら明示的に`local: true`と指定する必要がある。
ググると↑の情報はよく出てくるが、それでもうまくいかなくて困っているのである。

# 正解
正解は**Turboを無効にする**でした。
`rails "local: true" doesn't work`とかで[**ググる**](https://www.google.com/search?q=rails+%22local%3A+true%22+doesn%27t+work)と出てきました。

https://stackoverflow.com/questions/71194558/rails-7-local-non-xhr-request-with-a-form-with-form

Rails 7ではTurboがデフォルトで有効になっており、Turboが有効ではページの部分更新ができないので、今回のような場合には無効にする必要があったのでした。
めでたしめでたし。

結果的にログインフォームのviewはこうなりました。
```erb:new.html.erb
...
  <div class="login__forms">
    <%= form_with(url: login_path, scope: :session,
      data: { turbo: false }) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.submit %>
    <% end %>
  </div>
...
```
以上です。
ありがとうございました。
とあるサーバルームの温度管理　灼熱地獄編

# サーバルームの空調は大事
サーバを大量に設置するサーバルームは空調が重要です。
サーバは稼働している間熱を発するため、一定の温度を保つように空調で調節します。
そんなサーバルームの空調は、多くの場合専用の空調が整備されていますが、建物によっては通常の空調（エアコン）で賄っている場合も少なくありません。

そんな通常の空調でサーバルームの温度を調整している場合は気をつけましょう。
適当に仕事をしているとムスペルヘイム（灼熱の国）が顕現します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/f12bd2f8-7094-f99e-4ca5-3031d4759e7d.png)

## サーバの当然停止
ある日の深夜、保守システムがアラートを発しました。
アラート内容はサーバ停止、なお対象サーバは全サーバ。

なんちゃって冗長構成もなんのその、全サーバダウンという類まれなインシデントです。
すぐさま現場に駆けつけてサーバルームの扉を開けました。
すると・・・

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/d1fa70a4-5169-95f9-129d-2a6a9759eafd.png)
（イメージです）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/79660967-2fcc-503d-e07b-3da1c6a9c36b.png)
（イメージです）

扉を開けた瞬間熱風が体を包み込み、バックドラフトでもくらったかと錯覚しました。

どう見ても熱暴走です。本当にありがとうございました。
体感温度としてはサウナ並かそれ以上でしたが、それなら保守システムより先に火災報知器がなるはず・・・

この建物、煙検知方式だった＼(^o^)／

空調が壊れたのか確認するために制御パネルのある室内へ向かうことにした私は、さながら火災現場に挑むファイヤーファイター

サウナを思い起こす息苦しさに耐えながら制御パネルを確認すると「暖房」の文字が
さらに室温は６０度オーバー

すぐさま冷房に変更し、窓を全開放。
この間数分程度でしたがフルマラソンでもしたのかというレベルの汗をかきました。

その後サーバの点検をしたところ、メモリとCPUがいくつかおじゃん（性能劣化）になってました。
損害額はサービス停止も含め１０００万弱というところ

## 原因は？
インフラ担当者が帰り際に制御パネルに触れて設定を暖房に切替えた事が直接原因。
なお本人に自覚はなく、背負っていたバックが接触したと推察されました。

インフラ屋！！！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/06236c1f-d91f-6d66-f7ed-9866155dcb10.png)


根本原因としては、そもそも制御パネルを操作できる状態にしていたから。

## 対策
制御パネルが封印されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/1fd9a2aa-2dd3-6e8a-b74d-b9d6c4a5ffff.png)


# 　サーバルーム最終退出者は空調をチェックしましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/e1001fa3-3526-294a-768a-8aeb40c9ef5d.png)
シェルの基本と変数

# シェルとは
人間からコンピューターに伝えるためのプログラムです。人とコンピューターを繋ぐ通訳みたいな役割があります。
```カーネル```はOSなどのコンピューターの根幹部分です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748789/a9786a8c-a6d1-f2b4-c06b-7389ac49cd94.png)

>引用　　https://recruit.cct-inc.co.jp/tecblog/os/shell/

# シェルの種類
シェルにもいろいろ種類があります。
sh,bash,csh,tcsh,ksh,zshなどがありますが、bashやzshがよく使いそうな印象です。


こちらの記事も参考にしてください。
https://lanchesters.site/shellscript-for-beginner/


# シェルの基本操作

|入力キー|説明|
|---|---|
|Tabキー|コマンドやディレクトリ名などの文字列を補完する|
|Ctrl + A|行の先頭へカーソルを移動する|
|Ctrl + C|実行処理を中断する|
|Ctrl + D|カーソル上にある文字列を１文字削除する|
|Ctrl + E|行の最後へカーソルを移動する|
|Ctrl + H|カーソルの左側の文字列を１文字削除す|
|Ctrl + L　|画面をクリアして、現在の行だけを再表示する|
|Ctrl + Q|画面への出力を再開する|
|Ctrl + S|画面への出力を停止する|
|Ctrl + Z|実行処理を一時停止する|

>引用　　https://lanchesters.site/shellscript-for-beginner/

# ディレクトリを表すメタキャラクタ

|メタキャラクタ|説明|
|--|--|
|.|	　カレントディレクトリを表す。（ ピリオドを1回入力 ）|
|..|　１つ上のディレクトリを表す。（ ピリオドを2回入力 ）|
|~|　ホームディレクトリを表す。( チルダを入力 ）|

>引用　　https://www.infraeye.com/study/linuxz9.html

# 変数の定義
変数を使う時は```$```を使います。変数を定義して確かめてみます。
```
taro~ % hensu=100
taro ~ % echo $hensu
100
```

bashを立ち上げてみます。
```
taro ~ % bash

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ 
bash-3.2$ echo $hensu

bash-3.2$   #bashに入ったので変数が定義されていない
```

# 変数のエクスポート
別のシェルでも使えるようにするにはエクスポートする必要があります。`

```
$ export 変数名[=値]
```
変数名に＄をつけず　 ```set -o allexport```で新規作成の変数はエクスポートされます。

```
taro~ % export hensu=200
taro ~ % bash

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ echo $hensu
200
```

# 変数削除
変数を削除する方法を見ていきます。


```
$ unset 変数名
```
***変数名に$はつけません。***

実際に削除していきます。
```
taro ~ % echo $hensu
200
taro ~ % unset hensu
taro~ % echo $hensu

taro ~ % 
```

# 環境変数
シェル変数は変数を定義したシェルのみで使えるが```環境変数```は***複数のシェルやプロセスで使えます。***
色々な設定が環境変数として定義されています。

|環境変数名|説明|
|--|--|
|EDITOR|テキストエディタのパス|
|HISTFILE|コマンド履歴が格納|
|HISTFILESIZE|.bash_historyに記録するコマンド履歴の数|
|HISTSIZE|コマンド履歴をメモリに保存する履歴数|
|HOME|ホームディレクトリのパス|
|LANG|言語や文字コード(スケール)|
|PATH|プログラムを実行する際の検索するディレクトリのリスト|
|TERM|端末の種類|
|USER|現在のユーザーが誰なのか|


特に大事なのが```HISTFILESIZE```と```LANG```と```PATH```です。



USERの使い方
```
taro ~ % echo $USER
taro
```

### パスとは
pathが上で出てきましたが、Pathとはディレクトリやファイルの道筋（path）のことです。
***/のようなルートディレクトリからの道筋***を```絶対パス```と言います。
***特定のディレクトリからの道筋***が```相対パス```です。

### パスを追加

```
$PATH=$PATH:ディレクトリのパス
```
```:がないと追加ではなく上書きになります。```

```
taro~ % PATH=$PATH:/opt/bin
```

# 資料
https://wa3.i-3-i.info/word11219.html

https://www.udemy.com/course/linuc-lv1-101/
アーカイブと圧縮について

## 環境
Ubuntu20.4

## はじめに
普段、Windowsを使っていると意識することがないのだが、厳密にいえば、アーカイブと圧縮は別物として認識されます。Linuxを触ると、このことを否をなく意識せざるを得なくなります。アーカイブとは複数のファイルを１つのファイルにまとめたものです。圧縮はかかっていません。圧縮とは文字通り、ファイル容量を小さくするものです。圧縮の逆が解凍になります。そして、アーカイブの逆が展開となります。普段、Windowsを使っていると、アーカイブと圧縮は一遍に行われます。Windowsの環境でアーカイブだけを行うことは、自身の経験上、まずありません。しかし、Linuxを使っていると、アーカイブだけを行うことがあります。アーカイブを行うコマンドは**tar**コマンドです。そして、圧縮を行うコマンドは**gzip**コマンドです。**tar**コマンドだけでも、オプションを指定することで、アーカイブと圧縮とを一遍に行うことができます。

## やったこと
アーカイブと圧縮をやってみます。その前に、まず、テスト用のファイルを３つ作成します。
```
touch test{1..3}.txt
```

３つのファイルを束ねて、test9.tarというアーカイブファイルを作成します。アーカイブの拡張子は**tar**になります。
```
tar -cvf test9.tar test1.txt test2.txt test3.txt
tar -cvf test9.tar test{1..3}.txt			           # これでもOK
```

オプションについて説明します。**c**は、新規のアーカイブを作成するオプションです。これは必ずつけます。**v**は、tarコマンドを実行した直後に、圧縮元ファイルの一覧を確認のために表示するオプションです。このオプションはなくても問題ありません。**f**は、アーカイブのファイル名を指定するオプションです。これも必須です。

アーカイブされたファイルの中身を確認するコマンドです。
```
tar -tf test9.txt
```

オプションの**t**は、展開することなく、中身を確認するためのオプションです。
次に、アーカイブファイルを展開します。
```
tar -xvf test9.tar
```

オプションについて説明します。**x**は、アーカイブファイルを展開しますというオプションです。必須です。コマンドを実行すると、３つのファイルがカレントディレクトリに展開されます。同名のファイルが既に存在している場合は上書きされることになります。

３つのファイルを削除してから、展開をしてみます。
```
rm test{1..3}txt
tar -xvf test9.tar
```

３つのファイルが展開されていることがわかります。アーカイブを展開しても、元のアーカイブファイルは削除されません。削除したい場合は、展開後に手動で削除します。
```
rm test9.tar
```

次に、アーカイブと圧縮を一遍に行います。
```
tar -cvzf test9.tar.gz test1.txt test2.txt test3.txt
```

オプションの**z**は、**gzip**の圧縮処理を行うオプションです。アーカイブファイルでないファイルを圧縮するときは、拡張子は**gz**となるが、アーカイブファイルを圧縮するときの拡張子は**tar.gz**となります。拡張子を見ただけで、展開後のファイルが、アーカイブファイルなのか、そうでないのか、判断をつけることができます。

アーカイブと圧縮の両方行われているファイルを、展開・解凍することなく、中身を確認することもできます。コマンドは、アーカイブファイルの中身を確認するときと同じオプションです。
```
tar -xvf test9.tar
```

元ファイルを削除した後、アーカイブ・圧縮されたファイルを、一遍に、展開・解凍します。
```
rm test{1..3}.txt
tar -xvzf test9.tar.gz
```
とある VPS の Ubuntu 20.04.4 LTS で snapd がうまく動かない件について

とある VPS 上の Ubuntu 20.04.4 LTS にて Let's Encrypt をインストールする際につまづいた顛末のまとめ。

# ツンデレ snapd

近頃の Certbot のインストール方法は apt ではなく snapd というパッケージ管理システムによって行うのが良いそうで、残念ながら僕の Ubuntu にはプリインストールされていなかったから、[Certbot 公式の手順](https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal)にしたがってインストールを進めていた。

snapd のインストール自体はエラー等なくスムーズに終了したが、動作確認の下記コードを実行したところエラーメッセージが出た。どうやらインストールはさせてくれるが、ゆうことを聞いてくれるのは別の話みたいだ。

```shell
# snap install hello-world
error: system does not fully support snapd: cannot mount squashfs image using "squashfs": mount:
       /tmp/sanity-mountpoint-080563154: mount failed: 許可されていない操作です.
```

# 右往、左往

エラーメッセージでググって調べると、まず[「これらのパッケージをインストールしてはどうだ？」っというアンサー](https://stackoverflow.com/questions/60859806/chromeos-error-system-does-not-fully-support-snapd-cannot-mount-squashfs-imag)に出くわす。

```shell:NG
# apt install fuse libsquashfuse0 squashfuse
```

しかし、状況は改善しない。他にも「その後リブートしたらうまく行った」などググるといくつか情報が出てきたが、やはり snapd はエラーメッセージを吐き続ける。

同じことで困ってる日本人はいないのか、またはみんな英語がペラッペラなのか、ぜんぜん日本語の HowTo は見つからず。

# 『あなたは基本的に運が悪いですね』

最終的に Let's Encrypt のフォーラムにたどり着く。そこでは『 OpenVZ とか LXC とかの仮想環境ではどうもダメのダメみたい。運が悪かったですね』というような会話がされている。
→ [System does not fully support snapd: cannot mount squashfs image using “squashfs”](https://community.letsencrypt.org/t/system-does-not-fully-support-snapd-cannot-mount-squashfs-image-using-squashfs/132689)

そんな、まさか自分が……！ と思いつつ事実確認してみる。

```shell
# systemd-detect-virt 
openvz
```

# Certbot 公式サイトの Issue にもあがっていた

『OpenVZ や LXC の人に対して snapd 以外でのインストール方法の説明が必要ではないかしら？』と。
[Users on OpenVZ/LXC need non-snap instructions](https://github.com/certbot/website/issues/636)

# じゃあどうすれば？

このページ ([Get Certbot - Alternate installation methods](https://eff-certbot.readthedocs.io/en/stable/install.html#id5)) の中から snap 以外のインストール方法を選べばいいのだけど、僕の環境では他のサービスで Docker を一切使っていないので Pip でいいかなっという感じ。

とりあえずここまで。:smiley::ramen:

【数時間後に追記】

この件についてまったく同じではなさそうだけど Pip でのインストール方法などが参考になるページを見つけたのでリンクを置いておきます。

[IPv6 only環境でcertbotをpipインストールする (Let's EncryptでSSL証明書を自動更新する)](https://takesako.hatenablog.com/entry/2022/05/07/185846)

Certbot 公式だと証明書の自動更新を cron ジョブでまわしたらどう？とか書いてあって「うーん、、個人的には Systemd の方がいいなぁ。。」ってなっていたところ、この記事では Systemd でまわす方法が書いてあって参考になりました。ありがとうございます。

:smiley::ramen:
Nginx経由でHTTP通信の振り分け

前回からの続きです。

https://qiita.com/Fledgling_engineer/items/c3c01ff8c00e5cd8bd5b
## 今回の目的
Nginxのインストールが完了したので、HTTP通信ができるように設定を反映させていきます。
今回重要になってくるファイルが、nginx.confとdefault.confの2つです。
と言っても今回設定に変更を加えるのはnginx.confのみになります。[^1]

今回編集するnginx.confファイルのパス
`/etc/nginx/nginx.conf`

下記記載を参考に設定変更を行う
`server_name FQDN;`
`proxy_pass FQDN:ポート番号/;`
※FQDNとポート番号はご自身が現在使用しているFQDN、ポート番号を当て嵌めて下さい。
```
  include /etc/nginx/conf.d/*.conf;
    server {
        listen                  80 default_server;
        server_name             FQDN;
        proxy_redirect          off;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        location / {
                proxy_pass      http://FQDN:ポート番号/;
        }
    }
```
以上で、完了です。
これで設定したWEBサイトに行けるようになります。
[^1]:Nginx.conf内にincludeを記述し、拡張子が.confのファイルを読み込んでいるため。
ユーザー作成からsudo権限付与まで


- 前提条件
  - rootで作業ができる
 ### ユーザー作成
-gと-uでそれぞれグループ(gid),ユーザーid(uid)を指定し、最後にユーザー名
今回の場合はtaroinfraでやります。また、gid,uidは抜いても構いません
```
useradd -g 1000 -u 1002 taroinfra
```
パスワード設定
```
passwd taroinfra
```
### sudo権限付与
下記のコマンドで、sudoersファイルを編集します。
```
vi etc/sudoers
```
下記記載のように107行目の文章をコメントアウト(頭に#をつける)することでwheelグループに属するユーザー全員がsudoコマンドを使えるようになります。
```.tsx:etc/sudoers
 106 ## Allows people in group wheel to run all commands
 107 # %wheel        ALL=(ALL)       ALL
```
 `etc/sudoers`を抜け下記のコマンドでユーザーをwheelグループに追加
```
usermod -aG wheel taroinfra
```
`id taroinfra`でwheelグループに追加されているかの確認ができます。
```
[root@hoge]# id taroinfra
uid=1002(taroinfra) gid=1000(ks1000) groups=1000(ks1000),10(wheel)
```
#### おまけのユーザー削除
```
userdel -r taroinfra
```
成功してもなにも出ないため注意
ユーザーのホームディレクトリを削除したくない場合は`-r`を抜く
`-r`を抜いた場合はユーザーの削除はされるもののホームディレクトリなどは残ります。
ParameterとArgumentは何が違う？

### Parameter？Argument？
勉強しながら、ParameterとArgument（arg）という単語をよく聞きます。
自分でははっきりと区別がつかなかったため少し調べてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2662678/56047b63-710d-1733-655b-8049adcb3a95.png)
ここに書かれている通り、Parameterはメソッドを宣言する時の変数を表します。下の例ではint aと
int bがParameterになります。
<details><summary>Parameterの例</summary>

```ruby:Parameter
public int add (int a, int b) { 

    return a + b;
}
```
</details>

---

そして、Argumentは、メソッドが呼び出されたときに渡される実際の値とのことです。下の例では1と2がArgumentです。
<details><summary>Argumentの例</summary>

```ruby:Argument
add(1, 2); 
```
</details>

---


ここまでが調べた結果です。おさらいしますと、メソッドを宣言する時に使うのがParameter、
そのメソッドを呼び出す時に使うのがArgumentということでした。
<br><br><br><br><br>
>ソース　https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html