OmniAICreator/Qiita-1.07M huggingface dataset:
[Ubuntu 20.04.3 LTS]MATLAB インストール方法

## はじめに

Ubuntu 20.04にMATLAB R2021bをダウンロードする際、ハマった部分があったので備忘録。
今回は、評価版を使用しています。
評価版のダウンロードは[こちら](https://jp.mathworks.com/campaigns/products/trials.html?prodcode=ML)
試してないですが、ライセンスありの場合も同様にインストールできると思います。

## ハマったポイント

MATLABのインストール先がルート権限ではないと書き込みが出来ず、インストールできない
![Screenshot from 2021-10-19 19-54-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141174/956adcdd-6954-5450-593f-a7532df620b0.png)

## 結論

先に結論から述べると
rootユーザーとしてログインして、インストールする

## 試したこと

### 1. sudo ./install

まずは、rootユーザーとして実行すれば良いかなと思い、sudoで実行してみました。

```shell
$ sudo ./install
```

結果は何も表示されない。

### 2. インストール先のアクセス権限の変更

上記の画像からもわかるとおりデフォルトのMATLABのインストール先は `/usr/local/MATLAB/R2021b/` であるためこのディレクトリのアクセス権限を `777` に変更

```shell
$ sudo mkdir -p /usr/local/MATLAB/R2021b  # もしディレクトリが存在しない場合に実行
$ sudo chmod 777 /usr/local/MATLAB/R2021b
```

結局同じエラーが発生

### 3. pkexec を使用

```shell
$ sudo ./install
```

一番最初に試した上記のsudoを使用したコマンドの実行はGUIでは適用されないと気づき、pkexecを使用してGUIをroot権限で立ち上げようとしました。

```shell
$ pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY /path/to/install  # matlabのinstallファイルのpath
```

結果、何も表示されない。
ログもうまく表示できず、原因は不明。installファイルも読んでみましたが、shellの知識も乏しいのでどこで詰まっているのかわからず、、、。もし、わかる方がいれば、ご教授いただけると幸いです。

## うまく行った方法

最終的に、rootユーザーでログインしてしまえば、インストールできるのではないかという仮説のもと、試したところインストールできました。
ちなみに、rootユーザーでログインすることは、Ubuntu的には非推奨であるのでここから先は自己責任でお願いいたします。

まずは、rootユーザーのログインパスワードを設定します。
すでに設定している方は無視して結構です

```shell
$ sudo passwd
```

現在のパスワードと新しいパスワード、パスワードの再入力が聞かれるので指示通りに入力する

次に、GNOME3の設定ファイルを少しだけいじります。
修正するファイルは `/etc/gdm3/custom.conf` と `/etc/pam.d/gdm-password` の2種類です。

```/etc/gdm3/custom.conf
[security]
AllowRoot=true
```

securityの部分に `AllowRoot=true` を追記して、上書き保存。

```/etc/pam.d/gdm-password
#%PAM-1.0
auth   requisite       pam_nologin.so
auth   required        pam_succeed_if.so user != root quiet_success     <- この部分をコメントアウト
```

上記は、rootユーザーとしてログインできないようにする設定している箇所なので、上記コードで示した部分をコメントアウトして、上書き保存。

修正が終わったら、再起動し、ログイン画面でrootユーザーとしてログイン。

```shell
$ ./install
```

上記を実行してMATLABのダウンロード手順に従って進めれば、無事終了。

## 感想

rootユーザーとしてログインすれば、権限周りは解決でき、インストールまですることが出来たがrootユーザーとしてログインすると全権限を持っているため今回のように通信を行うような処理を行うものは少し怖いと感じました。
もし、代替案があれば共有していただけると幸いです。

## 参考文献

[https://jp.mathworks.com/matlabcentral/answers/478492-linux-os-ubuntu](https://jp.mathworks.com/matlabcentral/answers/478492-linux-os-ubuntu)
[https://qiita.com/chicken9/items/65799a63cccb9a4a947a](https://qiita.com/chicken9/items/65799a63cccb9a4a947a)
[https://cpoint-lab.co.jp/article/201904/9231/](https://cpoint-lab.co.jp/article/201904/9231/)
[https://linuxconfig.org/how-to-allow-gui-root-login-on-ubuntu-20-04-focal-fossa-linux](https://linuxconfig.org/how-to-allow-gui-root-login-on-ubuntu-20-04-focal-fossa-linux)
[https://www.youtube.com/watch?v=FgbkbwIEj1E](https://www.youtube.com/watch?v=FgbkbwIEj1E)
ABC220 C - Long Sequence を解いた

https://atcoder.jp/contests/abc220/tasks/abc220_c

![abc220c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/25a2a587-ae53-9095-e082-aa93f88cc8ae.png)
![abc220c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/6f6349bc-cab5-c3f9-099b-76f5b3b7ab31.png)
![abc220c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/fee817e2-d5f7-baa4-180c-38ec03682bcc.png)

X を超えるた項数であることに注意。

```abc220c.py
N = int(input())
A = list(map(int,input().split()))
X = int(input())

base = sum(A)
cnt = X//base
ans = 0
#print(cnt)
if cnt == 0:
    for i in range(N):
        ans += A[i]
        if ans > X:
            print(i+1)
            exit()
else:
    num = X%base
    #print(cnt,num)
    for i in range(N):
        ans += A[i]
        if ans > X-base*cnt:
            print(N*cnt+i+1)
            exit()
```

もう少し簡潔に書いてみよう。

```abc220c.py
def solv():
    N = int(input())
    A = list(map(int, input().split()))
    X = int(input())

    cnt = X // sum(A)
    num = sum(A) * cnt

    if cnt == 0:
        buf = 0
        for i in range(N):
            buf += A[i]
            if buf > X:
                print(i + 1)
                break
    else:
        buf = num
        for i in range(N):
            buf += A[i]
            if buf > X:
                print(i + 1 + cnt * N)
                break
solv()#52ms
```

cnt の数で場合分けしてるけど、
不要じゃね！？っと気付く。

```abc220c.py
def solv():
    N = int(input())
    A = list(map(int,input().split()))
    X = int(input())
    cnt = X//sum(A)
    num = sum(A)*cnt
    buf = num
    for i in range(N):
        buf += A[i]
        if buf > X:
            print(i+1+cnt*N)
            break
solv()#56ms
```

書きながら自分の考えを整理し、ココはまでは大丈夫と
自分をなだめながら本番に臨んでいる自分からすると前者の方が精神的にストレスが少なくて良い。
もっと実力が付けばサクッと後者の記述で切り抜けられるのだろう。
【Java】Byte配列を無限に連結したい

# やりたいこと
JavaでByte配列を扱う中で配列を連結させたい場面がありました。
しかも、ときには2つ、ときには3つ、、というように。
Kotlin版はこちら [【Kotlin】ByteArrayを無限に連結したい](https://qiita.com/hyuta/items/3ee62314fd2b21c4d515)

# 追記
コメントいただきましたが、Arrays.stream()を使うとよりスッキリ実現できます。(ありがとうございます。)

```Java
private static byte[] concatByteArrays(byte[]... arrays) {
    return Arrays.stream(arrays)
        .collect(ByteArrayOutputStream::new,
            (bos, bytes) -> bos.writeBytes(bytes),
            (left, right) -> left.writeBytes(right.toByteArray()))
        .toByteArray();
}
```

# 結果
Byte配列はByteBufferで扱い、引数は可変長引数で受け取ることで対応しています。

```Java
class ConcatArrayTest
{
    private static byte[] concatByteArrays(byte[]... arrays) {
        if (arrays.length <= 1) {
            return arrays[0];
        }
        int length = 0;
        for (byte[] array : arrays) {
            length += array.length;
        }
        ByteBuffer buffer = ByteBuffer.allocate(length);
        for (byte[] array : arrays) {
            buffer.put(array);
        }
        return buffer.array();
    }
    
    public static void main (String[] args) throws java.lang.Exception
    {
        byte[] array1 = {1, 2};
        byte[] array2 = {3, 4, 5};
        byte[] array3 = {6, 7, 8, 9};
        
        byte[] concatArray1 = concatByteArrays(array1);
        System.out.println("array1 : " + Arrays.toString(concatArray1));
        byte[] concatArray2 = concatByteArrays(array1, array2);
        System.out.println("array2 : " + Arrays.toString(concatArray2));
        byte[] concatArray3 = concatByteArrays(array1, array2, array3);
        System.out.println("array3 : " + Arrays.toString(concatArray3));
    }
}
```

Output

```
array1 : [1, 2]
array2 : [1, 2, 3, 4, 5]
array3 : [1, 2, 3, 4, 5, 6, 7, 8, 9]
```
ABC217 C - Inverse of Permutation を解いた

https://atcoder.jp/contests/abc217/tasks/abc217_c

![abc217c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/4b62373b-3b5f-20ce-1ab4-65e8403c0fc6.png)
![abc217c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/16b0ba63-48ad-ab77-b214-914950393df6.png)
![abc217c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/0137bfe9-2048-8936-bf26-8919c0dd6201.png)

文章の通りに打った。

```abc217c_r1.py
N = int(input())
P = list(map(int,input().split()))

lis =[0]*N

for i in range(N):
    lis[P[i]-1] = str(i+1)

print(" ".join(lis))
```

別解

```abc217c_r2.py
N = int(input())
P = list(map(int,input().split()))
num = [0]*N

for i in range(N):
    num[P[i]-1] = i+1

print(" ".join(map(str,num)))
```

**学んだこと**
map は for 文なしで一括変換が出来るって代物だと初めて知った。

https://www.modis.jp/staffing/insight/column_127/

**map(int,input().split())** は分割した入力 input().split() を一括で int(整数) に変換しているわけだ。
以下を参考にすると、そこそこ早いらしい。

https://tech-lab.sios.jp/archives/21127

っと言うことは以下の記述も同じ意味になり、
何やっているのかイメージしやすい

```change_format.py
#approach1
lis = list(map(lambda x:str(x),lis))

#approach2
lis = list(map(str,lis))
```

***

あれから。。
文字で扱うのも良いが、
以下のようにも書けるようだ。

```abc217c.py
N = int(input())
P = list(map(int,input().split()))
S = [0]*N

for i in range(N):
    S[P[i]-1] = i+1

print(*S)
```
JavaScriptのコールスタックとホイスティングについて

##コールスタック
言葉の定義としては実行中のコードがたどってきたコンテキストの積み重ねになります。

```JS:JS
function a() {
}
function b() {
 a();
}
function c() {
 b();
}

c();
```
この関数を実行した際にはまず、グローバルコンテキストが生成されます。次にｃの関数が呼ばれる際にｃの関数コンテキストが生成されます。次にｂの関数が呼ばれるのでｂの関数コンテキストが生成され、同様にaのコンテキストが生成されます。イラストで確認しましょう。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/762847f9-1ba9-f14f-0051-d228a4131773.png" width=50% >

実行中のコンテキストは常にコールスタックの一番上のコードになります。aのコードの実行が終了するとaは消滅し、次にb,c,グローバルの処理まで進み最後には全て消滅します。
![スクリーンショット 2021-10-16 18.19.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/e1f7b814-9c4a-d1a3-fdda-800830ce2019.png)

実際にブラウザで確認するとイラストと同様の現象が確認することができます。（※ここでのグローバルコンテキストはanonymousになります。）

##ホスティング
コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリーに配置すること。具体的な様子をブラウザから確認しましょう。

まず関数aを用意し、呼ばれたタイミングでconsoleに出力するようにします。

```JS:JS
function a() {
 console.log("called")
}
```

関数を呼び出してみましょう。

```JS:JS
function a() {
 console.log("called")
}
a();
```

![スクリーンショット 2021-10-16 19.15.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/c77fe92f-4608-a317-7d53-06e413e98380.png)
出力を確認できます。
関数の呼び出しを宣言前に書いてみましょう。

```JS:JS
a();
function a() {
 console.log("called")
}
```
![スクリーンショット 2021-10-16 19.15.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/4d06e432-d27e-2cf7-1dd9-231553ae38fa.png)
同様に出力を確認できます。
この理由は、関数や変数の宣言はコードの実行前（この場合はa()のこと）に既にメモリに配置されているからです。なので関数の宣言前に関数の実行を書いたとしても問題なく関数は実行されます。

続いて変数の場合も見ていきましょう。まず、varから見ていきましょう

```JS:JS
var a = "called"
console.log(a)
```
![スクリーンショット 2021-10-16 19.15.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/4d06e432-d27e-2cf7-1dd9-231553ae38fa.png)
出力を確認できます。
次に変数宣言を後にもってきましょう。

```JS:JS
console.log(a)
var a = "called"
```
![スクリーンショット 2021-10-16 19.30.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/a9605d10-d88e-a6b0-3d96-f94227ce861b.png)
値が未定義という結果になりました。これはconsole.logで値を呼んだ時、まだbに値が格納されていないから起こる現象になります。

let,constについても見てみましょう。

```JS:JS
console.log(a)
let a = "called"
```
![スクリーンショット 2021-10-16 19.36.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/f9e5bade-e365-8eea-edd1-2d1abc0239a7.png)
エラーが出力される結果になりました。これはaというメモリ自体は確保されていますが、undefinedの初期化がJavaScriptエンジンで行われないからになります。

##参考
Udemy： 【JS】ガチで学びたい人のためのJavaScriptメカニズム
メモ：node.jsでPostgreから簡単なデータをJSONで返す

手元で簡単にPostgres+Node.jsでRestAPI的なテストをしたい
という事もあるので自分用のメモ。
DB設計は別でやって、select文を発行してデータを受けたい。ORマッパー等は使わない。
（ORマッピングが嫌い、というのもある。SQLと実行計画は意識するべきだろ派）

環境はWindows
Node.jsとPostgresのインストールは省略。

## express

パッケージ管理のnpmを使う前にプロキシ設定が必要な場合は行う

```
call npm -g config set proxy http://xxx:8080
call npm -g config set https-proxy http://xxx:8080
```

プロジェクト用のディレクトリを作成し移動。
npm install express
でexpressをインストール。(新しいnpmではsaveオプション不要、らしい）

とりあえずexpressの動作見るだけならこんな感じ

```
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => res.send('Hello World!'));
```

## postgres

今回はpostgresからデータを検索したいので
npm install pg
でモジュールをインストール

postgresには

```
create table test(
	'id' integer,
	'name varchar(50)
);
```
のようにしてテーブル作成後適当にデータを投入してテスト。
	
検索して、とりあえずJSONで返すだけなら

```
const express = require('express');
const pgClient = require('pg');
const app = express();
const port = 3000;

var client = new pgClient.Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'postgres',
    password: 'rU9PYNKh',
    port: 5432
});

var pgSelect = {
    text: 'SELECT id,name from test where id=$1',
    values: [0],
}

client.connect()

app.get('/', (req, res) => 
	{
		var reqid = req.query.id;
		
		pgSelect.values = [reqid];
		
		client.query(pgSelect)
		.then(result => {return res.json(result.rows[0])} )
		.catch(e => { console.error(e.stack); return res.json({"error":e.stack}); }) // テスト用なのでそのまま返す
		
	}
);

app.listen(port, () => console.log(`Example app listening on port ${port}!`));
```

※ アローで省略するやつはスッキリという人もいるが、後から見て分かりづらい気がするのでreturnとかを付けるようにしている



postで登録したい場合はリクエストのボディを取得する必要があるので
npm install body-parser
でインストールしてから利用する。
トランザクションを考えない場合は以下

```
const express = require('express');
const pgClient = require('pg');
const app = express();
const bodyParser = require('body-parser');
const port = 3000;

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var client = new pgClient.Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'postgres',
    password: 'password',
    port: 5432
});

var pgSelect = {
    text: 'SELECT id,name from test where id=$1',
    values: [0],
}

var pgInsert = {
    text: 'INSERT INTO test(id,name) VALUES ($1,$2)',
    values: [0,'sample'],
}

client.connect()

app.get('/', (req, res) => 
	{
		var reqid = req.query.id;
		
		pgSelect.values = [reqid];
		
		client.query(pgSelect)
		.then(result => {return res.json(result.rows[0]);} )
		.catch(e => { console.error(e.stack); return res.json({"error":e.stack}); }) // テスト用なのでそのまま返す
		
	}
);

app.post('/', (req, res) =>
	{
		var reqid = req.body.id;
		var reqname = req.body.name;
		
		pgInsert.values = [reqid,reqname];
		
		client.query(pgInsert)
		.then(result => {return res.json(result.rows[0]);} )
		.catch(e => { console.error(e.stack); return res.json({"error":e.stack}); }) // テスト用なのでそのまま返す
	}
);

app.listen(port, () => console.log(`Example app listening on port ${port}!`));
```

Chromeの拡張機能のARCでBodyのcontentTypeをapplication/jsonにしてから本文にJSONを書いてPOSTでテスト。


次はもう少し普通に使える環境を。
EC2のSSH接続がConnection refusedになったときに疑うこと


あるネットワーク環境を用いた場合に
WindowsPCのPowerShellから、秘密鍵(.pem)とElasticIPでのシンプルなSSH接続ができなくて下記のエラーが出ました。

```
ssh: connect to host ip アドレス port 22: Connection refused
```


EC2はパブリックサブネットにあり、他のネットワークからの接続はできている状態です。

思わぬところに原因があったので書いておきます、、

# Connection refusedで調べたときに疑ったこと

- 秘密鍵の指定が正しいか確認

- セキュリティグループが正しいか確認

- PCのファイヤーウォールを切ってみる

- EC2が起動しているか確認
 
すべて確認しましたが問題は見当たりませんでした。


# たどりついた原因
- セキュリティグループでインバウンド(port22)に指定しているIPでSSH接続がされてない

    - モバイルWiFiなどではブラウザで確認できるIPアドレスとSSH接続に使用されるIPアドレスが異なる場合があるみたいです

# 確認方法と解決策

#### 別の接続可能なネットワークからEC2にSSH接続し、 SSH接続のログを表示
 
    
    sudo cat /var/log/secure
    
#### ログから、それらしいIPをさがす
    
    Received disconnect from xxx.xxx.xxx.xxx port xxxx


   これがセキュリティグループに指定しているIPアドレスと微妙に異なっていることに気づきました。
   このIPアドレスをセキュリティグループに設定することで無事に接続ができました。



#### 余談
ネットワークの知識が非常に乏しいことに気づかされました（文面から読み取れると思いますが、、）
モバイルWiFIではIPアドレス自体の変動も頻繁にあるようなので、
そもそもSSH接続が必要な開発をモバイルWiFiで行うこと自体が厳しいというお話かもしれませんね。
スタバとかでそれっぽい雰囲気で作業してる人とかはどうしてるんだろ～～
Macでローカルネットワークのホスト名とIPを検索

IPからホスト名検索

```terminal
smbutil -v status -ae 192.168.100.150

//下記情報が出力される
NetBIOS Name                     Number  Type    Description
QNAP220                          0x00    UNIQUE  [Workstation Service]
QNAP220                          0x03    UNIQUE  [Messenger Service]
QNAP220                          0x20    UNIQUE  [File/Print Server Service]
WORKGROUP                        0x00    GROUP   [Domain Name]
WORKGROUP                        0x1e    GROUP   [Browser Service Elections]
```


ホストからIP検索

```terminal
smbutil lookup QNAP220

//下記情報が出力される
Got response from 192.168.100.150
IP address of QNAP220: 192.168.100.150
```
VirtualBox Guest Addition がまた壊れたよ。

## TL;DR
* VirtualBox もしくはホストのOSアップデートを行った
* 起動時にエラーが出た
* ホストのツールとゲストのツールでバージョンが合わなくなった
* ゲストにログインして kernel 系をアップデート

## エラー詳細
`vagrant up` を実行したら、以下のエラーが出た。

```cli
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

/usr/sbin/rcvboxadd setup

Stdout from the command:

VirtualBox Guest Additions: Starting.
VirtualBox Guest Additions: Building the VirtualBox Guest Additions kernel 
modules. This may take a while.
VirtualBox Guest Additions: To build modules for other installed kernels, run
VirtualBox Guest Additions:  /sbin/rcvboxadd quicksetup <version>
VirtualBox Guest Additions: or
VirtualBox Guest Additions:  /sbin/rcvboxadd quicksetup all
VirtualBox Guest Additions: Kernel headers not found for target kernel 
3.10.0-1160.45.1.el7.x86_64. Please install them and execute
 /sbin/rcvboxadd setup


Stderr from the command:

modprobe vboxguest failed
The log file /var/log/vboxadd-setup.log may contain further information.
```

注目すべきは以下。
`Kernel headers not found for target kernel 
3.10.0-1160.45.1.el7.x86_64. Please install them and execute
 /sbin/rcvboxadd setup`
kernel のバージョンが想定と違っているということらしい。

## 対応
ゲストにログインして(エラーは出ているが、仮想マシン自体は立ち上がっているのだ)、以下のコマンドを実行する。

```cli
sudo yum -y update kernel && sudo yum -y install kernel-devel kernel-headers make bzip2 gcc
```

## コマンドの理由
おそらく仮想マシンを最初に作ったときにも同じコマンドを実行しているはず。
`VirtualBox Guest Additions` に必要なツール群。
もしかしたら全部はいらないかもしれないし、環境によっては他にも不足しているものがあるかもしれないので、適宜調整する。

---

VirtualBox っていう箱よく壊れるよね。
もう少し頑丈に作ってくれないだろうか。
Amazon商品ページでKindle配信先を自動的に選択する Chrome（またはEdge）の拡張機能を作った（その２）

[Amazon商品ページでKindle配信先を自動的に選択する Chrome（またはEdge）の拡張機能を作った（その１）](https://qiita.com/happykorroke/items/d828a88b4fcba4adfc32)
の続き。

<b>インストール方法</b>
（その１）を参照。コードのデバイスIDに書き換えも（その１）で説明している。

<b>解説</b>
配信先関連のエレメントについては（その１）で説明。

（その２）の特殊な点は以下のとおり。

+ manifest.json の設定でrun_at の属性値が document_start になっている。
+ MutationObserverを利用し、DOM構築中に処理を行っている。


```json:manifest.json
{
  "name": "Kindle DeliverTo Selector",
  "description": "Autoselect deliverTo Kindle device.",
  "version": "2.0",
  "manifest_version": 3,
  "content_scripts": [
    {
      "matches": ["https://www.amazon.co.jp/dp/*",
                  "https://www.amazon.co.jp/*/dp/*",
                  "https://www.amazon.co.jp/gp/product/*",
                  "https://www.amazon.co.jp/exec/obidos/ASIN/*",
                  "https://www.amazon.co.jp/o/ASIN/*"],
      "run_at": "document_start",
      "js": ["content_script.js"]
    }
  ]
}
```

```javascript:content_script.js
'use strict';
(function () {
//デバイスID
const deviceId = 'AL13906LU4VTW';
//これを自分のデバイスIDに書き換えること
//デバイスIDはHTMLソースにてSELECTエレメント(id="deliverTo")を調査すれば分かる

const observeConfig = { childList: true, subtree: true};

//DOM変更時処理
// 本関数はDOMに変更があった際、オブザーバーより呼び出される
function onMutate(mutations) {

	for(const m of mutations) {
		const elem = m.target;

		//ドロップダウンの選択結果を表示するSPANエレメントか判定
		if(elem.tagName === 'SPAN' && elem.classList.contains('a-dropdown-prompt')) {
			do {
				//祖先の配信先divを取得
				const div = elem.closest('div#deliverTo');
				if(div == null) {
					break;
				}
				//SELECTエレメントを取得
				const select = div.querySelector('select#deliverTo');
				if(select == null) {
					break;
				}
				//OPTIONエレメントを取得
				const opts = select.getElementsByTagName('option');
				for(const opt of opts) {
					if(opt.value === deviceId) {
						//エレメントを変更するため、無限ループに陥らないよう変更監視を停止する
						observer.disconnect();

						//SELECTエレメントの値を設定
						select.value = opt.value;

						//elem.dispatchEvent(new Event('change', { bubbles: true }));

						//SPANのデバイス名を書き換える
						elem.innerHTML = opt.innerHTML;

						//監視の再始動
						observer.observe(document, observeConfig);
						break;
					}
				}
			} while(false);
		}
	}
}

//オブザーバを作成
const observer = new MutationObserver(onMutate);

//ロード時イベントの処理登録（オブザーバーを停止させる）
addEventListener('DOMContentLoaded',
	function (event) { observer.disconnect(); }, {capture: true, once: true});

//監視の始動
observer.observe(document, observeConfig);
})();
```
年齢当てAIアプリを作った　続き


#はじめに

「Aidemy受講生が年齢当てAIアプリを作った」の続きの取り組みも**おまけ**として記述したいと思います。**おまけ**というのは、これから先は文章ばかりになってしまったので、読みづらいかなと思うからです。でも、せっかくなので、手短に！サクッと！前回以降に取り組んだことをまとめてみたいと思います。

#前回までの取り組み
機械学習部分をGoogleColaboratoryにて完成させました。


#今回の取り組み
機械学習部分のプログラムが完成しただけでは、アプリはまだできていません。
次に、アプリの見た目を作る→デプロイする。これで、ようやくアプリが完成となります。
なので、今回はアプリの見た目を作る→デプロイする。ここを振り返っていきたいと思います。

#目次
1.アプリの見た目（Html&Css）を作る
2.Herokuでデプロイする前のファイルを準備をする
3.Herokuでデプロイする
4.AIアプリ制作の流れ 
5.さいごに


***その前に、...***
やっておかないとならないことが一つありました。
機械学習のコードをテキストエディタVisualStadioCodeへ書き写すということです。

それでは、予定通り進めていきます。


#アプリの見た目を作る

「アプリの見た目を作る」とは、
つまりHtml&Cssを作成するということです。

今回私はHtml&CssはAidemyの講座で習ったものをほぼほぼ使用しているため、
コードの公開はしませんが、ここでHtml&Css部分でアプリの見た目を完成させました。

#Herokuでデプロイする前のファイルを準備をする
機械学習のコードとHtml&Cssのコードだけではデプロイできません。
他にFlaskでも実はアプリを作成していて、そのファイルもVisualStadioCode内に作ってあります。今回そこもばっさり割愛させていただいてます。

#Herokuでデプロイする
VisualStadioCodeに記述した機械学習部分のコードと
Html&Cssのコード、Flaskで作ったアプリ諸々をHerokuでデプロイします。

いざデプロイ！


***『あなたは「○○．○○○○」歳です。」***

あれ？？年齢が小数点で、しかもその数字は「かぎ括弧」で囲われています。

なぜ、年齢が小数点で出力されてしまったのか？というと、
機械学習の際、学習させやすくするために出てきた数字を255で割算していました。

```python
# データ型の変換＆正規化
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
```
255で割った数字が、そのまま年齢として現れてしまっているからでした。

なので、この部分は
round関数で整数になるように修正しました。

☆修正箇所は前回の機械学習のコードではない別のファイル内です。
前回のコード全文で変更箇所はありません。

修正して、再度デプロイ！！

きちんと整数で年齢が出力されていました♪

この勢いで次は、かぎ括弧をなくすために、ここも修正。
そして、再度デプロイ！！

***「あなたは○歳です」***
無事、狙い通りに出力されました。

これにて、***アプリ完成！！***

やったね〜！！
お酒飲めないけれど乾杯したい気分♪


最後に、AIアプリ制作の大きな流れを振り返って終わりにします。

#AIアプリ制作の流れ
①機械学習部分を制作する
②アプリの見た目をHtml&Cssで作る
③Herokuでデプロイする前のファイル準備をする(Flaskアプリを作る）
④Herokuでデプロイする

#さいごに

AIアプリ制作は、機械学習部分だけではなく、それ以外の部分との両方を作る必要があるため、難易度が高く大変ですが、だからこそやりがいがあり、楽しいのだと思いました。完成した時の感動は、きっとこれから先もずっと忘れられないと思います。

これまで携わってくださったチューターさんには本当に感謝です。一人では到底完成させることができない難しい課題でしたが、最後まで根気強く、丁寧に教えてくださりました。足を向けて眠れません。本当にどうもありがとうございました！
【Laravel】フォームに入力した値を保持する方法（新規登録・編集）

#はじめに
バリデーションエラーになってしまった場合に、入力した値が消えてしまうと、ユーザーに再び全ての値を打ち直させるのことになってしまい、ユーザーに過度の負担を強いてしまいますよね。
そこで、入力した値を保持し、エラーが発生した部分だけを書き換えれば済むように、`oldヘルパ`を使いましょう。

#oldヘルパ
###新規登録

```php:create.blade.php
<input type="text" name="menu_name" value="{{ old('menu_name') }}">
```

このように、value属性にoldヘルパを記述し、引数に`name属性値`を入れることで、エラー発生時に入力していた値を保持してくれます。

新規登録の際は、このような記述で良いのですが、編集の場合も同じように記述にすると、新規登録によってデータベースに登録していた値が表示されず、編集画面を開いた際、フォームには何も値が表示されていない状態になってしまいます。

そこで、編集・更新の場面では、データベースに登録していた値を初期値として表示した上で、エラーが発生したときにはエラー発生時に入力していた値を表示するという形にすることで、ユーザーのストレスを軽減することができます。

その記述の仕方は次のようになります。

###編集・更新

```php:edit.blade.php
<input type="text" name="menu_name" value="{{ old('menu_name', $menu->menu_name) }}">
```

第一引数は新規登録時と同じname属性値ですが、第二引数に`$menu->menu_name`と記述することで、デフォルト値としてデータベースの値を表示することができます。

#おまけ
###textareaの場合

```php:create.blade.php
<textarea name="description">{{ old('description') }}</textarea>
```

```php:edit.blade.php
<textarea name="description">{{ old('description', $menu->description) }}</textarea>
```
シェルエスケープの色付けを削除してテキストエディッタで扱いやすくする

備忘録。

# TL;DR

Mac の zsh 環境で、エスケープシーケンスによる色付けを削除する。

```zsh
alias mono="sed -E 's/'$'\e''\[([0-9]{1,2}(;[0-9]{1,2})*)?[mK]//g'"
```

このエイリアスを `.bashrc` なり `.zshrc` なりに登録しておく。
GNU sed では置換元の正規表現が違うかも…

# 使い方

```zsh
$ mono application.log

# 中身はただの sed なので、もちろんパイプとしても使える。
$ head application.log | mono
```
Firebase Cloud Messaging (FCM)、Apple Push Notification Service (APNs)、HUAWEI Push Serviceをすべてサポートしているサードパーティープラットフォームのまとめ

Firebase Cloud Messaging (FCM)、Apple Push Notification Service (APNs)、HUAWEI Push Serviceをすべてサポートしているサードパーティープラットフォームをまとめてみました。

|プラットフォーム名|ドキュメント|
|:---:|:---|
|[Airship](https://www.airship.com/)|https://docs.airship.com/platform/android/getting-started/|
|[Batch](https://doc.batch.com/)|https://doc.batch.com/android/huawei|
|[Braze](https://www.braze.com/)|https://www.braze.com/docs/developer_guide/platform_integration_guides/android/push_notifications/android/integration/huawei_integration/|
|[CleverTap](https://clevertap.com/)|https://developer.clevertap.com/docs/clevertap-huawei-push-integration|
|[Countly](https://count.ly/)|https://support.count.ly/hc/en-us/articles/360037754031-Android|
|[indigitall](https://indigitall.com/)|https://docs.indigitall.com/en/indigitallsetup/mobilepushquickstart/android.html|
|[Related Digital](https://www.relateddigital.com/)|https://relateddigital.atlassian.net/wiki/spaces/KB/pages/908525653/FCM+and+HMS+Integration+to+RMC|
|[infobip](https://www.infobip.com/)|https://github.com/infobip/mobile-messaging-sdk-huawei|
|[Kumulos](https://www.kumulos.com/)|https://docs.kumulos.com/developer-guide/sdk-reference/huawei/|
|[MoEngage](https://www.moengage.com/)|https://docs.moengage.com/docs/dashboard-configuring-huawei-push|
|[Netmera](https://www.netmera.com/)|https://netmera.readme.io/docs/quick-start-4|
|[Notificare](https://notificare.com/)|https://docs.notifica.re/guides/apps/services/hms/|
|[OneSignal](https://onesignal.com/)|https://documentation.onesignal.com/docs/huawei-sdk-setup|
|[Plot Projects](https://www.plotprojects.com/)|https://files.plotprojects.com/documentation/android/3.13.1/how-to-guides/QuickSync-How-to-get-started/|
|[PROXI CLOUD](https://proxi.cloud/en/)|https://developer.proxi.cloud/android/hcm-integration/|
|[Pushwoosh](https://www.pushwoosh.com/)|https://docs.pushwoosh.com/platform-docs/pushwoosh-sdk/android-push-notifications/huawei-integration|
|[TwinPush](https://twinpush.com/)|https://developers.twinpush.com/developers/android#setup-huawei-hms-push-kit|
|[Vizury](https://vizury.com/)|https://help.engage360.vizury.com/article/44-huawei-sdk-integration-guide|
|[WebInStats](https://www.webinstats.com/)|https://www.webinstats.com/blog/en/help/setup/|
|[WonderPush](https://www.wonderpush.com/)|https://docs.wonderpush.com/docs/huawei-mobile-services-hms-push-notification-support|
Vue.jsの基礎的な文法

## props
親→子に値を渡す時にはpropを使います。

```
props: {
      data: Array
  },
```

アクセス方法
js内 

```
this.data
```
html内

```
<test :data="testNumber"></test>
```
※「:」は「v-bind:」の省略形です。

## computed
データが更新されない限りキャッシュの値を使用しつづける

```
computed: {
  dataNow: function () {
        return Date.now()
   }
},
```

呼び出し方

```
{{ dataNow }}
```

## methods
データに即時性を求める場合に使用する

```
methods: {
    dataNow: function () {
      return Date.now()
    }
 }
```

呼び出し方

```
{{ dataNow() }}
```

```
<div id="#app">
  <button @click=“dataNow”>ボタン</button> 
</div>

```

## mounted
vueのライフサイクルフック(初期化)のうちの1つで
DOMが作成された直後に呼び出される。

```
mounted() {
          this.data = “data” ——>変数を定義したり
          this.getHeight() ——>メソッドを呼び出したり
 },
```

## 参考

https://reffect.co.jp/vue/vue-js-created-mounted-diffrence
https://zenn.dev/naoki_oshiumi/articles/8c99581ca5bdd0
https://cly7796.net/blog/javascript/try-using-vue-js-computed/
https://codelikes.com/vue-props/
フォルダ内のGoogleスライドを一括でPNG化

# GoogleSlidesのPNG化が地味にめんどくさかったのでGASで自動化してみた

Googleスライドは1ページずつしかPNG化できずめんどくさかったので連番で出力しフォルダ格納までを自動化した。

## いままでどうしていたのか

10p以上のスライドを一枚ずつPNG化、それを数ファイルするとなると、かなりめんどくさかった。
さすがに1ページずつはだるかったので、１ファイルずつPDFで出力⇒AcrobatでPNG化⇒バッチ処理で名前変更していたが、まぁまぁめんどくさかった。
またこの場合、PNGに不具合があった場合どこの手順（スライド⇒PDF）かPDF⇒PNG）かがわからず困ることもあった。

## 引っかかったポイント

- PNG化のメソッドが存在しない
[GoogleDevelopers](https://developers.google.com/apps-script?hl=en)を漁ったが見当たらなかった。
コンバート処理とかあればいいのにー。
GUIでファイル⇒ダウンロード⇒PNG形式を選択するようなかたちで解決した。
また、この方法の場合は1ページずつの処理になるので前ページ出力できるようにした。

参考
https://www.ka-net.org/blog/?p=12905

- 大量のスライドにスクリプトを貼るのがめんどう

全ページ出力のスクリプトを書いて満足しつつあったが、そのスクリプトを1ファイルずつ書いたり、ライブラリから引っ張ってくるのがめんどうだった。
そのため、フォルダ内のすべてのスライドに処理ができるようにした。

- スライドデータショートカットがショートカットになっている
スライドデータショートカットがショートカットになっている場合は`getFilesByType(MimeType.GOOGLE_SLIDES)` ではなく
`getFilesByType(MimeType.SHORTCUT)` でショートカットのID
IDを取得した後に　`id.getTargetID` で本体のIDを取得必要がある。

参考
https://developers.google.com/apps-script/reference/drive/file?hl=en


## 書いたコード

```javascript

function slides2Png() {

  let folder = DriveApp.getFolderById("folderID");　//スライドを格納しているフォルダIDを指定
  let files = folder.getFilesByType(MimeType.GOOGLE_SLIDES); ///ショートカットの場合はMimeTypeをSHORTCUTにする  スライドの場合はGOOGLE_SLIDES

  while (files.hasNext()) {

    let file = files.next();
    let id = file.getId();
    //この下はショートカットの場合元ファイルIDを取得する分岐をいれている
    const targetId = file.getTargetId();
    if (targetId != null) {
      id = targetId;
      const presentation = SlidesApp.openById(id);
      const slides = presentation.getSlides(); //スライド枚数
      const presentationId = presentation.getId();
      const fileName = presentation.getName();

      const format = 'png'; //出力形式
      const pngName = fileName.replace(/(^\d{1,3}_)(.*)(_.*$)/, "$2"); //元ファイル名から正規表現で変換

      let parentFolder = DriveApp.createFolder(pngName); //格納するフォルダ名を指定（今回はマイドライブ上に作成）

      let slidesNumber = 1; //スライドPNGに連番をつけるためにslidesNumberをつけておく

      for (const slide of slides) {
        const page_id = slide.getObjectId();
        const file = convertPresentation(presentationId, page_id, format, slidesNumber, pngName, parentFolder);
        slidesNumber += 1;

      }
    }

    function convertPresentation(presentation_id, page_id, format, slidesNumber, pngName, parentFolder) {
      const url = "https://docs.google.com/presentation/d/" + presentation_id + "/export/" + format + "?id=" + presentation_id + "&pageid=" + page_id;
      const options = {
        method: "get",
        headers: { "Authorization": "Bearer " + ScriptApp.getOAuthToken() },
        muteHttpExceptions: true
      };

      const response = UrlFetchApp.fetch(url, options);
      if (response.getResponseCode() === 200) {
        //連番がすべて2桁で出力されるように分岐
        if (slidesNumber < 10) {
          return parentFolder.createFile(response.getBlob()).setName(pngName + "_0" + slidesNumber + "." + format);
        } else {
          return parentFolder.createFile(response.getBlob()).setName(pngName + "_" + slidesNumber + "." + format);
        }
      }
    }
  }
}

```

ベストプラクティスではないとおもいますが、備忘録として置いておく。
LaTeXのBeamerで吹き出しを出す

LaTeXのBeamerでスライドに吹き出しを入れる方法。TikZの`callout`を使って吹き出しを作る。

![20211019_180611.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265545/c0cee054-41f2-4e02-9883-5654400f1562.png)


コード全体は[Gist](https://gist.github.com/ushitora-anqou/8fead2368df9d45f0409ebe6541b4559)に上げたものを参照のこと。

## 定義

プリアンブルに以下を書いておく。

```latex
\usepackage{tikz}
\usetikzlibrary{shapes.callouts,tikzmark,calc}
% Usage: \COLabel{name#1}{text#2}
% e.g.:  \COLabel{psi1}{$\psi_1$}
\newcommand{\COLabel}[2]{%
	\tikz[%
		remember picture,%
		baseline=(#1.base),%
		every node/.style={inner sep=0,outer sep=0}%
	]{%
		\node (#1){#2};%
	}}
% Usage: \CO<page#1>{target name#2}{++(x,y)#3}[opt#4]{text#5}
% e.g.:  \CO<2>{psi1}{++(-4,1.5)}[text width=3cm]{hogehoge fugafuga piyopiyo}
\NewDocumentCommand\CO{d<> m m O{} m}{%
	\begin{tikzpicture}[remember picture, overlay]
		\IfValueTF{#1}{\path<#1>}{\path} (#2.center) #3 node[%
			anchor=center,%
			rectangle callout,%
			fill=red!50,%
			callout absolute pointer={(#2.mid)},%
			font=\bfseries\footnotesize,
			rounded corners,
			#4
		]{#5};
	\end{tikzpicture}}
```

## 使う

吹き出しの先にしたいところに`\COLabel`で名前をつけておいて、実際の吹き出しは`\CO`を使って作る。引数には吹き出しを出したい位置を書く。

```latex
\begin{frame}\frametitle{}
	\begin{center}
		\COLabel{hogel}{}\COLabel{hoge}{ほげほげ}\COLabel{hoger}{}
	\end{center}
	\CO{hogel}{++(-1,-1)}{left}
	\CO{hoge}{++(0,-1)}{middle}
	\CO{hoger}{++(1,-1)}{right}
\end{frame}
```

## 参考にしたもの

- https://tasusu.github.io/tikz.html#id12
コンパイルしたJavaのバージョンとデプロイするJavaのバージョンがズレているとエラーになる話

テスト済みのWARファイルをDockerで動かそうとしたら以下のエラーになってしまった…ということがあったので、原因と解決方法をメモ
java.lang.UnsupportedClassVersionError: ukaf/simple2/WelcomeResource has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 60.0 （クラス[ukaf.simple2.WelcomeResource]をロードできません）
※ビルド～デプロイまでは正常に終わったのですが、以下のリクエストでAPIアクセスしたときにエラーになりました
http://localhost:8080/simple2/webapi/welcome?name=YOURNAME
##原因
タイトルにほぼ答えを書いてしまったのですが、ビルドしたときのJavaバージョンと実行するときのJavaバージョンにズレがあったのでエラーになっているそうです。
ビルド…WARファイルはEclipseで作ったプロジェクトから生成していたので、該当プロジェクトのプロパティを確認したところJava17でした。
※プロジェクトを右クリックで「プロパティー」を選択→「Javaのビルド・パス」をクリックしてJREシステムライブラリを確認
実行…Dcoker runしたときのコンソール画面を確認したところ以下のようなログがあったのでJava11っぽいです。
``19-Oct-2021 08:41:54.371 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /usr/local/openjdk-11``
##解決方法
ビルド時のJavaバージョンをJava11に設定して再ビルド・デプロイ→WARエクスポート→Dockerでビルド・デプロイ したところ、正常に動くようになりました。
##参考
これ↓をやっている中で起きた話なので、詳しい経緯や手順についてはこちらを見ていただけると幸いです
[簡単なJSPとJavaのセットを作ってDockerで動かす](https://qiita.com/mm0kan/items/fac88c4d2352980b3e70)
AtCodeログ：0134 - ABC 221 A

# 問題

https://atcoder.jp/contests/abc221/tasks/abc221_a

# 回答
## 回答1 (AC)
地震のマグニチュードが $X$ 大きいとき、地震のエネルギーの大きさは $32^X$ 倍となるので、$32^{A-B}$ を求めれば良いでしょう。条件より $A-B \le 6$ なので、出力は int 型でおさまります。コードは以下のようになりました。

```C++:abc221a-1.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int a, b;
  cin >> a >> b;
 
  cout << (int)pow(32,a-b) << endl;
}}
```

# 調べたこと
## [AtCoder の解説](https://atcoder.jp/contests/abc221/tasks/abc221_a/editorial) → [公式解説](https://atcoder.jp/contests/abc221/editorial/2721)
マグニチュードを $B$ から $A$ まで変化させる方法と、回答1と同じ解法を紹介しています。

# リンク
## 前後の記事
- 前の記事 → [AtCoderログ：0133 - ABC 221 に参加しました](https://qiita.com/RoadLynton/items/dad7d86b504cc1a32b91)
WP-CLIを使って環境構築

## 前置き

WP-CLIを使ったローカル環境の構築を行なったのでその時にやったことを書いていきます。

## WP-CLIとは

WordPressやプラグインのインストールや更新をCLIで行うための公式ツールです。
WORDPRESS.ORGに[ドキュメント](https://developer.wordpress.org/cli/commands/)があるのでちゃんと公式です。

## WordPressの開発環境を作る時にやること

WordPressの開発環境を手動で構築する際にはだいたい以下の作業を行うかと思います。

- WordPressの最新バージョンを公式サイトからダウンロード
- ローカルにPHPが動く環境を作成
- WordPressのインストール
  - wp-config.phpの作成
  - サイト情報と管理者ユーザの設定
- プラグインのインストール
- 使うテーマのインストール、もしくは自前のテーマを有効化
- 使用しないテーマ、プラグインの削除

この内、`ローカルにPHPが動く環境を作成`以外はWP-CLIで実施することが可能です。
それぞれの作業はWP-CLIを使うと以下の形式に置き換えることができます。

### WordPressの最新バージョンを公式サイトからダウンロード

```bash
$ wp core download --locale=ja
```

これを実行すると実行したディレクトリ内に日本語対応されたWordPressの各種ファイルが展開されます。

### WordPressのインストール

#### wp-config.phpの作成

```bash
$ wp config create --dbname=<DB名> --dbuser=<DBユーザ名> --dbpass=<アクセスするユーザのパスワード> --dbcharset='utf8mb4'
```

DBのホスト名を変更したい場合には`--dbhost`を指定することで変更することができます。

設定しない場合にはデフォルトのlocalhostが使用されます。

#### サイト情報と管理者ユーザの設定

```bash
$ wp core install --url=<サイトのURL> --title=<サイトのタイトル> --admin_user=<管理者ユーザ名> --admin_password=<管理者パスワード> --admin_email=<管理者のメールアドレス>
```

### プラグインのインストール

```bash
$ wp plugin install <プラグイン名>
```

ここでのプラグイン名は[XML Sitemaps](https://ja.wordpress.org/plugins/google-sitemap-generator/)を例にするとURLに出ている`google-sitemap-generator`がプラグイン名になります。

バージョンを指定したい場合には`--version`オプションでインストールしたいバージョンを指定、インストールと同時に有効化したいなら`--activate`をつけてあげればできます。

問題点としてはそのプラグインがインストールされているかどうかしかチェックしていないため、インストールされているバージョンとインストールしようとしているバージョンが違ってもアップデートを自動的に行ったりはしてくれません。

`--force`をつけることで強制的にインストールし直してアップデートをすることが可能ですが、CIの中に組み込んだりすると毎回インストールが走るので一度の実行がそこそこ時間がかかるようになります。

### 使うテーマのインストール、もしくは自前のテーマを有効化

#### 使うテーマのインストール

```bash
$ wp theme install <テーマ名>
```

インストールと同時に有効化する場合にはプラグインと同じく`--activate`をオプションを使うことで可能です。

テーマ名はプラグイン同様にURLのものを指定する必要があります。

#### 自前のインストール

```bash
$ wp theme activate <テーマ名>
```


### 使用しないテーマ、プラグインの削除

#### テーマの削除

```bash
$ wp theme delete <テーマ名>
```

#### プラグインの削除

```bash
$ wp plugin delete <プラグイン名>
```

## コンフィグを使ってオプションを省略

wp-cliを実行するディクレクトリにwp-cli.ymlを作成しておくとオプションのデフォルトを設定できる。

以下、例。

```yaml
path: public

core download:
  locale: ja
  version: 5.8.1

core install:
  url: localhost
  title: Example
  admin_user: supervisor
  admin_email: info@example.com

config create:
  dbname: dummy_wp_local
  dbuser: dummy_wp_user
  dbhost: db
  dbcharset: utf8mb4    
```

設定する細かい内容は[ドキュメント](https://make.wordpress.org/cli/handbook/references/config/)を参照.

管理者、DBのパスワードはオプションで指定するかpomptで入力する想定です。

## 終わりに

通常使う分にはスクリプトにまとめてしまえば共通的に実行できて管理画面をぽちぽちする必要が無いのでだいぶ楽ではある。

ただ、CIに組み込んだりする場合は自前でアップデートがあるかなどのチェックをする必要があるのでだいぶ使いづらい印象です。
インスタンスメソッド、クラスメソッドの違いについて

## はじめに

以前書いた「[インスタンス変数、クラス変数の違い](https://qiita.com/tta0206/items/74059f904fd967526d9b)」が反響がありましたので、
(読んでくださった皆さん、本当にありがとうございます🙏)、
そちらの続編としてメソッドについて説明していきたいと思います。

## インスタンスメソッド

インスタンス変数もしくはインスタンスメソッドを利用する場合に使われるメソッドです。
インスタンスという名前がついている通り、インスタンスに依存する処理を行うので、
メソッドを呼び出す前にインスタンスを作成する必要があります。

## クラスメソッド

インスタンスの中身に関係なく、共通の処理を行うときに利用されるメソッドです。
インスタンスに依存せず、処理内容が不変なことから英語で「静的」という意味の`static`をメソッドにつけます。
そのため、クラスメソッドの中ではそのクラスのインスタンス変数 or インスタンスメソッドは利用できません。(=thisを利用することはできない。)
その代わり、インスタンスに依存してないので、インスタンスを作成していなくても利用することができます。

## 使い分け

使い分けは至ってシンプルで、

**インスタンス変数 or インスタンスメソッドをメソッド内で利用する必要があるか？**

という質問に対して、
**Yes**(どちらか片方は必要)の場合、**インスタンスメソッド**
**No**(どちらも必要ない)の場合、**クラスメソッド**

を定義すればよい。

## それぞれの特徴

|　|インスタンスメソッド|クラスメソッド|
|-----|-------------|---------|
|特徴|インスタンスに依存する処理を行う|インスタンスに依存せず、共通の処理を行う|
|呼び出し方|1.オブジェクトを代入した変数.インスタンス変数<br>2.this.インスタンス変数(※インスタンスメソッドを定義したクラスから作成されたインスタンスを指し示す時に使う書き方)|1.クラス名.クラスメソッド<br>2.オブジェクトを代入した変数.クラスメソッド<br>3.クラスメソッド(※クラスメソッドの定義元であるクラスでのみ可能な書き方)|
|定義|アクセス修飾子 戻り値の型 メソッド名|アクセス修飾子 **staticキーワード** 戻り値の型 メソッド名
|this|使える|使えない
|オーバーライド|できる|できない

## 具体例

```java:Person.java
public class Person {

    private int age;
    private String name;
    private int birthYear;

    Person (int birthYear, String name) {
        this.birthYear = birthYear;
        this.name = name;
        this.age = this.calcAge();
    }

    public int calcAge() {
        return 2021 - this.birthYear;
    }

    public void printBirthYear() {
        System.out.println("あなたは" + this.birthYear + "年生まれです。");
    }

    public static void eat() {
        System.out.println("人間は食べ物を毎日食べる");
    }
}

```

```java:Main.java
public class Main {
    public static void main(String[] args) {
        Person person = new Person(1994, "Taro");
        person.printBirthYear();
        // 以下の2つは呼び出し方が違うだけで、実行しようとしている処理は一緒
        Person.eat();
        person.eat();
    }
}
```

```java:Mainの実行結果
あなたは1994年生まれです。
人間は食べ物を毎日食べる
人間は食べ物を毎日食べる
```

## まとめ

前回の記事と今回の記事ではJavaを使って説明しましたが、定義方法は違えど基本的な思想は他の言語でも
応用できるので、ぜひとも読んでいただければ幸いです。
次回は、thisとsuperの違い+αを記事にしようと考えております。
Workato：HTTPコネクターを含むレシピを他のワークスペースへ共有すると、HTTPコネクターのパラメータが引き継がれない問題と対応方法

# はじめに
Workatoにはレシピ共有（Sharing）機能があり、この機能を利用することで他のワークスペースに対してレシピを簡単に共有することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/7c454b5e-ca48-a8d3-3f9f-f8096ea4b13a.png)


しかし、この機能を利用して他のワークスペースにレシピを共有すると、共有先にインポートされたレシピにおいて、共有元のHTTPコネクター（のアクション）で設定していたURLを含めて、全てのパラメータが引き継がれないという問題があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/468749c6-c6f1-663d-2d50-342d2b334fd4.png)

# 理由
レシピ共有機能を利用して共有されたレシピにおいてHTTPコネクターのパラメータが引き継がれない事象は、Workatoの仕様となります。（おそらく誤った共有によって、プライベートなURLやパラメータの漏洩を避けることを意図して、つまりセキュリティ面の配慮により、そのような仕様になっているのであろうと思います）

# 対応方法
2通りの方法があります。

## １．Recipe lifecycle management を利用する
Recipe lifecycle management を利用してレシピのエクスポート/インポートを行うことで、HTTPコネクターのパラメータをインポート先のワークスペース上のレシピにおいても引き継ぐことが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/c244aa7f-bd6a-613d-1103-82aaec41f870.png)

Recipe lifecycle management については、以下をご参照ください。

https://docs.workato.com/recipe-development-lifecycle.html

## ２．Rubyコネクターを利用する

HTTPコネクターで実装していた処理をRubyコネクターに置き換えていただくことで代替することも可能です。Rubyコネクターで実装した処理は、共有先のワークスペースにおいてもパラメータを含めインポートして利用することが可能です。ただし、Rubyのコードを記述する必要があるため、やや敷居は高くなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64876/b6b9f5ef-7f8d-0e4e-9afd-8c8d754a49c1.png)



共有機能を利用したレシピのエクスポート/インポートは非常に手軽ですが、上記のような制限がありますので、ワークスペース間のレシピ共有は、できるだけRecipe lifecycle management を利用されることをお勧めします。
未経験が1ヶ月でCCNA取得(資格のおかげで案件に入れました！)

初めまして。未経験エンジニアです。
私は今年の7月に転職をして現在に至ります。
初めての記事で読みにくいとは思いますが徐々に改善して
行きますのでご了承下さい！

未経験である自分でもCCNAを取得することが出来ましたので
今からCCNAを取得しようと思っている方に参考になればと思いQiitaを始めました！最後まで見てもらえると助かります！

まず結論ですが、CCNAは暗記がかなりメインなので効率よく
短期で合格するのがモチベーションを下げずに続けることが
出来ると思います。その中でも自分が良くやっていたことを
少しでも共有できたらと思います。

1ヶ月で取得した資格勉強法(CCNA)
①試験の範囲を確認全体像を確認
②合格者の合格体験記を見て実際に学習を始める
　※Ping-tの合格体験記、YouTubeなど。
③Ping-tのCCNAの学習問題に早速取り掛かる。
　※Ping-tを利用するにあたって課金しないと出来ません
④Ping-tの問題集を2〜3周ほどすると基礎知識が高まるので
　大丈夫だと思ったら模擬試験を実施する。
⑤CCNAの黒本や白本などを使って模擬試験を解く
　※短期で取得されたい方は模擬試験から解くことをオススメ
　　白本は用語が分からない時の辞書の感覚で使うと
　　気持ち的にもとても見やすく使えると思います。

　※黒本は問題がPing-tより難しいので個人的に知識が
　　かなり身につくと思います。
⑥Ping-tで全てコンボが取れるようになったり
　模擬試験80%超えてると試験申し込み。

上記の様なやり方で実施していました。
個人的に1番試験で役立ったのは黒本の模試　白本の模試
がとても役立ちました。ネットワークの知識をしっかり身につけたい方は短期より長期で学習するのがオススメだと思います。
短期で資格取得をしたので深いところまでは詳しく説明が出来ないのでもっと時間をかけて学習したらと感じました。

CCNAを取得して役に立ったことを次に書きたいと思います
①案件に入る際に実際に評価されてネットワークの案件に入らことが出来た(ここが1番大事)
②基礎的な事をふんわり理解しているので実際の実務でも
　覚えやすくて実務に取り組みやすい
③資格勉強の継続力が身について、自信にもなるので
　勉強が苦にならない

上記のことを感じました。実際に現場に入ることが出来ましたがCCNAは幅が広いので取得をしてからがスタートだと感じました。実際に用語が飛び交うので実際に学んでいることを
しっかり説明できるぐらい落とし込まないと実務でも役に立つと思います。資格取得でゴールではなく取得してからがさらに
深く学習していくことをオススメします。

今回はこんな感じの内容となります。
少しでも参考になればと思います！
【RSpec】モックを使用したテスト

##環境
Ruby 3.0.2
Rails 6.1.4.1


##モックを使ったテスト

```spec.rb
describe 'XXXXX:XXXX' do
  #モックを作成
  let(:slack_client_mock) { double('slack client mock') }
  subject(:task) { Rake.application['XXXXX:XXXX'] }

  before do
    #chat_postMessageメソッドが呼ばれたらモックを返すように実装を書き換える
    allow(::Slack::Web::Client).to receive(:new).and_return(slack_client_mock)
    allow(slack_client_mock).to receive(:chat_postMessage).and_return(true)
  end
  
  it 'モックのテスト' do
    expect(slack_client_mock.chat_postMessage).to eq(true)
    subject
  end
end
```

##ポイント

- ```allow(モック).to receive(:メソッド名).and_return(hogehoge)```
モックがメソッドを実行したときに何が返るかを```and_return```で設定

- モックの設定はbeforeの中に書いたほうがテストの見通しがよくなる

##参考

https://qiita.com/jnchito/items/640f17e124ab263a54dd

https://zenn.dev/nogtk/articles/42d672aa85c80a6a886c
【iOS15】 Safari拡張をリリースしてみての感想

#はじめに
iOS15からMacと同じようにSafari拡張機能が使えるようになりました。WWDC2021でも取り上げられ、Xcode13にも専用のプロジェクトテンプレートが用意されるなど、結構気合の入っている印象です。とはいっても、まだまだ日本語の情報が少なかったので今回はアプリを作ってみた立場から、開発に役立ちそうなことをまとめていきたいと思います。

#つくったもの

https://apps.apple.com/jp/app/禁欲フィルタ-継続できるアプリ/id1589931500

作ったものはこちらです。性的に過激なサイトや単語をブロックしてくれるアプリになっています。実際の画面は以下の画像のようになっています。この画像では配慮のため英語を対象としていますが、もちろん日本語にも対応しています。

![過激な単語.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/6ad4e2f7-8646-19a9-be7d-ecaf03d8ce03.jpeg)

スマホで画面キャプチャをする際などに性的な言葉が表示されるのを防ぐこともできるので、配信などでBANを防止したいときにもオススメです。

#感覚はChrome拡張と同じ
正直なところ、仕組みやできることは、ほぼChrome拡張と同じように考えてもらって良いかと思います。後述するようにいくつか違いや注意点はあるのですが、Chrome拡張の開発経験のある方ならスムーズに進められるかと思います。

##Chrome拡張との違いは？
###background処理
当然のことながら、モバイル機器の処理能力には限界があります。そのため以下のように`background`の `"persistent": false`は必須となります。

```javascript:manifest.json
 "background": {
        "scripts": [ "background.js" ],
        "persistent": false
    },
```

###popupボタン
拡張機能にはボタンを押して起動したり、何らかの変化を起こすものがあります。PCなどでは大抵タブ上に表示されたりしてすぐに使えるのですが、iPhoneの場合は少し異なり、一端リスト上のページを生じさせる必要があります。

以下の画像は一例です。Mac版だと常に表示されているボタンをクリックするだけで済む操作も、iOSだといくつかの手順を踏む必要があります。

![過激な単語-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/facaa2d3-5ae0-547c-4d21-74a0b0648cef.jpeg)


また、ボタンを押した後の表示もハーフモーダル状になるようです。大きさなどは変更できるかもしれませんが、そのままだとWebページの半分は隠れたままになってしまいます。




###アプリ本体の存在
上の問題を解決する手段のひとつが、アプリのExtensionでない部分、つまりアプリ本体の活用です。すでにリリースされた他のSafari拡張アプリでは、このアプリ本体の画面から設定変更などを行なっているものもあります。（例：機能を実行しないWebサイトリストの編集、

また、他のアプリと同じようにWidgetやAppleWatchなどとの連携も可能です。このようにさまざまなものと連携させられるところは活用しがいがあるのではないでしょうか。

#コンテンツブロッカーは？
iOSにおけるSafariの機能を補助するものとしてコンテンツブロッカーが既に存在していました。広告ブロックが主な使用用途でしたが、Safari拡張ではそれらに比べてより幅広い操作が可能となっています。

コンテンツブロッカーの扱いが今後どのようになっていくかは分かりませんが、現状はSafari拡張と分けて考えられているようです。コンテンツブロッカーとSafari拡張をひとつのアプリに含めることも可能で、広告ブロックとして有名なAdGuardは両方を実装しています。


#SwiftUIとの関係
Xcodeで`Safari Extension App`から新規プロジェクトを始めると、UIKitベースのものが生成されます。しかしせっかくマルチプラットホームで使えるSafari拡張なので、SwiftUIを使いたいところです。

私のアプリは現在ほとんどSwiftUIを使っていますが、2021年10月時点ではMac版の正式リリースには至っていません。Safari拡張アプリこそSwiftUIの強みを活かせる分野だと思っているので、マルチプラットホームでのリリースを実現したらまた知見を共有したいと思っています。

#App Storeでの審査
セキュリティの問題も他のアプリとは異なるので、リリース前の審査が厳しいのではないかと心配していました。しかし、結果から言うとアプリ内課金の点以外でのリジェクトは一度もありませんでした。

一応英語の資料を添付していたのもよかったのかもしれません。個人差はあるかと思いますが、Safari拡張だからといって特別なにかがあるわけではないようです。

#さいごに
まだまだ`Safari Extension App`自体の情報は公式ドキュメント以外ほとんどない印象ですが、Chrome拡張など他の分野の知見を生かすことができます。個人的に便利そうなものを作るだけでも楽しいと思うのでぜひみなさんも挑戦してみてください。

#公式サンプルコード

WWDC2021で以下のサンプルコードが公開されました。

[ページ上の文字置き換え](https://github.com/artemnovichkov/wwdc21-samplecode/tree/63a99c0fd628e6d3d3c26030b1b216ddf34002df/AdoptingNewSafariWebExtensionAPIs)

[ページの画像や読み込みなどをブロック](https://github.com/artemnovichkov/wwdc21-samplecode/tree/63a99c0fd628e6d3d3c26030b1b216ddf34002df/AdoptingDeclarativeContentBlockingInSafariWebExtensions)
Rubyでソートする方法

# Sortメソッドについてまとめます
Rubyを学習中にソートする機会があったので、備忘録として投稿します。

# ソートとは？
特定のルールに従ってデータを並べること。

## たとえば？
たとえば

- 数字を大きい（小さい）順に並べる
- 名前を五十音順に並べる
- Qiitaの記事を古い（新しい）順に並べる

などがあります。
学生時代の「背の順」や「出席番号順」といった懐かしいものもありますね。

# sortメソッド
配列の内容をソートするメソッド。
[Array#sort](https://docs.ruby-lang.org/ja/latest/method/Array/i/sort.html)のドキュメントによるとによると

>ブロックとともに呼び出された時には、要素同士の比較をブロックを用いて行います。ブロックに2つの要素を引数として与えて評価し、その結果で比較します。ブロックは <=> 演算子と同様に整数を返すことが期待されています。つまり、ブロックは第1引数が大きいなら正の整数、両者が等しいなら0、そして第1引数の方が小さいなら負の整数を返さなければいけません。両者を比較できない時は nil を返します。

とのこと。

## 基本構文
sortメソッドは配列やハッシュに対して使うことができます。

```ruby
配列.sort
ハッシュ.sort
```

返り値は特定のルールに従って並び替えた新たに生成された配列です。

# 実際に書いてみる

```ruby
irb(main):001:0 > [3, 1, 2, 6, 9, 4].sort
=> [1, 2, 3, 4, 6, 9] # デフォルトでは昇順に並び替えられる
```

# こんなプログラムを書くときに使える
3つの整数を読み込み、それらを値が小さい順に並べて出力するプログラム

```ruby
puts gets.split(" ").map(&:to_i).sort.join(" ")
# 4 9 3
# => 3 4 9
```

# 参考にした情報
https://docs.ruby-lang.org/ja/latest/method/Array/i/sort.html
Power Automate 変更セットでBadGatewayエラーが発生した時の対処法

# 現象
Power Automateで変更セットを使用してデータの一貫性を保とうとした際に、BadGatewayエラーが発生してしまった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485970/e803453e-e76f-4261-aa98-18dd806706b5.png)

# 対処法
変更セット、および内部のDataverseへの更新アクションのタイトルに日本語(2バイト文字)を使用しないようにする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485970/c3cbc2da-3a2b-72ff-3c93-7376c0cf0d62.png)

# おわりに
変更セットに関する記事等はあまり無かったので共有させていただきました。
日本語の環境で変更セットを作成した場合のデフォルトのタイトルは「変更セット要求を実行する」なので、デフォルトでは失敗してしまうかと思います。
市民開発者に馴染みが深いPower Automateで日本語名が使えないのはもしかしたらハードルになってしまうかもしれないので、問題なく使えるようになることを期待したいです。
Pythonで 'U+5B57' <--> '字' 変換

# なに
Pythonで `'U+5B57'`<-->`'字'`のような、`'U+xxxx'`という文字列とそれが指し示す文字の相互変換を行います。

ここで注意なのが、「python Unicode　文字列　変換」とかでググるとあまた出てくる`unicode-escape`うんぬんの話ではないです。

# 変換対象
`'字'` ... 文字列
`'U+5B57'` ... 文字列

# 方法

* `'U+xxxx'` --> 文字

```python
u = 'U+5B57'
print(chr(int(u[2:], 16)))
# output: 字
```

* 文字 --> `'U+xxxx'`

```python
kanji = "字"
print(f"U+{hex(ord(kanji))[2:].upper()}")
# output: U+5B57
```
# おわり
以上です。
お疲れ様でした。
WebRTCでトランシーバーアプリを作成

イベント警備や家電量販店で無線を利用したトランシーバー(インカム)を今回スマホブラウザで作成してみました。
この記事ではAgora.io SDKを用いて以下の機能の実装方法を解説します。
 
 - 特定のCHに接続
 - ボタンを押している時だけ発話

利用するSDKはWebSDKになります。
サンプルは[AgoraIO-WebRTC-Transceiver](https://github.com/fu-jimoto/AgoraIO-WebRTC-Transceiver)に公開しています。

##実装内容
実装内容を詳しく見ていきます。

###STEP1
まずは、音声通話への入室を実行します。プルダウンで選択したCHに入室します。
入室時には音声ミュートをしておきます。

```javascript:index.js
async function join() {

  // add event listener to play remote tracks when remote user publishs.
  client.on("user-published", handleUserPublished);
  client.on("user-unpublished", handleUserUnpublished);

  // join a channel and create local tracks, we can use Promise.all to run them concurrently
  [ options.uid, localTracks.audioTrack ] = await Promise.all([
    // join the channel
    client.join(options.appid, options.channel, options.token || null),
    // create local tracks, using microphone and camera
    AgoraRTC.createMicrophoneAudioTrack()
  ]);
  
  //default mute
  localTracks.audioTrack.setMuted(true);//ボタンを押したら発話する仕様なのでデフォルトでミュートにしておく
  // publish local tracks to channel
  await client.publish(localTracks.audioTrack);
  console.log("publish success");
}
```

###STEP2
次に発話するための実装です。

```javascript:index.js
function startup() {
  var el = document.getElementById("talk");
  el.addEventListener("touchforcechange", function(evt){evt.preventDefault()}, false);
  el.addEventListener("touchstart", function(evt){localTracks.audioTrack.setMuted(false)}, false);
  el.addEventListener("touchend", function(evt){localTracks.audioTrack.setMuted(true)}, false);
}
document.addEventListener("DOMContentLoaded", startup);
```
Talkボタンをタッチしている間はミュートを解除します。
ボタンから離れるとミュートをかけます。

touchforcechangeについてはiOS Safariでボタンを長押ししているとコピー等のメニューが出てしまうため、処理を追加しておきました。
（正しい実装なのかは怪しいですが、、、）
![スクリーンショット 0003-10-19 17.16.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/b4bc8952-6c5a-acd3-4021-3a44747e3bec.png)



実装の解説は以上になります。


###補足
実サービスにおいては発話しているのは1名だけという制御が必要になるかもしれません。
その際は、他者が発話中はTalkボタンを無効にするという実装が追加になります。
（複数に同時で発話できてよいかとも思いますが、要件次第かと思います）


##最後に                                                                                                    
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=7b93bf69c53cca240850)   
[<img width="912" alt="スクリーンショット 0001-08-15 13.41.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5d815376-9b89-08fa-9434-6fa7e8e67bcf.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=7b93bf69c53cca240850)
M1  Macでpod installでバスエラーをRossettaなしで回避


CocoaPodsをアップデートしたら`pod install`
`/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.4/lib/ffi/library.rb:275: [BUG] Bus Error`
とかでクラッシュするようになり。
調べるとRossetta使って回避！とかしてる~~残念な~~人達が多かったので、いや、ないわーと。

[Segmentation fault on M1 macOS Big Sur #864](https://github.com/ffi/ffi/issues/864)


 `sudo gem install ffi -- --enable-libffi-alloc`
で回避しました

終わり。

戻し方
---

3年？も経つと状況もまた変わってくるわけで、CocoaPodをアップデートしようとしたら逆にビルドエラーするようになっていたので

```
sudo gem uninstall ffi
```

でffiを一度アンインストールしてやると無事に通りましたとさ。