OmniAICreator/Qiita-1.07M huggingface dataset:
【JavaScript】反復処理⑥　反復可能オブジェクト（イテレーター）


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/3a4708f1723bb0f0148f


#目的

* 反復処理についての理解を深める

#本題
###1.反復可能オブジェクト

イテレーターを使って反復可能オブジェクトに書き換える

####前提とゴール

Object.entiresを必要とせず、for...ofの中でobjを使えるようにしたい

````js
const obj = {
	prop1: 'value1',
	prop2: 'value2',
	prop3: 'value3'
}

// 通常のオブジェクトの場合はfor...ofの反復操作はできないので下記のように配列に置き換える
const items = Object.entries(obj);
for (let item of items) {
	console.log(item);
}
`````

####イテレーターの準備

````js
const obj = {
	prop1: 'value1',
	prop2: 'value2',
	prop3: 'value3'
}
// イテレーターを準備
Object.prototype[Symbol.iterator] = function(){
	return {
		// nextメソッドが必要
		next(){
			return{
				// とりあえず無限ループしないようにtrue
				done: true,
				// こちらも仮で0にする
				value: 0
			}
		}
	}
}
`````

####プロパティの値を取得する

thisを使って取得する　

`````js
// for...ofで使えるようにitemsに変数を変更
const items = {
	prop1: 'value1',
	prop2: 'value2',
	prop3: 'value3'
}

Object.prototype[Symbol.iterator] = function(){
	// objへの参照がthisに含まれている
	console.log(this);
	// このthisに値を詰めていって、下記のvalueで返却する
	return {
		next(){
			return{
				done: true,
				value: 0
			}
		}
	}
}
// const items = Object.entries(obj);
for (let item of items) {
	// objの値が出力される　prop1: "value1",,,,,,
	console.log(item);
}
``````

実際に取得してみる

````js

const items = {
	prop1: 'value1',
	prop2: 'value2',
	prop3: 'value3'
}

Object.prototype[Symbol.iterator] = function(){
	// このようにするとプロパティが配列になったものがkeysに格納される
	const keys = Object.keys(this);
	// keysの値を一つずつ取っていきたい
	// 変数iを定義し、iが+1されるごとにkeysからキー情報を取得してそれを元に値を取得していく
	const i = 0;
	return {
		// next()が呼び出されてた時に値を一つずつ取得していく
		next(){
			let i = keys[i];
			// 省略して上記にi++でも可
			i++;
			return{
				done: true,
				value: 0
			}
		}
	}
}
// const items = Object.entries(obj);
for (let item of items) {
	console.log(items);
}
```````

thisの呼び出し元が実行するコンテキストによって参照先に変わるのでその確認をする

````js
// objに変数を変更
const obj = {
	prop1: 'value1',
	prop2: 'value2',
	prop3: 'value3'
}

Object.prototype[Symbol.iterator] = function(){
	// 関数内で呼ばれたthisはwindowオブジェクトを呼び出す
	const keys = Object.keys(this);
	let i = 0;
	// ここでreturn内のthisもobjの値を取れるように設定
	let _this = this;
	return {
		next(){
			let key = keys[i];
			i++;
			// ここで取れるthisはnextという値が入ったthisが取れてくるので下記のように書かない
			// console.log(this);
			// 下記の_thisではスコープチェーンを辿ってnextのwindowオブジェクトを参照する
			// console,log(_this);
			return{
				// ループの終了条件追加
				// 配列の要素数のがiより大きくなった＝true＝終了
				done: i > keys.length,
				// ここでvalueを設定
				// entriesと同じように配列で渡したいので[]でkeyと_thisのkeyをもとに値を取得する
				value: [key, _this[key]]
			}
		}
	}
}
// const items = Object.entries(obj);
// 分割代入でk,vとして値を渡す
for (let [k,v] of obj) {
	console.log(k,v);
	// 出力結果はprop1,value1.....となる
}
`````



今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
* [Let'sプログラミング JavaScript入門](https://www.javadrive.jp/javascript/)
windowsでiverilog その117

#概要　

windowsでiverilogやってみた。
練習問題やってみた。

#練習問題

pwmを実装せよ。

#サンプルコード

```

module pwm(input wire clk, input wire rst, input wire [7:0] address, input wire write, input wire [15:0] writedata, input wire read, output reg [15:0] readdata, output wire out);
	reg [15:0] count;
	reg [15:0] duty;
	reg [15:0] period;
	assign out = (count <= duty) ? 1 : 0;
	always @(posedge clk)
	begin
		case(address)
		8'h00:
			readdata <= period;
		8'h01:
			readdata <= duty;
		8'h02:
			readdata <= count;
		default:
			readdata <= 0;
		endcase
	end
	always @(posedge clk)
	begin
		if (rst)
		begin
			duty <= 0;
			period <= 0;
		end
		else if (write)
		begin
			case(address)
			8'h00:
				period <= writedata;
			8'h01:
				duty <= writedata;
			endcase
		end
	end
	always @(posedge clk)
	begin
		if (rst)
			count <= 0;
		else if (count >= period)
			count <= 0;
		else
			count <= count + 1;
	end
endmodule

module test;
	reg clk = 0;
	reg rst = 1;
	reg [7:0] address = 0;
	reg [15:0] writedata = 0;
	reg read = 0;
	reg write = 0;
	wire [15:0] readdata;
	wire out;
	pwm u(.clk(clk), .rst(rst), .address(address), .write(write), .writedata(writedata), .read(read), .readdata(readdata), .out(out));
	initial
	begin
		$monitor("%d %h %h %h %h %h", $time, write, writedata, read, readdata, out);
	end
	always
		#1
			clk <= ~clk;
	initial
	begin
		#10
			rst = 0;
		#10
			write = 1;
			address = 0;
			writedata = 32'd100;
		#10
			write = 0;
		#10
			write = 1;
			address = 1;
			writedata = 32'h50;
		#10
			write = 0;
		#10
			read = 1;
			address = 1;
		#10
			read = 0;
		#1000
			$finish;
	end
endmodule





```

#結果

```
                   0 0 0000 0 xxxx x
                   1 0 0000 0 xxxx 1
                   3 0 0000 0 0000 1
                  20 1 0064 0 0000 1
                  23 1 0064 0 0064 0
                  30 0 0064 0 0064 0
                  40 1 0050 0 0064 0
                  41 1 0050 0 0000 1
                  43 1 0050 0 0050 1
                  50 0 0050 0 0050 1
                  60 0 0050 1 0050 1
                  70 0 0050 0 0050 1
                 183 0 0050 0 0050 0
                 223 0 0050 0 0050 1
                 385 0 0050 0 0050 0
                 425 0 0050 0 0050 1
                 587 0 0050 0 0050 0
                 627 0 0050 0 0050 1
                 789 0 0050 0 0050 0
                 829 0 0050 0 0050 1
                 991 0 0050 0 0050 0
                1031 0 0050 0 0050 1

```

以上。
windowsでiverilog その116

#概要　

windowsでiverilogやってみた。
練習問題やってみた。

#練習問題

10bitパラシリ変換を実装せよ。

#サンプルコード

```
module pase10(input scl, input [9:0] sda, output reg q);
	reg [9:0] v;
	initial
	begin
		q = 1'b0;
		$monitor("%b %b %b", scl, v, q);
	end
	always @(sda)
	begin
		v = sda;
	end
	always @(posedge scl)
	begin
		q = v[0];
		v = {1'b0, v[9:1]};
	end
endmodule


module test;
	reg scl;
	reg [9:0] sda;
	wire q;
	pase10 u(.scl(scl), .sda(sda), .q(q));
	initial
	begin
		scl = 0;
		sda = 10'b0101010101;
		//$display("scl, sda, q");
		//$monitor("%b %b %b", scl, sda, q);
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			$finish;
	end
endmodule





```

#結果

```
0 0101010101 0
1 0010101010 1
0 0010101010 1
1 0001010101 0
0 0001010101 0
1 0000101010 1
0 0000101010 1
1 0000010101 0
0 0000010101 0
1 0000001010 1
0 0000001010 1
1 0000000101 0
0 0000000101 0
1 0000000010 1
0 0000000010 1
1 0000000001 0
0 0000000001 0
1 0000000000 1
0 0000000000 1
1 0000000000 0
0 0000000000 0

```

以上。
windowsでiverilog その115

#概要　

windowsでiverilogやってみた。
練習問題やってみた。

#練習問題

10bitシリパラ変換を実装せよ。

#サンプルコード

```
module sepa10(input scl, input sda, output reg [9:0] q);
	initial
	begin
		q <= 10'h0;
	end
	always @(posedge scl)
	begin
		q <= {q[8:0], sda};
	end
endmodule


module test;
	reg scl;
	reg sda;
	wire [9:0] q;
	sepa10 u(.scl(scl), .sda(sda), .q(q));
	initial
	begin
		scl = 0;
		sda = 0;
		$display("scl, sda, q");
		$monitor("%b %b %b", scl, sda, q);
		#2
			sda = 1;
			scl = 1;
		#2
			sda = 0;
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			scl = 1;
		#2
			scl = 0;
		#2
			$finish;
	end
endmodule





```

#結果

```
scl, sda, q
0 0 0000000000
1 1 0000000001
0 0 0000000001
1 0 0000000010
0 0 0000000010
1 0 0000000100
0 0 0000000100
1 0 0000001000
0 0 0000001000
1 0 0000010000
0 0 0000010000
1 0 0000100000
0 0 0000100000
1 0 0001000000
0 0 0001000000
1 0 0010000000
0 0 0010000000
1 0 0100000000
0 0 0100000000
1 0 1000000000
0 0 1000000000

```

以上。
Macのキーリピート間隔の限界突破

以下のコマンド入力後に再起動すると、キーリピート間隔が早くなります。
`InitialKeyRepeat`がリピート開始するまでの待ち時間で、`KeyRepeat`が繰り返し間隔のようです。

```
defaults write -g InitialKeyRepeat -int 10
defaults write -g KeyRepeat -int 1
```
モブプログラミングしたい

#モブプログラミングに興味を持つ
 自分のプログラミングスキルに自信がないので、いろんな人にコメントもらいながらスキルを上げていけないかと調べ始めた。

###モブプログラミングとは
モブプログラミングは、2012年頃にHunter Industies社で取り組みが始まりました。そこで得た知見をWoody Zuillさん達がカンファレンス等で紹介することで広まっていきました。

###Mob Programming – A Whole Team Approach by Woody Zuill
https://www.agilealliance.org/resources/experience-reports/mob-programming-whole-team-approach-woody-zuill/

###やるまでの道のり
・あれ自分のスキルがへっぽこすぎて人に見せられないのでは
　→度胸だ。とりあえずやってみろ
・20代の若い人のほうがPG力高そう。やっぱりやめたほうが・・
　→度胸だ。とりあえずやってみろ
・誰に声かければいいか分からない
　→いまどきconpassとかで勉強会の記事がいっぱいあるから参加してみろ
・モブプログラミングがうまくいったら職場で実践できるかな・・・
　→効率を求めるな。複数人でプログラミングをしてるんだから工数かかるもの。ただ、バグ発生による手戻りは抑えられるし質のいいものが作れるから将来を考えてやるべし。

#モブプログラミング関連リンク
https://agile-monster.com/blog/mobpro_manual/

https://takaking22.com/tag/mobprogramming/
PHPの関数を使ってみよう！

#PHP関数
<br>
書いたコードを見やすくする為に関数でまとめていきます！

以下のコードがあったとします。
　<br>

```php:index.php
<?php
echo '・・・・・・・・' . PHP_EOL;
echo '・・・・♡・・・・' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;
echo 'おにぎり食べたい！' . PHP_EOL;
echo 'ケーキ食べたい！' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;
echo '・・・・♡・・・・' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;
echo 'ハンバーガー食べたい！' . PHP_EOL;
echo 'チョコレート食べたい！' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;
echo '・・・・♡・・・・' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;
```

<br>
ターミナルに以下を入力すると...
<br>

```
~$ php index.php
```

<br>
このように表示されました！
<br>

```
~ $ php main.php
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
おにぎり食べたい！
ケーキ食べたい！
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
ハンバーガー食べたい！
チョコレート食べたい！
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
~ $ 
```

<br>
同じコードが重複していて見にくいので、

echo '・・・・・・・・' . PHP_EOL;
echo '・・・・♡・・・・' . PHP_EOL;
echo '・・・・・・・・' . PHP_EOL;

↑こちらを関数にまとめていきます！

function 関数名 ( ) {
 　　　　　処理内容
}
<br>


```php:index.php

<?php
function show () {         //show関数に重複する処理をまとめます
  echo '・・・・・・・・' . PHP_EOL;
  echo '・・・・♡・・・・' . PHP_EOL;
  echo '・・・・・・・・' . PHP_EOL;
}


show ();            //show関数を呼び出します
echo 'おにぎり食べたい！' . PHP_EOL;
echo 'ケーキ食べたい！' . PHP_EOL;
show ();           //show関数を呼び出します
echo 'ハンバーガー食べたい！' . PHP_EOL;
echo 'チョコレート食べたい！' . PHP_EOL;
show ();           //show関数を呼び出します

```
<br>
もう一度ターミナルで以下を入力すると...
<br>

```
~$ php index.php
```

<br>
最初の結果と同じになりました！
<br>


```
~ $ php main.php
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
おにぎり食べたい！
ケーキ食べたい！
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
ハンバーガー食べたい！
チョコレート食べたい！
・・・・・・・・
・・・・♡・・・・
・・・・・・・・
~ $ 
```

<br>
このように関数にまとめるとコードが見やすくなるのと、

同じような処理を繰り返す時に便利です！
<br>
テキストフィールドに予め値を入れておく方法

## 開発環境

言語：Ruby (2.7.2)
フレームワーク：Ruby on Rails (6.1.3.2)
フロントエンド：HTML&CSS/Bootstrap/JavaScript/jQuey
DB：PostgreSQL
テスト：Rspec
インフラ：EC2　RDS　
ソースコード管理：GitHub(issueで、タスク管理する)

## データベースに予め値を入れておく方法
テキストフィールドに初期値を入れておく方法はマイグレーションファイルに`default: "初期値"`を記載することでで可能なことがわかった。

```ruby
class ChangeColumnDefaultSolutionOfIncidents < ActiveRecord::Migration[6.1]
  def change
    change_column :incidents, :solution, :text, null: false, default: "解決方法"
  end
end
```

### データベースに初期値を入れる方法

1. マイグレーションファイルを作成
    
    ```ruby
    $ rails g migration ChangeColumnDefaultSolutionOfIncidents
    ```
    
2. カラムに初期情報を入れる構文を記載
    
    ```ruby
    class ChangeColumnDefaultSolutionOfIncident < ActiveRecord::Migration[6.1]
    SOLUTION = <<"EOS"
    【やりたいこと】
    【原因】
    【対応方法】
    【参考資料】
    EOS
      def up
        change_column :incidents, :solution, :text, null: false, default: SOLUTION
      end
    end
    ```
    
3. マイグレーションを実行
    
    ```ruby
    $ rails db:migrate
    ```

## 最後に
テスト的にマイグレーションファイルを作り、簡単な値を入れて理解を深めました。
そのときに不要になったマイグレーションファイルを削除しようとしたときロールバックができませんでした。
以下記事でその時の解決方法を記載しているので参考ください
[データベースをロールバックできなくなった件](https://qiita.com/iskwky/items/054a4ead9e47b071369a)
Nuxt Fatal errorの解決方法

###nuxt2.15.8でビルド時にこのようなエラーがでました。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/d5edfaf6-ae74-100f-00b6-0666efaca03b.png)

#解決方法
エラーが起こっているのは「node_modules\globby\gitignore.js」のようです。

Windowsのパスに問題がありました。

```nuxt
const ensureAbsolutePathForCwd = (cwd, p) => {
//追加	p = path.normalize(p);
	cwd = slash(cwd);
	if (path.isAbsolute(p)) {
		if (p.startsWith(cwd)) {
			return p;
		}

		throw new Error(`Path ${p} is not in cwd ${cwd}`);
	}

	return path.join(cwd, p);
};
```
nodeのpathモジュールを使用してノーマライズしてます。
AppleシリコンMac上のDockerでSchemaspyを動かす方法

## Appleシリコン上のDockerでもSchemaspyを動かしたい！

Schemaspyは、とても便利なER図とテーブル定義自動作成Toolです。
私はデータベース設計を考える時、視覚的に考えたいのでSchemaspyを愛用してます。

しかし、AppleシリコンMac上のDockerだと今の所パッと調べて出回っている手順では動作させることが出来ません。

私は今まではIntelMacと併用していたのですが、最近環境をAppleシリコンMacに全て切り替えました。
AppleシリコンMacでSchemaspyを動作させる方法を調べて動かすことが出来たので手順を共有します。

## 何故動かないか？

docker hubで公開されているイメージの参照しているopenjdkイメージがlinux/arm64に対応していないのが原因のようでした。ですので、linux/arm64に対応したバージョンのopenjdkイメージに差し替えて動作させちゃいましょう。

https://github.com/schemaspy/schemaspy/blob/master/Dockerfile#L1

## コード

自分の環境のコードからSchemaspyに関する部分だけ抜粋したので、実際は参照先DBコンテナを立ち上げるなり必要になるので、その辺は各自の環境に合わせて変更してみてください。

ディレクトリ構成はこんな感じです。

```ディレクトリ構成:ディレクトリ構成
docker-compose.yml
docker
 + schemaspy
   + Dockerfile
   + config
     + schemaspy.properties
```

```docker-compose.yml
version: "3.8"

services:
  schemaspy:
    build: ./docker/schemaspy
    image: schemaspy/schemaspy
    volumes:
      - ./docker/schemaspy/output:/output
      - ./docker/schemaspy/config/schemaspy.properties:/app/schemaspy.properties:ro
    container_name: "schemaspy"
    command: "java -jar schemaspy.jar"

  nginx_schemaspy:
    image: nginx:1.19.5-alpine
    container_name: "nginx_schemaspy"
    ports:
      - "8080:80"
    volumes:
      - ./docker/schemaspy/output:/usr/share/nginx/html:ro
```

```Dockerfile:Dockerfile
FROM openjdk:8u212-jdk-alpine

ENV DRIVER_URL https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.6/mysql-connector-java-5.1.6.jar
ENV APP_URL https://github.com/schemaspy/schemaspy/releases/download/v6.1.0/schemaspy-6.1.0.jar

WORKDIR /app

RUN apk --update add graphviz ttf-dejavu && \
    apk --update add --virtual .builddep tzdata wget libressl && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    wget -O mysql-connector-java.jar ${DRIVER_URL} && \
    wget -O schemaspy.jar ${APP_URL} && \
    apk del .builddep && \
    rm -rf /var/cache/apk/*
```

schemaspy.propertiesの`<DB名>`などの値は環境により書き換えてください。

```schemaspy.properties
# type of database. Run with -dbhelp for details
schemaspy.t=mysql
# optional path to alternative jdbc drivers.
schemaspy.dp=/app/mysql-connector-java.jar
# database properties: host, port number, name user, password
schemaspy.host=<DBホスト名>
schemaspy.port=3306
schemaspy.db=<DB名>
schemaspy.u=<ユーザー名>
schemaspy.p=<パスワード>
# output dir to save generated files
schemaspy.o=/output
# db scheme for which generate diagrams
schemaspy.s=<スキーマ名>
```

## 参考

https://takahashik.hatenablog.com/entry/2018/10/09/075957
djangoで論理削除を使いたい

今回のお題
-
今回のお題は`djangoアプリでの論理削除の使い方`です。

ユーザーの論理削除（退会処理）は`AbstractUser`クラスに最初から実装されていますが他のモデルについては自分で用意する必要があるので、メモとしてまとめておきます。

論理削除とは
-
レコードを削除した後も情報を参照できるようにすること。

例えば会員制のサービスでは退会後も過去の利用履歴の確認などができるようにしておく必要があるため、物理削除（＝通常の削除）ではなく論理削除が使われる。

内部的には`deleted_at`というフィールドを用意し、この属性値の有無を用いて削除されているかいないかを判定する。

django_boost
-
論理削除を手軽に実装できるパッケージ。

具体的な使い方は後述。

導入方法は、pipでのインストールと`INSTALLED_APPS`への追加のみ。

```shell:ターミナル
% pip3 install django_boost
```

```settings.py
INSTALLED_APPS = [
  # 中略
  "django_boost",# 追加
]
```

LogicalDeletionMixin
-
`django_boost`を導入することで実装可能になるクラス。

このクラスを継承させたモデルでは論理削除を扱うことができる。

Mixinと名前がついているが`models.Model`を継承しているため、これ単体でモデルの親とすることが可能(汎用ビューのように多重継承する必要はない）。

```python:LogicalDeletionMixinのソースコード
class LogicalDeletionMixin(models.Model):
    """Provide logical delete."""

    deleted_at = models.DateTimeField(
        verbose_name=_("deleted date"), blank=True, null=True, default=None, editable=False)

    class Meta:
        abstract = True

    objects = LogicalDeletionManager()

    @classmethod
    def get_deleted_value(cls):
        return now()

    def delete(self, using=None, keep_parents=False, hard=False):
        if hard:
            return super().delete(using=using, keep_parents=keep_parents)
        self.deleted_at = self.get_deleted_value()
        return self.save()

    def revive(self, force_update=False, using=None):
        """Revive logical deleted item."""
        self.deleted_at = None
        return self.save()

    def is_dead(self):
        """Return True if the item is dead."""
        return self.deleted_at is not None

    def is_alive(self):
        """Return True if record is alive, otherwise False."""
        return self.deleted_at is None
```


論理削除の使い方
-

```python
# 対象のインスタンスを論理削除
item.delete()
# 既存のdelete()メソッドが論理削除に置き換わります(DeleteViewを通した場合などを含めて）。

# 物理削除
item.delete(hard=True)

# 論理削除された日時の取得
item.deleted_at # 論理削除実行時に自動で設定される

# 論理削除されているかどうかの判定
item.is_alive() # 削除済みならfalse
item.is_dead() # 削除済みならtrue
# テンプレートの{% %}で使う際には最後の()はつけない

# 論理削除の状態に合わせたレコードの前取得
Item.objects.alive() # 削除されていないもののみ
Item.objects.dead() # 削除済みのみ
Item.objects.all() # 削除の有無に関わらず全て
# こちらもテンプレートでは()をつけない
```
[Nuxt.js, Vue] v-onでv-bindと同じように条件分岐する。

v-bindで三項演算子を使い条件分岐する方法はたくさん紹介されていたのですが、
v-onで三項演算子を使った例が見つけられず、半信半疑で書いてみたら普通に使えたので、その備忘録。

###まずはv-bindで三項演算子

```vue
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
```
これは、常にerrorClassは適応されており、さらにisActiveがtrueの時だけactiveClassが効く。
詳しくはこちら：[クラスとスタイルのバインディング](https://jp.vuejs.org/v2/guide/class-and-style.html)

###v-onで三項演算子

```vue
<div v-on:click="isActive ? unclick() : click()"></div>
```
これは、isActiveがtrueの時はunclick()関数が動き、isActiveがfalseの時はclick()関数が動く。

###活用例
.idと.nameを持ったitemList配列がv-forで並べられており、
itemがselectedListに含まれていたら、
unclick(item.id)、cssはitem-selectedが適用される。

含まれていなかったら、
click(item.id)、cssはitemが適用される。

unclick()、click()はそれぞれselectedListからitem.idを削除、追加する処理が書かれている。


```vue
<li
v-for="item in itemList"
:key="item.id"
v-on:click="selectedList.includes(item.id)　? unclick(item.id)　: click(item.id)"
v-bind:class="selectedList.includes(item.id) ? 'item-selected' : 'item'"
>
{{ item.name }}
</li>
```
github.comの認証設定手順

> 2021年8月からGit操作の認証要件に変更がかかり、
> github.comで認証操作を行う際に今まで使用してきた
> アカウントパスワードが使用できなくなったので、簡単に手順を記載することに
> <details><summary>詳細</summary>
> https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/
> </details>


## 手順
### １. アクセストーク生成
> <details><summary>公式ドキュメント参照</summary>
> https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
> </details>

### 2. 生成したアクセストークを使用してgit push ※git cloneの場合も同様
```
> git push origin main
Username: your_username
Password: your_token（トークンを入力する）
```

### 3. github.comへのパスワードを記憶
```
# 2. を終わった後に実行する
> git config credential.helper store
```

<details><summary>参考記事</summary>
https://style.potepan.com/articles/31072.html
https://qiita.com/Kamo123/items/c92b03278b6302c641e3
</details>
CUIでのラズパイのネットワーク設定

## Wifi

### Wifiの基本設定

Wifiの設定ファイルは、``/etc/wpa_supplicant/wpa_supplicant.conf``となります。

```shell-session:
pi@raspberrypi:~ $ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf
```
今、家のアクセスポイントを設定しているので１つのネットワークの設定が書かれています。

```shell-session:/etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=JP

network={
        ssid="WifiのSSID"
        psk="平文のパスフレーズ"
        key_mgmt=WPA-PSK
}
```

なにか書き換えたら再起動してIPアドレスの確認
※今回はIPv4のみ確認(-4オプションを外すとIPv6も一緒に表示されます)

```shell-session:
pi@raspberrypi:~ $ sudo reboot


pi@raspberrypi:~ $ ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.10.20/24 brd 192.168.10.255 scope global dynamic noprefixroute wlan0
       valid_lft 85222sec preferred_lft 74422sec
```

### パスフレーズの暗号化

パスフレーズを暗号化してWi-Fiの設定を行う場合は、

```shell-session:
pi@raspberrypi:~ $ wpa_passphrase "Wi-FiのSSID" "平文のパスフレーズ"
```

と実行すると、パスフレーズが暗号化されたものが出力されるので、``/etc/wpa_supplicant/wpa_supplicant.conf``の平文と置き換えることで暗号化されたパスフレーズを設定できます。

```shell-session:
network={
        ssid="Wi-FiのSSID"
        #psk="平文のパスフレーズ"
        psk=暗号化されたパスフレーズ
}
```

### その他

``/etc/wpa_supplicant/wpa_supplicant.conf``に複数の設定を記載すると接続可能なアクセスポイントに繋がります。
パラメータに``priority``を設定すると優先順位が付けられます。


## 固定IPアドレスを割り振る

DHCPでIPアドレスを取得しているとアドレスが変わる可能性があるので、サーバなどに使う場合は固定IPアドレスを割り振ります。
``/etc/dhcpcd.conf``にwlan0の設定を追加します。


```shell-session:
pi@raspberrypi:~ $ sudo vi /etc/dhcpcd.conf
```

今回は192.168.10.100というIPアドレスを割り振りました。

```shell-session:/etc/dhcpcd.conf（追加内容）
interface wlan0
static ip_address=192.168.10.100/24
static routers=192.168.10.1
static domain_name_servers=192.168.10.1
```

設定を追加したら再起動してIPアドレスを確認します。

```shell-session:
pi@raspberrypi:~ $ sudo reboot
```
設定した内容に問題がなければ、IPアドレスが変更されて起動してきます。

```shell-session:
pi@raspberrypi:~ $ ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.10.100/24 brd 192.168.10.255 scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
```

DHCPに戻したいときは、設定を削除して再起動するとDHCPサーバから取得したIPアドレスになります。
facebookのshareボタンの内容（デフォルト）が変更されない時 

https://www.facebook.com/sharer.php?u=https://hogehoge.fuga
でサイトをfacebookでシェアする時に、シェア内容が更新されないときは、facebook developer toolを開いてスクレイピングをし直すと、シェア内容が更新されます。
球面上での一様乱数生成

こんにちは。CG業界ではRay-Tracingを含む様々な手法でサンプリングが用いられています。以下の図のような青線の半球において効率よくサンプリングが行える手法としてHemispherical Importance Sampling Monte Carlo Methodがあります。2回にわたって理論的な部分を説明します。本記事では球面上での一様乱数生成についてです。

<img width="500" alt="IMG_0078.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277765/3631977a-b8d4-4e63-349b-2c0e12059c16.jpeg">

#目次
[1.事前知識](#1-事前知識)
[2.2変数確率密度関数を分離する](#2-2変数確率密度関数を分離する)
[3.球面上での一様乱数の生成](#3-球面上での一様乱数の生成)
[4.おわりに](#4-おわりに)

#1. 事前知識
➀立体角と緯度経度との変数変換(https://batapara.com/archives/solid-angle.html/)

```math
d\omega = \sin\theta d\phi d\theta \tag{1}
```
➁球面上での一様なサンプリング(https://qiita.com/aa_debdeb/items/e416ae8a018692fc07eb)
以下のように定義された($\theta,\phi$)をそれぞれ一様乱数として取得すると、極のあたりでサンプルが集中してしまう。逆関数法を用いて球面上で一様な乱数を生成できるような手法を紹介している。
<img width="500" alt="IMG_0078.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277765/3f0131bd-f59f-f345-4522-2c78e21ed52e.jpeg">

➂立体角を用いた確率密度$~p(\omega)~$と緯度経度を用いた確率密度$~p(\theta, \phi)~$の関係性
半球上での積分に関して以下の式が成り立つ。

```math
\int_{\mathcal{H}_2} p(\omega) d\omega = \int_{\mathcal{H}_2} p(\theta,\phi)d\theta d\phi = 1
```
よって

```math
p(\omega) d\omega = p(\theta,\phi)d\theta d\phi \tag{2}
```

(1)と比べると、

```math
\sin\theta ~p(\omega) = p(\theta,\phi) \tag{3}
```
➃Monte Carlo法
以下の式において$f(X_i)$が環境光の色にあたり、$pdf(X_i)$が$p(\omega)$にあたる。

```math
\left< F^N\right> = \frac{1}{N} \sum^{N-1}_{i=0} \frac{f(X_i)}{pdf(X_i)} \tag{4}
```
#2. 2変数確率密度関数を分離する
球面上の一様乱数生成法は前節の引用先で紹介されているが、証明に不十分な点があるので、再度ここで考え直す。任意の2変数確率密度関数$p(x,y)$を$p(x,y) = p_x(x)~ p_y(y)$のように分離できることが望ましいが、このような場合は難しいので、以下のように一方の次元を積分することで分離できる。

```math
p(x) = \int p(x,y) dy　\tag{5}
```
xが生じるという条件の下でyが生じる条件は

```math
p(y|x) = \frac{p(x,y)}{p(x)} \tag{6}
```

となる。

#3. 球面上での一様乱数の生成
球面上で一様な乱数を生成するとは立体角を用いた確率密度関数$p(\omega)$が球面上で一定となるようなサンプリングのことである。つまり、$p(\omega) = 1/4\pi$である。よって(3)より、$p(\theta, \phi) = \sin\theta/4\pi$である。このことと(5)を用いると、

```math
p(\theta) = \int^{2\pi}_0 p(\theta, \phi) d\phi = \int^{2\pi}_0 \frac{\sin\theta}{4\pi} d\phi = \frac{\sin\theta}{2} \tag{7}
```
(6)を用いると、

```math
p(\phi) = p(\phi|\theta) = \frac{p(\theta,\phi)}{p(\theta)} = \frac{1}{2\pi}\tag{8}
```
ここで求められた結果は引用先での結果と合致している。

#4. おわりに
次回はImportance Samplingについて書き、この話を深めていく。
Nuxt.jsでsmoothscroll-polyfillを使う方法（Safariでsmoothスクロールを使えるようにしたい）

#はじめに
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/802f7727-9115-9c9a-1447-4b964c7ca088.gif)
こんな感じで（わかりにくいけど・・・）、safariでsmoothスクロールを使えるようにします！！

#何が問題なのか？

https://tadtadya.com/css-js-easiest-smooth-scrolling/

cssとかでも「このブラウザは対応してないから表示が崩れた！」とか、今だにあるようじゃないですか:frowning2:

同じような問題が、javascriptでもあるようで、、、

```javascript
 methods: {
    down() {
      window.scrollBy({
        left: 0,
        top: 235,
        behavior: "smooth", // ここだけsafariは効かない。。。
      });
      console.log("下がった！！");
    },
  },
```

👆こんな感じで`window.scrollBy`でスムーズスクロールをさせたいのだが`behavior: "smooth"`がなんと**safariでは効かないのである！！！**
~~めんどくせーりんご帝国。。。~~日本では貧乏人でもiPhone持ってる国なんで、これは由々しき問題である！

#smoothscroll-polyfillを導入するだけで、safariでスムーズスクロールが可能になる

https://www.npmjs.com/package/smoothscroll-polyfill

これを導入します。

##packageを入れる

```console
# npm 
npm install smoothscroll-polyfill --save
 
# yarn 
yarn add smoothscroll-polyfill
```

##pluginsにファイルを追加

そして`plugins/smooth-scroll.ts`を作成
（まだpluginsフォルダがない場合は作成！）

```typescript:plugins/smooth-scroll.ts
import Vue from 'vue'
import SmoothScroll from 'smoothscroll-polyfill'

SmoothScroll.polyfill();

Vue.use(SmoothScroll as any);
```

※私の場合、

```typescript
Vue.use(SmoothScroll as any);
```

の部分で`as any`がないと、よくわからないエラーが発生しました。。。
エラーがない人は`as any`は特になくて良いかも！:upside_down:


```console
No overload matches this call.
  Overload 1 of 2, '(plugin: PluginObject<unknown> | PluginFunction<unknown>, options?: unknown): VueConstructor<Vue>', gave the following error.
    Argument of type 'typeof import("/Users/***/node_modules/@types/smoothscroll-polyfill/index")' is not assignable to parameter of type 'PluginObject<unknown> | PluginFunction<unknown>'.
      Property 'install' is missing in type 'typeof import("/Users/***/node_modules/@types/smoothscroll-polyfill/index")' but required in type 'PluginObject<unknown>'.
  Overload 2 of 2, '(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): VueConstructor<Vue>', gave the following error.
    Argument of type 'typeof import("/Users/***/node_modules/@types/smoothscroll-polyfill/index")' is not assignable to parameter of type 'PluginObject<any> | PluginFunction<any>'.
      Property 'install' is missing in type 'typeof import("/Users/***/node_modules/@types/smoothscroll-polyfill/index")' but required in type 'PluginObject<any>'.ts(2769)
plugin.d.ts(6, 3): 'install' is declared here.
plugin.d.ts(6, 3): 'install' is declared here.
```

##nuxt.config.jsで読み込み

```javascript:nuxt.config.js
 // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
    { src: '~/plugins/smooth-scroll.ts', mode: 'client' },
  ],
```

これだけでsafariでsmoothスクロールが効くようになりました！！！:hugging:
わーい


#参考
polyfill.io？とやらを使ったやり方もあったりするようだが、よくわからなかったのでこの方法に落ち着きました:rolling_eyes:

https://cly7796.net/blog/javascript/implement-smooth-scrolling-of-in-page-links-with-non-jquery/

https://qiita.com/miyawash/items/8d237ebbaa5d75d7dd7b#smoothscroll-polyfill
Cloud9とGithubをSSHで接続してpushするまで

###メモ程度なので雑なまとめ方ですが、Cloud9とGithubをSSHで接続してpushするまでをまとめました。

###Cloud9にGithubの認証情報を登録
```$ git config --global user.name ユーザー名
$ git config --global user.email メールアドレス```
###ローカルリポジトリの作成
```$ cd プロジェクトディレクトリへ移動
$ git init```
Initialized empty Git repository in /home/ec2-user/environment/プロジェクトディレクトリ/.git/   直下に.gitディレクトリを作成```
###コミット対象を登録
```$git status コマンドでリポジトリの状態を確認
$ git add ＜ファイル名＞
$ git add ＜ディレクトリ名＞
$ git add .　更新があったファイルやディレクトリを一括してステージングエリアへ追加
$ git commit -m "コミットメッセージ"```
###コミットログ確認
```$ git log```
###【GitHub】リポジトリを作成
GitHubでソースコードの保管場所(リポジトリ)を用意するために＋ > New repositoryを選択。
Create a new repositoryページでRepository name(リポジトリ名)を入力してCreate repository(リポジトリを作成)ボタンをクリックするとGitHub上にソースコードの保管場所(リポジトリ)が作成される。
リポジトリ作成直後はページ上にソースコードの保存に利用するURLが表示される。(SSHを選択)
このURLを利用してCloud9からGitHubにソースコードをコピーしリモートリポジトリ登録を行う。
###【Cloud9】リモートリポジトリを登録
今回はCloud9上のローカルリポジトリをリモートリポジトリ名をoriginという名前で登録する
```$ git remote add origin コピーしたURL　
$ git remote -v　登録したリモートリポジトリの確認
$ git remote rm origin　登録リモートリポジトリ削除```
###公開鍵の作成
```$ ssh-keygen -t rsa -C リモートレポジトリを登録した自分のメールアドレス(・・・@gmail.comなど)
$ cat ~/.ssh/id_rsa.pub ssh-rsaから始まる部分をコピー```
github settings>SSH and GPG keys>New SSHkey 貼り付け
###SSH実行
```$ ssh -T git@github.com```
###【Cloud9】ソースコードをプッシュ
```$ git push origin master```
keyから探すeclipseとVisual Studioのショートカットキー (Windows)

# バージョン情報

### eclipse
- Eclipse IDE for Enterprise Java and Web Developers (includes Incubating components)<br>Version: 2021-09 (4.21.0)

### Visual Studio
- Microsoft Visual Studio Community 2022 (64 ビット) - Current
Version 17.0.0

# はじめに
- バージョンによって多少の差異があるかもしれないので一応載せてはいますが、ほぼ同様に使えるとは思います。
- ショートカットが大好きなので曲がり角とかも人等いない限り回り込むような事はせず、ギリギリを攻めるタイプです。が人生にはショートカットなんてありませんよ。
- eclipseでよく使うショートカットキーがVisual Studioでは挙動が違うことが割とあって混乱したので備忘録として残します。
- ちょくちょく更新していく予定です。

# 検索キーワード
(やりたいことから探したい場合は以下をコピーしてページ内検索してください。 ※適当に抜粋してます。)

- 下に 1 行挿入
- 上に 1 行挿入
- 行の削除
- 行の移動
- エディタ内のタブ移動ダイアログ表示
- エディタ内タブ移動

# メイン
## eclipseでよく使うショートカットキー
まじか？！ (動きが想定と逆とか) ってなったショートカットには独断でkeyに■つけてます。

| Key | eclipse | Visual Studio |
| --- | --- | --- |
| Shift + Enter| 下に 1 行挿入 | 下に 1 行挿入 |
| Ctrl + Enter | － | 上に 1 行挿入 |
| ■ Ctrl + Shift + Enter | 上に 1 行挿入 | Shift + Enterが勝って下に 1 行挿入 |
| ■ Ctrl + d | 行の削除 | 複製 |
| Alt + ↑ or ↓ | 行の移動 | 行の移動 |
| Alt + Shift + ↑ or ↓ | 複製 | エディタ内のタブ移動ダイアログ表示 ※ ↓ の場合のみ |
| Ctrl + Alt + PageUp or PageDown | エディタ内タブ移動 ※ Alt なしでもOK | エディタ内タブ移動 |
TwitterAPIを使ってTweetをリアルタイムで取得したい

はじめに
---
* Qiita初めてなので慣れてないです
* SE歴4年ほど（コードを書くのは本業ではない）
* TwitterAPIを使って自由自在にツイートを取得したい
* 飽き性です




事前準備
---
* TwitterのDevelopperアカウントの作成
* pythonの実行環境整備（私はjupyterを使用しています）




ストリームって何？
---
よくわかりません。
なんかリアルタイムで情報をやりとりするっていう感覚です。





とりあえずドキュメントを漁ってみる
--
TwitterAPIのv2ってやつが最新っぽいのでそれを使います
https://developer.twitter.com/en/docs/twitter-api

Filterd streamっていうやつがなんか名前的にそれっぽい
https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/introduction

quick startを読んでみる
https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/quick-start

###quick startをまとめると

**1.ルールを作りましょう**
　→ストリーム配信する定義の設定をする
　　「ルール」＝「取得するツイートのクエリ」といったイメージでしょうか
　→後続を読む感じ複数設定できそう

**2.ルールにタグをつけましょう**
　→ルールにタグを設定すると、どのルールでマッチしたかわかるように、タグをpayloadに記載してくれるらしい

**3.ストリームにルールを追加しましょう**
　→addというのを使ってルール（value）とタグ(tag)をセットしているっぽい

**4.Bearer Tokenを使って認証を通そう**

**5.ストリームにルールを追加しましょう**
　→なぜか2回目の登場だが、3.はあくまで説明で、実行順序としては「認証を通す」→「ルールの追加」って感じがする

**6.取得したいフィールドを設定しよう**
　→デフォルトだけで不満だったらいろいろカスタマイズできるみたい

###つまりよくわからん


とりあえずやってみる
--
サンプルコードがあるのでやってみる
https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Filtered-Stream/filtered_stream.py

まるまるコピペして、
bearer_tokenのところだけ書き換えます

```
# To set your enviornment variables in your terminal run the following line:
# export 'BEARER_TOKEN'='<your_bearer_token>'
bearer_token = "XXXXXXXXXXXXXXX"
```

上から順番に実行していくと・・・
こんな感じのレスポンスが帰ってきました

まずツイートじゃないところ

```
{"meta": {"sent": "2021-11-12T16:41:47.610Z", "result_count": 0}}
{"data": [
  {"value": "cat has:images -grumpy", "tag": "cat pictures", "id": "1459199771915452417"}, 
  {"value": "dog has:images", "tag": "dog pictures", "id": "1459199771915452416"}
], 
"meta": {"sent": "2021-11-12T16:41:51.249Z", "summary": {"created": 2, "not_created": 0, "valid": 2, "invalid": 0}}}
200
```
最初の行は`get_rules()`のログでしょうかね
ルールを取得しに行きますが、初期状態だと設定されているルールがないので、送信時間と、`result_count:0`が帰ってきてますね

ルールが設定されていないのでdelete_all_rules(rules)は`return None`が実行されるため、ログ出力なし

そして`set_rules(delete)`のログっぽいのが帰ってきていて、
各種ルールとタグが設定され、IDがふられた感じになっているのでしょうか

そしてお待ちかねツイート

```
{
    "data": {
        "id": "1459199755876134915",
        "text": "RT @AdoptMePimaAZ: Adoptable #Dog #Casper_PIMAAZ_04 Tongue tied \ud83e\udd23 https://t.co/TyXx8oO5zW https://t.co/xlQIgPbhIN"
    },
    "matching_rules": [
        {
            "id": "1459199771915452416",
            "tag": "dog pictures"
        }
    ]
}
```

`data`の中身はツイートIDとそのテキストですかね
この感じだとRTか引用RTを取得している気がします。

そしてそのツイートを取得するのにどのルールがマッチしたかが書いてあるようです。

とりあえずストリームの取得？はできました。


今後
--
ストリームできましたが、まだまだやりたいことはたくさんあります。

* **RTを省きたい**
→ツイートを取得する上でRTが混ざっていると、何個も同じツイートを拾ってしまうのでそこを避けたい

* **このストリームをクラウドサービスと連携させてデータを蓄積したい**
→これを機に少しでもクラウドに触れてみたいなあという気持ちがあります。
→AWSのkinesisかSQSってやつが使えそうで、大容量でもないし、金もないのでSQSってやつが有力候補

* **データ分析とかできたら楽しそう**
→分析したい内容がないのに、とりあえず取得したデータで分析するって言い出すのは馬鹿がやることだと思っていますが、データを分析基盤まで持っていくというクラウドのアーキテクチャは気になります。
→分析だとGoogleのBigQueryが気になります。
　AWS→GCPのデータの移行の話とかも触れたら楽しそうだなあとか思ってます。
Dante 1.4.3をUbuntu 20.04にインストールする

# 目的
- SOCKS5プロキシサーバをUbuntu 20.04上で*IPv4/IPv6デュアルスタック構成*で構築したい
- 一般的なプロキシサーバとしての用途でなく、接続元の隠蔽としての利用をしたい

今回は、SOCKS5プロキシを実現するため、Danteを利用する。
Ubuntuで最も簡単にDanteをインストールするには`apt install dante-server`を実行すればよいが、aptで入るDanteのバージョンは`V1.2`と古い。
今回IPv6環境においてもSOCKS5プロキシを利用したいため、対応しているバージョンである`v1.4.3`をビルドして導入する。

# Danteビルド
[公式ページ](https://www.inet.no/dante/doc/1.3.x/config/install.html)を参照のこと。
下記、簡単な手順を記す。

- `apt install gcc make`で必要なパッケージを導入する。
- 下記の通り、makeする。

```shell
# Download the source from inet.no
wget https://www.inet.no/dante/files/dante-1.4.3.tar.gz
# Extract tarball
tar xzvf dante-1.4.3.tar.gz
# Make
cd dante-1.4.3
./configure
make
make install
```

- ビルドに成功すれば`/usr/local/sbin/sockd`ができているので、`sockd -v`でバージョンを確認する。
- `apt`経由だとファイル名が`danted`なので、お好みでリネームする。本稿では以下`danted`とリネームした前提で記載する。

# Service設定
## generated serviceを消す

私の環境では、`/etc/init.d/danted`から自動的に生成されたsystemdサービス設定が存在したので、これを削除する。

```shell
rm /etc/init.d/dante
systemctl daemon-reload
systemctl list-unit-files --type=service | grep dante # サービスが消えたことを確認
```

## サービス設定ファイル作成

サーバ起動時にIPアドレスがうまくバインドできず起動に失敗する事象が発生したので、暫定対応として`ExecStartPre`で`sleep 10`を入れている。

```shell
touch /etc/systemd/system/dante.service
vim /etc/systemd/system/dante.service
```

```dante.service
[Unit]
Description=SOCKS (v4 and v5) proxy daemon (danted)
Documentation=man:danted(8) man:danted.conf(5)
After=network-online.target

[Service]
Type=simple
PIDFile=/run/danted.pid
ExecStart=/usr/local/sbin/danted -f /etc/danted.conf
ExecStartPre=/bin/sh -c ' \
    sleep 10; \
    uid=`sed -n -e "s/[[:space:]]//g" -e "s/#.*//" -e "/^user\\.privileged/{s/[^:]*://p;q;}" /etc/danted.conf`; \
    if [ -n "$uid" ]; then \
        touch /var/run/danted.pid; \
        chown $uid /var/run/danted.pid; \
    fi \
    '
PrivateTmp=yes
InaccessibleDirectories=/boot /home /media /mnt /opt /root
ReadOnlyDirectories=/bin /etc /lib -/lib64 /sbin /usr
DeviceAllow=/dev/null rw

[Install]
WantedBy=network-online.target
```

# Dante設定ファイル

うろ覚えですが、make成功時に`/etc/sockd.conf`で設定ファイルが生成されていたはず。今回`danted.conf`とリネームして使用。
詳細な設定内容は[公式サイト](https://www.inet.no/dante/doc/1.4.x/index.html)と`danted.conf`のコメントを見るとすべてわかるはず。

以下に、簡単な設定内容を示す。パスワードは**平文**でやり取りされることに注意。

```danted.conf
logoutput: /var/log/danted.log
internal: eth0 port=50001
external: eth0
socksmethod: username
clientmethod: none
user.privileged: root       # 認証にusernameを使う場合はrootが必要
user.unprivileged: nobody
#user.libwrap: nobody
client pass {
        from: 0/0 to: 0/0   # IPv4/IPv6すべてを通す時は0/0記法が可能
        clientmethod: none # match all idented users that also are in passwordfile
        socksmethod: username
}
socks block {
        from: 0/0 to: lo0
        log: connect error
}
socks pass {
        from: 0/0 to: 0/0
        command: bind connect udpassociate
        log: connect error
        socksmethod: username
        clientmethod: none
        protocol: tcp udp
}
```
【EC2】ssm-userでgit cloneするとPermission denied (publickey).となる。

system managerとかいうサービスを使うと、sshしないで安全に接続できると聞いたので使ってみたのですが、git cloneできなくてハマりました。

間違っていたらご指摘願います。

やったこと↓↓

# sshの設定

・ssh-keygenする
・https://github.com/settings/keys ここに公開鍵を登録

# configを作成
```
Host github.com
HostName git
Port 22
User xxxxxxxxxx
IdentityFile ~/.ssh/xxxxxxxxx
```

# ポート開く
(これやっていいのかわからないのですが、) 以下設定する必要があるとかないとか

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355521/b4cbb781-3c72-13f8-99a4-7083fcbf75d2.png)

# 認証させる？
下記コマンドで、ああ、あの鍵使ってるのはお前なんだな、とgithub.comが確認してくれるみたい。

```
ssh -T git@github.com
```

# ユーザーに注意
```
#これはだめ
sudo git clone レポジトリ
```

ずっとsudoをつけて実行していたのですが、sudoをつけて実行すると、rootが実行したユーザーになります。しかし、gitに認識されているユーザーはssm-user(現在ログインしているユーザー)なので、Permission denied (publickey).となります。

## ①ユーザーを指定して実行
```
sudo -u ssm-user git clone レポジトリ
```

これでrootではなく、ssm-userが実行しいることになります。

##　②/var/www/htmlの権限を変更

sudoをつけていた理由は、ssm-userにはディレクトリに書き込み権限がなかったから。
権限を与えました。

```
$ sudo chown -hR ssm-user:ssm-user /var/www/html/
$ chmod 775 /var/www/html
```

勉強不足を感じました、、、、
これからも頑張ります。
Mac OS Montereyクリーンインストールをする

## バックアップ
内蔵HDの内容は消えるので、外付けHDDなどにバックアップを取る


## クリーンインストール(２時間くらい
3ステップ。一度上書きするのは避けられない

1. Montereyを上書きインストール
1. ディスクユーティリティからフォーマットしデータを消去
1. まっさらなHDに、Montereyを再インストール


### Montereyを上書きインストール
App Storeで検索、Monterey🔍
Montereyをインストールする
ダイアログ通りに再起動する(または、放置で自動再起動)
ログインすると、いつもの自分のデスクトップが表示されている

### ディスクユーティリティからフォーマット

純正キーボードを繋ぐ
Macの電源を落とす
command (⌘) + Rを押しながら起動(リンゴマークが表示されるまで)

復旧モードのメニューが表示される
![IMG_2984-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/0bd472c8-29c0-e9b6-7f11-ff963db903d8.png)

ディスクユーティリティを選択する
まずボリュームを削除する
![IMG_2987 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/f85f145c-909a-d8eb-3b82-69e154e39f43.png)

フォーマットして使えるようにする
名前はMacintoshHDにした
![IMG_2989 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/90724ea8-c3b8-36d1-e909-afc648427374.png)

ほとんどが空き領域になっていればOK
![IMG_2991 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/d6711969-d4c7-713d-1360-0ae428d51106.png)

### Montereyを再インストール
Mac OS Montereyを再インストールをクリック
![IMG_2985 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/eefa130e-df86-1c94-21c3-f2451061ff58.png)

あとは放置してソシャゲでもやっていれば、終わったらVoiceOverが喋りだすので
画面の指示のとおりにiCloudの認証などをしていけばOK
![スクリーンショット 2021-11-12 16.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13135/d5d7ba3c-c18a-ed87-4acc-38a7755b33a2.png)

これから地獄のアプリケーションインストールや各種設定の始まり
お疲れさまでした🙏

## 参考
[\[完全版\]macOS Catalinaをクリーンインストールする方法 - Qiita](https://qiita.com/PaSeRi/items/59e9785580dbd518ac93)
[Mac の起動時のキーコンビネーション](https://support.apple.com/ja-jp/HT201255)
cpuのbyte orderの影響範囲と具体例

例1: レジスタ上に存在する1バイトの`0000_0001`をメモリに乗っけると`0000_0001`のままである. 
例2: レジスタ上に存在する2バイトの`0000_0001 0000_0010`をメモリに乗っけると`0000_0010 0000_0001`の並びで配置されます 
例3: レジスタ上に存在する4バイトの`0000_0001 0000_0010 0000_0011 0000_0100`をメモリに乗っけると`0000_0100 0000_0011 0000_0010 0000_0001`の並びで配置されます

この様に、レジスタからメモリに移す際に反転される.

参考: https://qiita.com/tobira-code/items/a03f39a02678d80bbd26

## ext4のブロックに書き込む際

> ext4のすべてのフィールドは、リトルエンディアンの順序でディスクに書き込まれます。ただし、jbd2（ジャーナル）のすべてのフィールドは、ビッグエンディアンの順序でディスクに書き込まれます。

参考: https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout

理由の考察:
そもそも項目毎にリトルエンディアンとして書き込まないと駄目な理由は、ext4自体は項目毎に読み込んで、読み書きする.
ブロックへの書き込みも、ブロックサイズ未満の書き込みでも、ext4自体はブロックを読み込み、ブロック単位で書き込みをしている. その際に、メモリ上で読み込んだデータと今回書き込むデータを合成し、ブロック単位で書き込んでいるので、以下のAの様にはならず、Bの様に保存できる.

A:
`0000_0001 0000_0010`を書く. 続く所から`0000_0011 0000_0100`を書く.
実際: `0000_0010 0000_0001 0000_0100 0000_0011 ...`
これは間違い

B:
`0000_0001 0000_0010`を書く. 続く所から`0000_0011 0000_0100`を書く.
実際: `... 0000_0000 0000_0000 0000_0000 0000_0000 0000_0100 0000_0011 0000_0010 0000_0001`
の様に後ろから書き込まれていく.
これは正

## 通信する際

ネットワークプロトコルはビッグエンディアンだけを使う.

送る際にはhtonl
受け取る際にはntohl
で、エンディアン変換を行う. (ビッグエンディアンのCPUの場合は、何も処理が行われない. リトルエンディアンのCPUの場合だけ、byte orderが逆になる.

参考: https://www.jpcert.or.jp/m/sc-rules/c-pos39-c.html

### 理由

リトルエンディアンの規格のCPUの場合、メモリの並び順はCPU外部からみた際には、リトルエンディアンである.
レジスタの並び順はビッグエンディアンである. (レジスタはCPUからしか触れないけど.
メモリは共有資源であり、ストレージやネットワークデバイスは、メモリバス経由でメモリを読んだり書いたりする.
また、ストレージやネットワークデバイスはmemory-mapped i/o経由で操作される.
send_to syscallには、メモリ上に存在するbufferのアドレスと送信する長さを指定する.
リトルエンディアン規格のCPUの場合だと、メモリの中に存在するbuffer並び順はリトルエンディアンである.
ネットワークデバイスは指定されたアドレス値の中身を順番に送信していく為、リトルエンディアンの並び順のままだと受け取り側が正しく復号化できないので、TCP/IPプロトコルに沿ってビッグエンディアンの並び順になるようにメモリの状態を変更してから送る.
同様にビッグエンディアンの並び順で受信側は届くので、リトルエンディアンの並び順に変更してからデータを操作する必要がある.

補足すると、レジスタからメモリに配置する際に反転するので、structなら要素単位、固定長配列なら、要素単位で反転する.なので、もしstructの各要素をビッグエンディアンに並べ替えたい場合は、それぞれの要素に対してhtonlをする必要がある. また、固定長配列の要素をビッグエンディアンに並べ替えたい場合は、全要素にhtonlをする必要がある. これが必要になるケースは、バイナリをネットワーク経由でやりとりする際だが、ここまで来ると最適化の為に、ネットワーク経由でリトルエンディアンのままでのやりとりや保存とかもしそうだと思いました.

## その他参考資料

https://jp.quora.com/ippan-teki-ni-ritoru-en-de-i-an-deha-naku-biggu-en-de-i-an-ga-saiyou-sa-re-te-iru-riyuu-ha-nanda-to-omoi-masu-ka
https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol2A_i.pdf
https://blog.framinal.life/entry/2021/06/15/235343?utm_source=feed
https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/13_IOSystems.html
https://gist.github.com/yegle/4560621
速?報!! typeormの @transaction() decoratorはazure functionsでは使えない

今日ほぼ1日ここにハマりました。

```typescript
@Transaction()
    async _update(id: string, status: string, @TransactionManager() manager?: EntityManager): Promise<void> {
        if ( manager === undefined) {
            throw new Error('DB not getting TransactionManager.');
        }
        const res = await manager.findOneOrFail(Job, id);
        res.status = status;
        res.save();
    }
```

要するには、上記のようなコード書いたら、
local開発や`mcr.microsoft.com/azure-functions/node:3.0-node12`のようなdocker使って開発するのは全然OKのだが、いざazure functionsにデプロイすると、意味不明な下記エラーが出ます。

```sql
2021-11-12T15:20:38Z   [Information]   Error:  TypeORMError: Connection is not established with mysql database
    at new TypeORMError (/home/site/wwwroot/node_modules/typeorm/error/TypeORMError.js:9:28)
    at /home/site/wwwroot/node_modules/typeorm/driver/mysql/MysqlDriver.js:668:22
    at new Promise (<anonymous>)
    at MysqlDriver.obtainMasterConnection (/home/site/wwwroot/node_modules/typeorm/driver/mysql/MysqlDriver.js:656:16)
    at MysqlQueryRunner.connect (/home/site/wwwroot/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:59:58)
    at MysqlQueryRunner.<anonymous> (/home/site/wwwroot/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:208:63)
    at step (/home/site/wwwroot/node_modules/tslib/tslib.js:143:27)
    at Object.next (/home/site/wwwroot/node_modules/tslib/tslib.js:124:57)
    at /home/site/wwwroot/node_modules/tslib/tslib.js:117:75
    at new Promise (<anonymous>)
```

`transaction`以外のDB操作が少なくて(唯一のDBの読み取り操作も別の原因で振る舞いが狂って、接続ができたと言う確信が持たなくて)、全然`azure functions`と`typeorm`のせいだと思わずに、黙々接続情報やenvなどをデバッグしていた1日でした。

多分`@Transaction()`デコレータを使わずに、普通に`conn.transaction`すれば普通にいけると思うけど、今日はもうここまでにして。。。

別途azureとtypeormのgitにissueを出しますが、まずここにまとめます。同じ地雷を踏んで調べている人がいれば、少しでも時間節約できたら幸いです。

> typeorm version 0.2.38

> azure functions:
> Runtime version:3.3.1.0,
> FUNCTIONS_EXTENSION_VERSION: ~3,
> Operating System: Linux

> github actionsのAzure/functions-action@v1を使ってデプロイしている
アートを感じた話「ロイヤル・バレエ・スクールの『夢の架け橋』」

先日、会社で「７つの習慣」の研修を受けまして、その中で１つ、とてもアートを感じて印象に残ったことを書いておきます。

ロイヤル・バレエ・スクールに架けられた橋（渡り廊下）のお話です。

↓がロイヤル・バレエ・スクールの説明
https://thewells.co.jp/schoolguide/royal

そこに架けられている橋がコレ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/84a9e6ee-264c-15da-3953-f4409eed3462.png)
（参考： https://plaza.rakuten.co.jp/hotstuff1976/diary/200803310000/ ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448092/91d8894e-f4ff-bedd-b3c6-d6bd8645c9b6.png)
（参考： https://smileinuk.exblog.jp/13311457/ ）

とても斬新なデザインでそれだけでアートを感じさせますね(`･ω･´)

この橋は、「Bridge of Aspiration（直訳すると「願望の橋」）」という名前がついていて、「夢の架け橋」「希望の橋」とか呼ばれているものだそうです。

どこと繋がっているのかというと、「ロイヤル・オペラ・ハウス」、、つまり、スクールを卒業して一人前のバレリーナだけが立つことのできる舞台なのです。
そこと繋がっており、「ロイヤル・オペラ・ハウス」の舞台に立てるバレリーナになった人だけが渡れるもので、誰でも渡ることができるわけではないそうです。

スクールの生徒たちは、頻繁に隣の「ロイヤル・オペラ・ハウス」の舞台に立つプロのバレリーナの姿を見て学ばせ、そこで、生徒たちに「そこの舞台に立つ」という明確な目標を持たせることをしており、そして、「いずれはその橋を渡ってやる！」　そんな具体的な想いを持たせることで、日々の稽古に励ませているのでしょうね。

ただキレイで斬新なデザインの建築物というだけではなく、そこに関係する人達にとっての深い意味やストーリーを持たせたというところに私はアートを感じたのです。


私もこういったビジネスの提案だったり、エンプロイーエクスペリエンス（Employee Experience）の仕組みを提案できるアーティスティックなエンジニアになりたいものですわ

こういった話、とても興味あるので、似たような話ありましたら、ぜひ教えてくださいm(__)m
# Amazon LightSailからNTT-PC WebARENA Indigo VPSに移動②：初期セットアップ

# Amazon LightSailからNTT-PC WebARENA Indigo VPSに移動②：初期セットアップ

[前回のあらすじ](https://qiita.com/syakesaba/items/34553a66e0d467797629)

> ぼく「国内で安いのは[NTT WebARENA Indigo](https://web.arena.ne.jp/indigo/)かぁ。」
> ぼく「Indigo、OSサポート、AlmaもRockyも無いんだね。Ubuntu LTS 20.04が一番サポート長いから使ってみるか」

# VPSプランの選択
- 選んだプラン: 月額1,399円1時間当たり2.2円
 - OS: **Ubuntu 20.04 LTS**
 - メモリ: **4GB**
 - CPU: **4vCPU**
 - SSD: **80GB**
 - NW: **500Mbps上限**

# インスタンスを作成
    1. 暫定でSSHの鍵ペアをWebコンソールで作る（後で吹き飛ばす）
    1. インスタンスを作成（同時にグローバルIPアドレスが付与される）
    1. 鍵ペアを手元の端末の.ssh/id_rsaにでもコピーしてからSSHで乗り込む: 

```
ssh -l ubuntu <IP>
```

# 不必要なアカウントを消し去り、rootに対する公開鍵認証でしかログインできないようにする

```
sudo su
passwd #Webコンソールから入る為に設定
userdel ubuntu
rm -rf /home/ubuntu
(/root/.ssh/*を適当なファイルにするかssh-keygen、authorized_keysに公開鍵を保存)
```

# その他細かい初期セットアップ

```
#ホスト名関連
vi /etc/hostname
vi /etc/hosts
#swap作成関連
fallocate -l 4G /swap
chmod 600 /swap
mkswap /swap
swapon /swap
cp /etc/fstab /etc/fstab.bak
vi /etc/fstab #以下を追記
/swap swap swap defaults 0 0
#ローカライズ関連
apt-get install language-pack-ja
update-locale LANG=ja_JP.UTF-8
timedatectl set-timezone Asia/Tokyo
#アップデート
apt-get -y update
apt-get -y upgrade
#再起動
reboot
#swapの確認
free -h
#localeの確認
locale
```

# パーソナルファイアウォールの有効化

```
#ufwファイアウォールの設定(一旦SSHのみ許可)
ufw allow ssh
ufw enable
#fail2banの設定(ufwによる管理)
vi /etc/fail2ban/jail.conf #以下に変更
banaction = ufw
banaction_allports = ufw
systemctl enable fail2ban
systemctl restart fail2ban
#適当にSSHでパスワードアタックしてufw statusにREJECT載ってきたらOK
```

# 個人的VPSサーバ設計思想
- VPSは大抵の標準でswap領域が無いが、せっかくSSDなのでメモリと同レベルのswapを用意しておく。
- 一般ユーザは消す。rootでなんでもやって、やらかしたときはVPSを作り直せば良い。
- パスワードは絶対わからないレベルのものをrootに与える（SSHは公開鍵認証。緊急時にWebコンソール使えりゃそれでいい）。
- パスワードアタックしてくるやつは大抵敵なので、たとえ公開鍵認証しか許可してなくても、fail2banで通信を拒否する。

# ③へつづく・・・・
MySQL レコード、テーブル命令

#MySQL レコード、テーブル命令

```
SELECT カラム名 FROM テーブル名；
<!-- テーブルからレコードの取得(カラム名を * にすると全て取得する)　　-->

SELECT カラム名 FROM テーブル名　WHERE カラム名 = レコード値；
<!-- 条件付き指定の取得 -->

SELECT カラム名 FROM テーブル名　WHERE カラム名 = レコード値１　OR カラム名 = レコード値２；
<!-- 複数レコードの取得  -->

SELECT ＊ FROM テーブル名 WHERE  カラム名 like "%検索ワード%";
<!-- 曖昧検索　  -->

INSERT　INTO テーブル名(カラム名, ...) value(レコード名, ...);
<!-- レコード追加  -->

UPDATE テーブル名 SET カラム名 = 新しいレコード WHERE id = 更新したいid;
<!--  レコードの更新 -->

DELETE FROM テーブル名 WHERE id = 削除したいid;
<!-- レコードの削除  -->


CREATE TABLE テーブル名 (カラム名, ...);
<!-- テーブル作成  -->

CREATE TABLE テーブル名 (カラム名, ...)DEFAULT CHARACTER SET = utf8;
<!-- 「DEFAULT CHARACTER SET = utf8」 で文字化け対策 -->

DROP TABLE テーブル名;
<!-- テーブル削除 -->

ALTER TABLE テーブル名 ADD 新しいカラム名 データ型 そのほか条件;
<!--  カラムの追加 -->

ALTER TABLE テーブル名 CHANGE 変更前のカラム名 新しいカラム名 データ型 そのほか条件;
<!-- カラムの変更、更新  -->

ALTER TABLE テーブル名 DROP カラム名;
<!-- カラムの削除  -->
```
jquery.cookie.js を使いたい。でも SameSiteに対応していない

#ご安心下さい。簡単な改造で jquery.cookie.js に samesiteに対応することができます。

CDNでは対応することができませんので、ソースをダウンロードします。

[jquery.cookie.js(github)](https://github.com/carhartl/jquery-cookie/blob/master/src/jquery.cookie.js)


##その後、ソースに以下の部分を探す


```jquery.cookie.js
			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
```

###おおむね、以下のように変更する

```jquery.cookie.js
			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.sameSite  ? '; sameSite=' + options.sameSite : '',
				options.secure  ? '; secure' : ''
			].join(''));

```

				options.sameSite  ? '; sameSite=' + options.sameSite : '',

を途中加わっていることがわかるかと思います

###使い方

```
$.cookie("name", "value", {expires: 365, path: "/", sameSite: "lax"});
```
アイコンをランダムにしゃべらせる

![スクリーンショット 2021-11-13 0.11.31.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2245182/1a88cc01-e42e-7d37-3de1-3d0bc48d4e32.jpeg)

こんなかんじでロゴをホバーしたらランダムに喋らせる試み
ホバーするたびに内容が変わるようになっている

## 開発環境
`ruby 2.6.5`
`Ruby on Rails 5.2.5`

## 前提
`jQuery` を導入している
喋らせたいロゴをつくってある

## 本題

吹き出しは以下を参考にした

https://blog.raizzenet.com/balloon-on-image-on-mouse-hover/

ランダムに喋らせる `JS` のスクリプトは以下

```:js
document.addEventListener("turbolinks:load", function(){
  $('.sample').hover(function() {

      let lists=[
            喋らせたい内容を配列でいれる
          ];
      let rNo = Math.floor(Math.random() * lists.length);
      let rSt = lists[rNo]
      this.lastChild.innerHTML = rSt
  
　　});
});
```

`View` は以下

```:view
             li.sample 
                = link_to image_tag("/logo.png", class: ""), home_path, 
                .box
                  p.text
                    |クリックしてもジャンプしません
```

`li` のホバーにイベントをつけてやって、
ホバーする度にランダムの数を生成し、DOM操作で書き換える
【Python】文字列をn文字に分割する

## 環境

Python 3.9.2

## 関数

```py
import re

def split_n(text, num, fraction=True):
    if fraction:
        return re.findall(f'.{{1,{num}}}', text) 
    else:
        return re.findall(f'.{{{num}}}', text)
```

## 利用例

```py
# 端数も返す
print(split_n("abcdefgh", 3))
# => ['abc', 'def', 'gh']

# 端数を返さない
print(split_n("abcdefgh", 3, fraction=False))
# => ['abc', 'def']
```

## 関数化までは不要な場合

```py
import re

# 端数も返す
print(re.findall('.{1,3}', "abcdefgh"))
# => ['abc', 'def', 'gh']

# 端数を返さない
print(re.findall('.{3}', "abcdefgh"))
# => ['abc', 'def']
```
raspberry pi でPythonを自動起動させたいんや！！！

タイトルのとおりです。

```
sudo chmod 755 hello.py
sudo nano /etc/rc.local
```

```
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

python3 /home/pi/Desktop/hello.py 

他の処理

他の処理

exit 0
```

```
sudo reboot
```

はい、これだけ

他の記事を見ていると
```
python3 /home/pi/Desktop/hello.py &
```
と書かれているが、 
### ＆をなくしたら動いた。

なんでか分からん。

Bootをネットワークにしてたが、Desktopに変えても動いた。
まずはこの形で試してみるほうが差表が少なくていいかもしれない。


2021/12/02追記

シンプルにコードのミスで動いてなかった？
ループするコードなら&は書いておいた方が良い。