OmniAICreator/Qiita-1.07M huggingface dataset:
Node-RED（初級）：芝犬の画像をランダムに取り出すデモを作成する方法

#node-REDを使ってみる
Node.jsを少し触るようになってから、興味のあったローコードであるnode-REDについて学ぶ機会があったので、健忘録がてら使用方法についてまとめました。

#node-REDの初期設定
##ローカルで実行するための準備
###インストール

```:ターミナル
$ sudo npm install -g node-red
```

###node-redを実行

```:ターミナル
$ node-red
```

下記のような表示が出れば成功
![スクリーンショット 2021-02-11 21.42.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/f3a5dc37-0f2f-6aab-355f-060be69d741e.png)

###ブラウザで起動
今回の場合、http://127.0.0.1:1880/をブラウザに直接入力

![スクリーンショット 2021-02-11 21.45.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/3ed471b9-29fd-4f88-2ed1-303f7dc05540.png)

node-REDが起動
![スクリーンショット 2021-02-11 21.47.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/9b8d5b9e-f083-2233-1a4b-26c4ee70e083.png)

## node-REDをとりあえず使ってみる
まずは簡単に、タイムスタンプがコンソールに表示されるフローを作成
①　injectノードをフィールドにドラックアンドドロップ
②　debugノードをフィールドにドラックアンドドロップ
③　デプロイをクリック
④　デバッグタブを選択
⑤　フィールド上にあるタイムスタンプの左側をクリック
⑥　デバッグタブ内にタイムスタンプがポストされる
[![Image from Gyazo](https://i.gyazo.com/a1b69e1e35affb259991b7b344b8b72e.gif)](https://gyazo.com/a1b69e1e35affb259991b7b344b8b72e)

#node-REDでAPIを利用
##使用するAPIを選択する
今回は、次のURLから利用できるAPIを選択。
https://github.com/public-apis/public-apis#food--drink
その中で、ランダムに柴犬の画像を取得できる以下のAPIを今回利用
http://shibe.online/api/shibes?count=[1-100]&urls=[true/false]&httpsUrls=[true/false]

##APIをデバッグ上に表現する

###'http request'を使って、3つのランダムなAPIを取得する。
[![Image from Gyazo](https://i.gyazo.com/fdf46567c68a72cc1cb95cabecdc8d5d.gif)](https://gyazo.com/fdf46567c68a72cc1cb95cabecdc8d5d)

###その後、'change node'を使って3つの中から1つを選択する
[![Image from Gyazo](https://i.gyazo.com/ba42101095d8bd10ecdf522bfff20117.gif)](https://gyazo.com/ba42101095d8bd10ecdf522bfff20117)

##画像をフィールド上に表現する
###'node-red-contrib-image-output'をパレットに追加
[![Image from Gyazo](https://i.gyazo.com/a69cb0cc1c874788b730cb25467e9385.gif)](https://gyazo.com/a69cb0cc1c874788b730cb25467e9385)

###image nodeを利用してフィールド上に画像を描出する
[![Image from Gyazo](https://i.gyazo.com/91d80481567b7a9843edda431ad4ad5a.gif)](https://gyazo.com/91d80481567b7a9843edda431ad4ad5a)

##ダッシュボードを利用してデモ画面を作る
###'node-red-dashboard'をパレットに追加する
[![Image from Gyazo](https://i.gyazo.com/e3d55789933fd4c6b41d90c73a80ae9c.gif)](https://gyazo.com/e3d55789933fd4c6b41d90c73a80ae9c)

###buttonをクリックすると画像が表示されるように設定
'button'と'template'を利用
template内に

```html:
<img ng-src="{{msg.payload}}">
```
と記載。

[![Image from Gyazo](https://i.gyazo.com/c9eb21b94e26159f5e38212ce4fa9e53.gif)](https://gyazo.com/c9eb21b94e26159f5e38212ce4fa9e53)
SWIGで文字列を返す

# やりたいこと

引数で文字列を返すメソッドをSWIGで生成したい。
SWIG 4.0.2はstd::string*に対応していないので手動で対応してみる。

```c++:Sample.h
class Sample {
public:
  void GetSettingName(std::string* settingName) {
    *settingName = "ABC";
  }
};
```

C#側では以下のように呼び出す。

```c#:Program.cs
static void Main() {
  var sample = new Sample();
  sample.GetSettingName(out string settingName);
}
```

# 実装例

sample.iの設定でC++側のC#用メソッドが作成される。
CSharp_Sample_GetSettingNameはC#向けに公開したいメソッド。
%ignoreで該当メソッドを自動生成しないようにする。
生成されたC#側のクラスにメソッドを追加したいのでpartial指定する。

```text:sample.i
%module example
%typemap(csclassmodifiers) Sample "public partial class"
%ignore Sample::GetSettingName(std::string* settingName);
%{
#include <windows.h>
#include "Sample.h"
%}
%include <windows.i>
%include "Sample.h"
%{
#ifdef __cplusplus
extern "C"
#endif
SWIGEXPORT void SWIGSTDCALL CSharp_Sample_GetSettingName(
  void * self, BSTR* bstrSettingName) {
  std::string settingName;
  ((Sample*)self)->GetSettingName(&settingName);
  *bstrSettingName = ::SysAllocStringByteLen(
    settingName.c_str(), settingName.length());
}
%}
```

手動で作成したC++側のメソッドを呼び出す。

```c#:Sample.manual.cs
using System.Runtime.InteropServices;
partial class Sample {
  private static class NativeMethods {
  [DllImport("example", EntryPoint = "CSharp_Config_GetSettingName")]
  public static extern void GetSettingName(
    HandleRef self,
    [MarshalAs(UnmanagedType.AnsiBStr)] out string bstrSettingName);
  }
  public void GetSettingName(out string settingName) {
    NativeMethods.GetSettingName(swigCPtr, out settingName);
  }
}
```
はじめてのコマンドライン

# はじめに
※この記事は**コマンドの覚書きがメイン**で、「パス」「カレントディレクトリ」などの説明ができていません。
　その部分から学びたい方がいらっしゃれば、
　「絶対パス　相対パス」「ディレクトリ」「コマンドライン」などの検索ワードで
　図や写真が載っていて分かりやすい記事が見つかります。

# ✏️コマンドラインってなに？
わたしたちは普段、感覚的にコンピューターを操作しています。
例えば・・・「クリックしてサイトを開く」「右クリックでファイルを作成する、削除する」など。
(📝用語でいうとGUI）

これを、文字だけで操作するものを**コマンドライン**といいます。

**ターミナル**(windowsの方は違うものかもしれません)と呼ばれる場所で、
**コマンド（コンピューターへの命令文）**を入力します。
すると、文字だけでコンピュータを操作できます。
(📝用語でいうとCUI）

# ✏️メモ
私が学習する時に、「なんだろう」と思ったことを書き残しておきます。
#### ■気になった「$」マーク
ターミナル(windowsの方は違うものかもしれません)を開くと、
最初から「$」マークが表示されているかと思います。
気にしなくて大丈夫です。
(パソコンによって表示が異なるようです。)

#### ■これからコンピューターを操作する全体的なイメージ
$マーク(パソコンによって異なる可能性有)の右側にコマンド(コンピューターへの命令文)を打つ
↓
Enterキーを押す=コマンド（命令）を実行する
↓
打ったコマンドが実行されて、実行した結果が表示される

# ✏️どんなコマンドがあるの？
:white_check_mark:**「$」マークははじめから表示されているので打たなくてよいです**
:white_check_mark: **コマンドを打ったらEnterキーを押します**

#### ■自分の今見ているフォルダの場所を確認する(pwd)
Present Working Directoryの略といわれているようです。
(📝用語でいうとカレントディレクトリ)

```
$ pwd
打てたらEnterキーを押して実行
```
#### ■自分の今いるフォルダ内のファイルを全表示する(ls)

```
$ ls
打てたらEnterキーを押して実行
```
#### ■隠しファイルまで全表示する(ls -a)
lsでは表示されていないファイルまで表示できます。

```
$ ls -a
打てたらEnterキーを押して実行
```
#### ■ファイルの中身を表示する(cat)
```
$ cat　ファイル名
打てたらEnterキーを押して実行
```
#### ■フォルダを移動する(cd)
cd = change directory
今自分の見ているフォルダから別のフォルダを見たい時に使います。
※フォルダの場所を移動させるコマンドではありません

```
$ cd フォルダ名
# 打てたらEnterキーを押して実行
```
#### ■ひとつ前の親フォルダに移動する(cd)

```
$ cd ..
# 打てたらEnterキーを押して実行
```

#### ■いちばん番親のフォルダに移動する(cd)
(📝用語でいうとホームディレクトリ)

```
$ cd
# 打てたらEnterキーを押して実行
```

#### ■フォルダを新規作成する(mkdir)
mkdir = make directory

```
$ mkdir フォルダ名
打てたらEnterキーを押して実行
```
#### ■ファイルを新規作成する(touch)
```
$ touch ファイル名
打てたらEnterキーを押して実行
```
#### ■ファイル・空のフォルダを削除する(rm)
```
$ rm ファイル・フォルダ名
打てたらEnterキーを押して実行

```
#### ■中身のあるフォルダを削除する(rm -r -f)
rm = remove
-r = 中身を
-f = 強制的に

```
$ rm -r -f フォルダ名
打てたらEnterキーを押して実行
```
#### ■ファイル・フォルダを移動させる(mv)
ファイル・フォルダを別の場所に移動させたいときに使います。
(📝パス→「プログラミング　パスとは」などの検索ワードで解説記事がでてきます)

```
$ mv 移動させたいファイル・フォルダ名 移動先のパス
打てたらEnterキーを押して実行
```
#### ■ファイル・フォルダ名を変更する(mv)
mv = move

```
$ mv 変更したいファイル・フォルダ名 新しく名付けるファイル・フォルダ名
打てたらEnterキーを押して実行
```

#### ■ファイルをコピーする(cp)
cp = copy

```
$ cp コピーしたいファイル名 コピーした新しいファイルの名前
打てたらEnterキーを押して実行
```

#### ■フォルダをコピーする(cp)
cp = copy
中身のあるフォルダごとコピーしたい時に使います。

```
$ cp -r コピーしたいフォルダ名 コピーした新しいフォルダの名前
打てたらEnterキーを押して実行
```

# 便利な機能
#### Tabキーの補完機能
コマンドラインには便利な機能があります。
コンピューターが「これかな？」と予想して単語を自動入力してくれる機能です。

例えば・・・**shirokuma.txt**というファイルの中身を見たいとき、
「$ cat shi」まで打って**Tabキー(キーボードの左はじっこくらいにあります)**を押すと
「$ shirokuma.txt」と自動で予想して打ってくれます。

入力ミスを減らせて時短にもなります。

# おわりに
最後までご覧いただきありがとうございます。
間違いもあるかと思いますので、その際はコメントで教えていただけるとうれしいです。
Herokuにデプロイする際のエラーstatus=500の解消例

#はじめに
status=500のエラーが起こる原因は様々でデバックには一苦労しますが、
初めからアプリを作成し直すのは待ってください！！

なお、DBに問題があり、デバックできないケースもありますので作り直さなければ
ならない場合もあります。

デバックできないと判断するまで、原因を洗い出しエラー解消に努めましょう！！

本記事はあくまでもstatus=500のエラーの一例です。

#バージョン
rubyのバージョン　ruby-2.6.5
Railsのバージョン Rails:6.0.0

#前提
app/javascript/packs/application.jsのturbolinksの記述は削除済み

```app/javascript/packs/application.js

〜省略〜

require("@rails/ujs").start()
// require("turbolinks").start()// ←この行を削除する
require("@rails/activestorage").start()
require("channels")

〜省略〜

```

#結論
app/views/layouts/apprication.html.erb内のturbolinksの記述を全て削除すると、エラーが解消されました。

（変更前）

```app/views/layouts/apprication.html.erb

〜省略〜

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

〜省略〜

```

↓

（変更後）

```app/views/layouts/apprication.html.erb

〜省略〜

<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>

〜省略〜

```

#エラーの内容

①Herokuにデプロイしてheroku openした瞬間エラーが起こりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/0699e396-c151-e99d-f19e-ae11065aee3a.png)

②ログを確認するとstatus=500の赤字が断続的に出現。

```
$ heroku logs --tail --app praise-homekata

```

#エラーの原因
application.js内のturbolinksの記述を削除しても、turbolinksの画面遷移を高速化する効果は残ります。
そのため、自身が記述していたコードのエフェクトがturbolinksの効果の兼ね合いで、上手く読み込まれず今回のエラーが起こったと考えられます。

以上です。
with_optionsで複数のバリデーションをまとめる

# with_options

`with_options 〇〇`と書くことで、
複数の情報に対して共通したオプションを付けることが可能。

## 具体例を用いて比較

「a、b、cそれぞれのカラムが空では保存できない」というバリデーションを設定する場合

```rb:
class Sample < ApplicationRecord
  validates :a, presence: true
  validates :b, presence: true
  validates :c, presence: true
end
```
>この様に共通の`presence: true`(カラムが空では保存できない)といった
validationをひとつひとつに設定していく


```rb
class Sample < ApplicationRecord
  with_options presence: true do
    validates :a
    validates :b
    validates :c
  end
end
```
>上記の様にdo〜end内でバリデーションを指定した属性に対して、
値が存在 (presence) しなければならないという`制限を一括で設ける`ことができる

---
---

### 一括で設けた上で更に細かなvalidationを設定する

>validatesメソッドのオプション
>今回は`formatオプション`に関して
>>`numericality`オプションもあります

- モデルクラスの中で`validatesメソッド`を使用する際に利用できるオプション
- `文字列`で保存される情報に正規表現によって入力制限をすることが可能

```:記述方法
format: {with: 正規表現, message: 正規表現にマッチしなかった場合のエラーメッセージ}
```
<br>

### 今回ここで紹介する正規表現は以下の６種類

|正規表現|意味|
|:-:|:-:|
|/\A[ぁ-んァ-ン一-龥々]/|全角ひらがな、全角カタカナ、漢字|
|/\A[ァ-ヶー－]+\z/|全角カタカナ|
|/\A[a-zA-Z0-9]+\z/|半角英数|
|/\A\d{3}[-]\d{4}\z/|郵便番号（「-」を含む且つ7桁）|
|greater_than_or_equal_to: 〇〇|〇〇と同じか、それ以上の数値|
|less_than_or_equal_to: △△|△△と同じか、それ以下の数値|

>実際に使用する正規表現
>>1. 全角ひらがな、全角カタカナ、漢字
2. 全角カタカナ
3. 半角英数

<br>

```rb:実際の記述
class User < ApplicationRecord
  with_options presence: true do
    validates :name, format: { with: /\A[ぁ-んァ-ン一-龥々]/, message: "全角ひらがな、全角カタカナ、漢字で入力して下さい" }
    validates :name_reading, format: { with: /\A[ァ-ヶー－]+\z/, message: "全角カタカナで入力して下さい" }
    validates :nickname, format: { with: /\A[a-z0-9]+\z/i, message: "半角英数字で入力して下さい" }
  end
end
```
- `with_options presence: true do`
    - 値が存在 (presence) しなければならないという制限を一括で設ける
- `validates :name`
    - カラム名を指定
- `format: { with: ~~`
    - withオプションで与えられた正規表現と属性の値がマッチするかどうか

>この様な形で共通のvalidationを設定した上で、
`更に細かなvalidationを設定する`ことが可能

## 更にまとめる

```rb:with_optionsにwith_options
with_options presence: true do
  validates :user
  validates :item
  with_options uniqueness: true do
    validates :price
    validates :email
  end
end
```
>ネストして組み合わせると
上記の様に`よりコンパクトにまとめる`事も可能
Gitの基本

#【Gitの基本】

山浦清透さんのUdemy [Git： もう怖くないGit！チーム開発で必要なGitを完全マスター](https://www.udemy.com/course/unscared_git/)で学習させていただいています。
いつもありがとうございます。

Gitの備忘録です。

##Git

Gitは、スナップショットで記録している。
　→直前のコミットを保存しているので、前の状態に戻すことが容易

##Gitの流れ

開発作業の大半は、ローカルでおこなうため、
Gitのローカルでの流れの理解がとても重要になる。

<strong>ローカルは３つのエリアに分かれている</strong>

・ワークツリー （ファイルを変更する作業場所）
・ステージ （コミットする変更を準備する場所）
・リポジトリ （スナップショットを記録する場所）

ワークツリーからステージに記録するコマンド
　→ git add （追加）　

ステージからリポジトリに記録するコマンド
　→ git commit （コミット）　

##ローカルリポジトリの新規作成

.gitディレクトリを作成
　→ git init 

このディレクトリの中身は、

　・リポジトリ
　　・圧縮ファイル　
　　・ツリーファイル
　　・コミットファイル
　・インデックスファイル
　・設定ファイル

などであり、Gitに必要なファイルが保存されている。

##変更を記録

Gitエディタが立ち上がり、ファイルの変更内容を確認できる。
コミットする前に、変更内容を最終確認することが出来る。
　→ git commit -V

git statusで何のファイルが変更されたか確認後、git commit
（ステージに変更しただけで、リポジトリにコミットしていないものがわかる。）

##変更差分を確認

git addする前の変更分（ワークツリーとステージの間）
　→　git diff

git addした後の変更分（ステージとリポジトリの間）
　→　git diff --staged

##ファイルの削除を記録

リポジトリとワークツリーから削除
　→　git rm < ファイル名 > 
　→　git rm -r < ディレクトリ名 > 

リポジトリからのみ削除
　→　git rm --cached < ファイル名 > 

##ファイルの移動を記録

旧ファイルから新規ファイルに移動
　→　git mv < 旧ファイル > < 新規ファイル >
３次元極座標系と一様乱数を用いると球面上に一様に乱数は生成できない 

３次元極座標系を用いて球面上に一様な乱数を生成しようとする。

```math
x=r\sin{\theta}\cos{\phi}\\
y=r\sin{\theta}\sin{\phi}\\
z=r\cos{\theta}\\   
\\
(0\leq{\theta} \leq {\pi}, 0\leq{\phi} \leq 2{\pi})
```
#実際のプログラム

今回はAnacondaのJupyter Notebookを用いてプログラムを実行した。

###全体

全体としては下記のように記述した。

```python:
import numpy as np
import random
import math
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize = (5,5))
ax = fig.add_subplot(111, projection = "3d")


test=1000
r=1.0
for i in range(test):
    a = random.uniform(0, np.pi)
    b = random.uniform(0, 2*np.pi)
    
    x = r*np.sin(a)*np.cos(b)
    y = r*np.sin(a)*np.sin(b)
    z = r*np.cos(a)
    
    X = [x]
    Y = [y]
    Z = [z]
    
    ax.plot(X,Y,Z,marker="o")
    ax.tick_params(labelbottom = False,
               labelleft = False,
               labelright = False,
               labeltop = False)
    
plt.show()
```

###方針

方針としてはx,y,zについてr=1とし、θとΦをramdom関数を用いて擬似乱数として1000回設定し、点(x,y,z)がどのように球面状に分布するかを考える。
（果たして球面上に一様に分布するかどうかを確かめる。）

####必要なライブラリをインポート
numpy,matplotlib.pyplot,randomをインポートする。

```python:
import numpy as np
import random
import math
import matplotlib.pyplot as plt
```



####綺麗な３次元図形を描くための準備をする

サイズが対称な３次元図形となるために必要なコードを入れる。

```python:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize = (5,5))
ax = fig.add_subplot(111, projection = "3d")
```

####for文の中で(x,y,z)を作成しプロット

r=1としtest=1000回の繰り返しを実行する。
a=θ, b=Φとしてそれぞれ0~π, 0~2πの範囲の中でランダムな数を生成する。
x,y,zにθ,Φを代入する。

このまま散布図としてプロットすることができなかったため、x,y,zの値をX,Y,Zのリストの中に入れてからプロットする。

```python:
test = 1000
r = 1.0
for i in range(test):
    a = random.uniform(0, np.pi)
    b = random.uniform(0, 2*np.pi)
    
    x = r*np.sin(a)*np.cos(b)
    y = r*np.sin(a)*np.sin(b)
    z = r*np.cos(a)
    
    X = [x]
    Y = [y]
    Z = [z]

   ax.plot(X,Y,Z,marker = "o")

plt.show()
```

####補足

無駄な軸の目盛りを削除した。

```python:
ax.tick_params(labelbottom = False,
               labelleft = False,
               labelright = False,
               labeltop = False)
```

###結果

極付近に点が集中してしまい、一様に分布しない。
つまり、極座標系を用いて球面上に一様乱数を生成することはできない。

![スクリーンショット 2021-02-11 22.57.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067896/68d16a00-c341-bab9-d492-d93be62b10c8.png)



#一様分布にならなかった理由

３次元の場合、球面上の微小面積は

```math
dS=r^2\sin{\theta} d{\theta}d{\phi}\\\
```
すなわち、曲付近（θが小さい）ほど微小面積が小さく、小さな範囲に乱数が集中してしまうためである。

次回は一様乱数を球面上に作るための方法を考える。
基于Nacos实现高可用服务注册中心部署（一：虚拟机环境配置）

目标：在Linux系统搭建三个节点组成的Nacos集群，实现高可用服务注册中心。

**相关环境**
`OS:macOS Big Sur`
`VM:VMware Fusion 12`
`Linux镜像:CentOS-7-x86_64-Minimal-1611.iso`
`JDK:CentOS-7-x86_64-Minimal-1611.iso`
`nacos:1.4.0`

## 创建虚拟机
用CentOS-7-x86_64-Minimal-1611.iso镜像文件在VMware中创建虚拟机。注意：分配2048MB内存，设置系统初始化root密码。设置虚拟机网卡：连接模式选择桥接模式（Bridged Adapter）。虚拟机必须有一块网卡，这块网卡是通过什么方式去宿主机里的网卡进行通讯，桥接网卡，就可以让你的虚拟机里的网卡跟你宿主机的一块网卡进行通讯。

## 配置网络
修改文件：`$vi /etc/sysconfig/network-scripts/ifcfg-ens33`

```
...
ONBOOT=yes #设置系统启动激活网卡
...
```
重启服务：`$service network restart`
查看自动分配的IP地址：`$ip addr`
设置静态IP：

```
BOOTPROTO=static       #静态IP
IPADDR=192.168.1.8     #IP地址
NETMASK=255.255.255.0  #掩码
GATEWAY=192.168.31.1   #即本机路由器
#注释掉设备ID
```
重启服务：`service network restart`

检查网络状态

```
检查NetManager的状态：$systemctl status NetworkManager.service
检查NetManager管理的网络接口：$nmcli dev status 
检查NetManager管理的网络连接：$nmcli connection show
```
设置dns：`$nmcli con mod enp0s3 ipv4.dns "114.114.114.114 8.8.8.8"`
使dns配置生效：`$nmcli con up enp0s3`
*注：要搭建集群，在本地有防火墙的话，可能会没有办法互相连接，会导致搭建失败。*

关闭防火墙：

```
$systemctl stop firewalld.service
$systemctl disable firewalld.service
```

## 安装JDK
1. 将rpm文件通过scp上传到虚拟机中：`$scp jdk-8u131-linux-x64.rpm root@ip:/usr/local`。
2. 安装JDK：`$rpm -ivh jdk-8u131-linux-x64.rpm`
3. 配置jdk相关的环境变量：

```
$vi /etc/bashrc #edit

...
export JAVA_HOME=/usr/java/latest
export PATH=$PATH:$JAVA_HOME/bin
...

$source /etc/bashrc #enable 
```
测试jdk安装是否成功：`java -version`。

## 安装CentOS集群
按照上述步骤，再安装2台一模一样环境的linux机器。安装好之后，在每台机器的hosts文件里面，配置好所有的机器的ip地址到hostname的映射关系
`$vi /etc/hosts`

```
192.168.1.8 nacos01
192.168.1.9 nacos02
192.168.1.10 nacos03
```

## 设置SSH免密码互相通信
1. 生成SSH密钥：`ssh-keygen -t rsa`
2. ssh-keygen命令默认会将公钥放在/root/.ssh目录下。`cd /root/.ssh`
3. `cp id_rsa.pub authorized_keys` 将公钥复制为authorized_keys文件，此时使用ssh连接本机就不需要输入密码了。
4. 配置三台机器互相之间的ssh免密码登录，在每台机器上执行下面的命令
使用ssh-copy-id -i hostname命令将本机的公钥拷贝到指定机器的authorized_keys文件中。

```
nacos01 $ssh-copy-id -i nacos02
nacos01 $ssh-copy-id -i nacos03
...
```
测试SSH免密配置是否成功：`ssh nacos02`。
【早見表】よく使うDocker コマンド

よく使うので自分の備忘録として

### コンテナを立ち上げる
```
$ docker-compose up -d 
```
### コンテナ内のappに入る
```
$ docker-compose exec app bash
```
### イメージの一覧が確認
```
$ docker images
```
### イメージの削除
```
$ docker rmi <image ID> 
```
### ボリュームの一覧が確認
```
$ docker volume ls
```
### ボリュームの削除
```
$ docker volume rm <volume ID> 
```
コンテナイメージとボリュームを一度全部消してから再度 docker-compose up -d してみてください。
（消すときは事前に docker-compose down でコンテナを停止した状態で行ってください）

### 起動しているものと起動していないもの、全てのコンテナの一覧をみれる

```sh
$ docker ps -a
```

### ログ出力
```sh
$ docker attach CONTAINERID
```
AppleのHuman Interface Guidelinesを読む 〜Tab Bar編〜

# やること
下記のガイドラインのTabBarの部分を和訳&要約して理解する。
[Human Interface Guidelines - Design - Apple Developer](https://developer.apple.com/design/human-interface-guidelines/ios/bars/tab-bars/)

# 要約
* Tab Barはナビゲーションのために使う。→ 作成・削除などの操作ボタンを表示したい場合はTool Barを使う
* Tab Barの項目数は適切な数にする。 → iPhoneでは大体3〜5個
* Tab Barを非表示にしない。 → モーダルビューは例外的にOK。
* Tab Barの項目を消したり、無効化(タップできないように)しない。 → 利用できないTabでは、利用できない理由や利用するための方法を説明する

# Tab Bars
* アプリ画面の下部に表示されるTab Barによって、アプリの様々なセクションをすばやく切り替えることができる。
* Tab Barは半透明で、背景の色合いを残しつつ、すべての画面で同じ高さを維持する。
* キーボードが表示されると非表示になる。
* 水平方向のスペースが限られていて一部のタブを表示できない場合は、最後のタブは「その他」タブになる。

### Tip
* Tab BarとTool Barの違いを理解することは重要。
* どちらのBarも画面の下部に表示されるが、以下のような違いがある。
* Tab Barは、例えば時計アプリの「アラーム」、「ストップウォッチ」、「タイマー」など、様々なセクションを切り替えるためのものである。
* Tool Barは、「アイテムの作成」、「削除」、「コメントの追加」、「写真の撮影」など、現在のコンテキストに関連するアクションを実行するためのボタンを配置するためのものである。
* Tab BarとTool Barが同じ画面に同時に表示されることはない。

## Tab Barを使用してアプリレベルで情報を整理する
* Tab Barは情報階層をフラット化し、複数の同等な情報カテゴリ・モードへのアクセスを提供する優れた方法である。

## Tab Barはナビゲーションのために使用する
* **アクションを実行するためにTab Barを使用してはならない**。
* 現在のViewの要素に作用するコントロールを提供する必要がある場合は、代わりにTool Barを使用する。

## 適切な数のTab数にする
* Tabの数が多すぎると、各Tabのタップ可能な領域が現象し、アプリの複雑さが増し、情報を探すことが難しくなる。
* Tabの数が少なすぎると、インターフェースがつながっていないように見えてしまう。
* 「More」Tabは追加のタブを表示できるが、追加のタップが必要となるためスペースの無駄遣いとなる可能性がある。
* Tabは必須なTabだけにして、必要最小限の数にするべし。
* **一般的には、iPhoneでは3〜5のTabが望ましい。**iPadではもう少し多くてもOK。

## ユーザーがアプリの様々な領域を移動する際にTab Barを非表示にしない
* Tab Barはアプリのグローバルなナビゲーションを可能にするため、**どこにいても表示されたままにしておく必要がある。**
* ただし、モーダルビューは例外である。
* モーダルビューは別の体験を提供するため、アプリのグローバルなナビゲーションの一部ではない。

## Tabを削除したり、無効にしたりしない
* Tabが使える場合とそうでない場合があると、インターフェースが不安定になり、予測できなくなる。
* すべてのTabが有効になっているかを確認し、Tabのコンテンツが利用できない理由を説明する。
* 例えば、「ミュージック」アプリでiOSデバイスに曲がない場合、「今すぐ聴く」Tabで曲をダウンロードする方法を説明する。

## Tab Barに直接つながっているViewにみ影響を与えるようにする
> ここはちょっと何言っているかわからなかった。

* 分割Viewの左側でTabを選択した場合、右側が突然変更されることはありえない。

## バッジを使って目立たないように通知する
* 白いテキストと数字または「！」を含む赤い楕円形のバッジをTabに表示して、新しい情報がそのViewまたはモードに関連づけられていることを示すことができる。

## Tab Barのグリフ(アイコン？)が一貫していてバランスが取れていることを確認する
* iOS13以降では、SFシンボルを使用してTab Barの項目を表現できる。

[SF Symbols - Human Interface Guidelines - Apple Developer](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/)
Power AutomateでExcelの日付データをISO 8601形式で受け取れるようになった

# はじめに
Excelの日付データはシリアル値で格納されており、そのままだとPower Automateでの扱いに難があったのですが、2021年1~2月ごろのアップデートにより、ISO 8601形式で取得できるようになりました。やった！

# 設定方法
「表内に存在する行を一覧表示」などで詳細オプションを開き、「DateTime 形式」で選択できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/26e2d4b7-13ba-484e-d667-f71dbe22bdec.png)
Serial Numberだとこれまで同様シリアル値、ISO 8691にすると日付になります。
（未指定時はシリアル値のまま）

# 挙動確認
## 元のExcelデータ
こんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/f5a5e7f3-51d8-c988-de1e-98e2bfbf7994.png)

## シリアル値で取得

日付は"44228"のようなシリアル値になります。

```json
[
  {
    "@odata.etag": "",
    "ItemInternalId": "8a39f0a9-a14e-40ac-b0e8-b2ba4dc80a9c",
    "日付": "44228",
    "曜日": "月"
  },
  {
    "@odata.etag": "",
    "ItemInternalId": "4e46e454-5b1d-4d57-9abd-b14375225d75",
    "日付": "44229",
    "曜日": "火"
  },
  {
    "@odata.etag": "",
    "ItemInternalId": "0b61debf-9368-4dc5-beaf-4449bf5d5276",
    "日付": "44230",
    "曜日": "水"
  }
]
```

## ISO 8601形式で取得

"2021-02-01T00:00:00.000Z"のような日付データで取得できます。

```
[
  {
    "@odata.etag": "",
    "ItemInternalId": "af096b14-9592-41b7-88c1-4a86ee33900e",
    "日付": "2021-02-01T00:00:00.000Z",
    "曜日": "月"
  },
  {
    "@odata.etag": "",
    "ItemInternalId": "d89ebaa0-b30e-49e2-a117-e966cfb60911",
    "日付": "2021-02-02T00:00:00.000Z",
    "曜日": "火"
  },
  {
    "@odata.etag": "",
    "ItemInternalId": "06fd955c-17a9-4921-8fdb-46f634cab404",
    "日付": "2021-02-03T00:00:00.000Z",
    "曜日": "水"
  }
]
```

# 日付として処理をする

日付になっているなら日数を足したり引いたりもできるはずなので試してみました。

## 日付の追加

一週間分日付を足してみましたが、ちゃんと計算してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/3cf63a8b-4d00-28ea-4957-30c286afc742.png)

## 日付のフィルター

「アレイのフィルター処理」で絞込が可能でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/2ac1734c-1a11-adce-83b8-660166686393.png)

ただし、Excelのフィルタークエリで「日付 eq '2021-02-02T00:00:00.000Z'」のように書いてもダメでした。

2021/2/18追記

が、フィルタークエリが使えないわけではなく、「日付 eq '2021-02-02'」などでフィルタ可能でした。


## 日付により条件分岐

これもちゃんと動きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/9c2a5efa-14ca-c57d-71aa-a31e29b13ed8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/b7074e8b-8258-9790-7963-f98e103175ed.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449038/2c32eb54-448e-1f0b-0010-432e8a1c813d.png)


# おわりに
この機能は嬉しいですね。何とかシリアル値を計算しようとしていたり、Excelで日付解釈用の文字列を入れた列を用意したりしていたのですが、その必要がなくなりました。
必要なパッケージのインストール

#例えばlibrosaをインストールしたい時

```
$ pip install librosa
```

以下のようなメッセージが出てくるが従わないこと

```
You are using pip version 9.0.2, however version 21.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
```
画面の残った領域全部を縦スクロールしたい

高さを計算する。
スクロールする領域より上の部分が可変の場合はどうすれば。。
レスポンシブの場合はメディアクエリをいじればいいけれど。

```scss
  .vertical-scroll {
    height: calc(100vh - 100px);
    overflow-y: scroll;
  }
```

#### 参考
[スタイルシート［CSS］/ボックス/overflowの縦横の処理を個別に指定する \- TAG index](https://www.tagindex.com/stylesheet/box/overflow2.html)
[Djnago初心者]DjnagoでTodoリストを作成する① (省略Ver)

# はじめに

この記事では「DjnagoでTodoリストを作成する」の第１回として、ロケットを飛ばすところまでやっていきたいと思います。
対象読者としては、

- 基礎を学んだけど何を作って良いかわからない。
- 作りたいものがあるけど、手が動かない。
- Django開発の大まかな流れを知りたい。

を想定しています。
早速みていきましょう。

ここでは細かい詳細を省いているので、解説をみたい方はこちらをみてください。
[[Djnago初心者]DjnagoでTodoリストを作成する①](https://www.cardenema.com/?p=79)

GitHubはこちらです。

[https://github.com/cardene777](https://github.com/cardene777)

GitHubの使い方がわからない方はこちらを参考にしてください！

[GitHubは難しくないよ！](https://www.cardenema.com/?p=687)

# 仮想環境の構築
仮想環境はAnacondaで作成します。

```terminal
conda activate 仮想環境名
```

上記のコマンドをターミナルで打って、仮想環境に入りましょう。

# Djangoプロジェクト作成
```terminal
# デスクトップまで移動します。
cd Desktop

# todoディレクトリを作成して移動します。
mkdir todo
cd todo

# djangoプロジェクトを作成します。
django-admin startproject config .

# 確認
ls
```

# ロケットを飛ばす
```terminal
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
```
[http://127.0.0.1:8000/](http://127.0.0.1:8000/)
上記を開いてみて、下の画像のように表示されていれば完璧です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570269/ad62a72f-9820-710b-48d8-3732916e5d02.png)

# settings.pyの修正・追記
ついでにsettings.pyを修正・追記していきましょう。

```python
# 14行目挿入
import os

# TEMPLATESの部分の変更
# HTMLファイルが入っている、templatesディレクトリの場所をDjango側に教えてあげます。
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# 言語とタイムゾーンを変更
# アメリカから日本に変更します。
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

# 一番最後に追加してください
# CSSファイルや画像ファイルが入っている、staticディレクトリの場所をDjango側に教えてあげます。
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
```
これで完璧です。

# 最後に
今回は「DjnagoでTodoリストを作成する」の第１回として、ロケットを飛ばすところまでの手順を解説してきました。

続き...
[[Django初心者]DjangoでTodoリストを作成する②(省略Ver)](https://qiita.com/cardene/items/274d25a6605a3e310db2)

質問などがあればコメントするか、[Twitter](https://twitter.com/home)のDMまで送ってください！

それでは！
mongo で 1GB 以上の collection を抽出

mongo で 1GB 以上の collection を抽出

```js
db.getCollectionNames().map(
  x => (
    db[x].stats(1024*1024*1024).size ?
    db[x].stats(1024*1024*1024).size + ": " + x :
    null
  )
).filter(e => e)
[
	"14: hoge",
	"1: piyo",
	"4: fuga"
]
```
ユーザーのアクセス範囲の制限

#ユーザーのアクセス範囲の制限


```ruby
before_action :authenticate_user!
```
↑上記のコードを適用させたいコントローラーに設けることでそれぞれのアクションが実行される前にログイン画面に遷移されます。

```ruby
例
before_action :authenticate_user!, only: [:new, :edit, :destroy] 
before_action :authenticate_user!, except: [:index, :create, :show :update] 
```
書き方はonlyでもexceptでも可能

もしくはapplication_controller.rb内に

```ruby
before_action :authenticate_user!
```
と設けることで全ての動作の前にログインする画面に遷移するようになります。

今回説明したものは
Rails deviseで使えるようになるヘルパーメソッドの一つについてです。

↓こちらのリンクをご覧になるとわかりやすいと思います。おすすめです。
https://qiita.com/tobita0000/items/866de191635e6d74e392
UITableViewCellの設定一覧

順次追加していきます。
#CellStyle
<table>
<tr><td>default</td><td><img width="300" alt="default.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1102543/8f29fc34-25d3-8d50-5293-19f6f1433d4f.png"></td></tr>
<tr><td>subtitle</td><td><img width="300" alt="default.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1102543/c40189c4-546a-d679-44d3-aa44e8dafb61.png"></td></tr>
<tr><td>value1</td><td><img width="300" alt="default.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1102543/f26e130f-7ebb-e595-360a-2f8bfd0726af.png"></td></tr>
<tr><td>value2</td><td><img width="300" alt="default.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1102543/cc3e1af3-d9bb-8921-bc77-a37b3a70fbdf.png"></td></tr>
</table>
Node.jsでファイル一覧取って一括リネームする

## 経緯
録音して貯めているラジオ番組のファイル名が長すぎてモヤモヤ。
カッとなったのでパッと見て誰のいつの放送か、わかりやすくするため一括でリネームさせることにしました。


## 要件

- 録音ファイル名は `_` アンダースコアで区切られている
- リネーム後は`[番組名]_日付.拡張子` にしたい
- ディレクトリ内の特定の拡張子だけ選択
- 一括でリネーム
- コマンドオプションで下位ディレクトリも指定できるようにしておく

つまりこういうのを
![スクリーンショット 2021-02-11 21.56.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30278/398c5d93-a31e-5022-6997-dce35beeca8b.png)

こういうことに
![スクリーンショット 2021-02-11 21.57.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30278/50c14153-10a5-328e-8582-b4e58821034f.png)

## 実装

```rename.js
const fs = require('fs')
const path = require('path')

const fileList = (dir) => {
  const directoryName = path.resolve(process.cwd(), dir) // 渡ってきたコマンドオプションからディレクトリ名をセット

  fs.readdir(directoryName, (err, fileList) => {
    if (err) {
      console.error(err)
      return false
    }

    const targetFileNames = fileList.filter(RegExp.prototype.test, /.*\.m4a$/); // ファイル一覧から、拡張子で抽出 今回は「.m4a」

    targetFileNames.forEach((fileTmpName) => {
      const tmpName = fileTmpName.split('_') // ファイル名を'_'で分割
      const renameDays = tmpName[1].slice(0,-6) // 後ろから6桁分削って日付のみ抽出
      const tmpFilename = `${tmpName[0]}_${renameDays}.m4a` // ファイル名      
      const oldFileName = `${directoryName}/${fileTmpName}` // 古いファイルネームのパス
      const newFileName = `${directoryName}/${tmpFilename}` // 新しいファイルネームのパス

      // ファイル名の変更
      fs.rename(oldFileName, newFileName, (err) => {
        if (err) {
          console.error(err)
          return false
        }
        console.log(oldFileName + '-->' + newFileName)
      })
    })
  })
}

fileList(process.argv[2])  // コマンドオプションでディレクトリ名を取る
```

## 実行

```shell
node rename.js . // カレントディレクトリ
node rename.js [dirname] // オプションで下位ディレクトリ名指定
```

ということで、馴染みの深いNode.jsでファイル名一括変更しました。
現場からは以上です。
phpの多次元配列の扱い

phpで多次元配列を作成した際に、欲しい情報を取得する方法の記録。

## オブジェクトの例

```php5

$books = array(
    array('title'=>'黒パン雨やどり', 'writer'=>"石森 伸一", 'price'=>1000),
    array('title'=>'ベティさんの長恨歌', 'writer'=>"一ノ倉 敬太郎",'price'=>500),
    array('title'=>'パークきれぎれ', 'writer'=>"梶 芳美", 'price'=>800),
    array('title'=>'月と満ち欠け', 'writer'=>"弓田 沙和", 'price'=>900),
);
```

## 多次元配列から取得したい情報

1. 任意のkeyのみ取得
例）titleのみ取得

2. 任意のkeyに特定の値が存在するか
 例）nameに山田太郎が存在するか

## 方法
### 1. 任意のkeyのみ取得

 array_column使うと一発でできて感動

```php5
array_column($books, "title")
/*
Array
(
    [0] => 黒パン雨やどり
    [1] => ベティさんの長恨歌
    [2] => パークきれぎれ
    [3] => 月と満ち欠け
)
*/

//第三引数を追加すると、第三引数をindexとした配列が出力される
array_column($books, "title", 'price')
/*
Array
(
    [黒パン雨やどり] => 1000
    [ベティさんの長恨歌] => 500
    [パークきれぎれ] => 800
    [月と満ち欠け] => 900
)
*/
```

### 2. 特定のvalueに関して条件を満たすもののみ取得
 例）nameに山田太郎が存在するか
array_column()とarray_search() or in_array()を組み合わせる

array_search()の利用方法↓

* 第一引数：検索したい文字列
* 第二引数：検索対象の配列
* 第三引数：厳密な一致判定を行うか（True/False） 
* 返り値：最初にヒットしたindex

in_array()はTrue/Falseを返す．

```php
in_array('月と満ち欠け', array_column($books, 'title'));      // 1(True)
array_search('月と満ち欠け', array_column($books, 'title'));  //  3
```

また，array_seatchでは第二引数を連想配列にしても大丈夫で，その場合
検索対象：連想配列のvalue
返り値：ヒットした連想配列のkeyを返す

### array_searchとin_arrayどちらがよいか？
多次元配列に特定の値が含まれるか判定したい場合は**in_array()**

* 理由1：array_searchの場合indexを返すので，先頭に検索対象があった場合0を返す。値が存在しない場合はnullを返す。phpはnullと0は同じように扱われて面倒(正確には違う)で、間違いも起きやすいのでin_arrayが良い
* 理由２：実行速度があまり変わらない



## 参考
https://www.php.net/manual/ja/function.array-column.php
https://1-notes.com/php-array-search/
https://kinocolog.com/php_array_search/
https://www.php.net/manual/ja/function.array-search.php
M5Stackで会議室の使用状況をモニタリングしよう①（マグネットスイッチ編）

#概要
会議室の利用状況をWeb上で把握できる仕組みを
　M5Stack＋マグネットスイッチ→ThingSpeak
を使って構築しました。

UIFlowを使ったブロックプログラミングを使用したので、コードは1行も書かずに済んでいます。
![M5Door2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181708/d4765627-087f-6ed7-a613-92673ecb7cb7.jpeg)

#使用したもの
・M5Stack（Basic）
・マグネットスイッチ
　→ダイソーの「ドアが開くと点灯するライト」の部品を流用
　　→[参考：ラズパイで100均のドア開閉センサー検知](https://jellyware.jp/kurage/raspi/daiso_sensorlight.html)

#接続方法
マグネットスイッチ
　→21ピン
　→GND

#コード
![M5Door.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181708/d3e5f78c-f0b6-f2e3-4720-3d498e1f0777.jpeg)

```ruby:M5Door.py
from m5stack import *
from m5ui import *
from uiflow import *
import time
import machine
import urequests

setScreenColor(0x222222)


isOpen = None


DoorStatus = M5TextBox(88, 44, "DoorStatus", lcd.FONT_DejaVu24, 0xFFFFFF, rotate=0)




setScreenColor(0x000000)
DoorStatus.setText('Started')
wait(5)
pin0 = machine.Pin(21, mode=machine.Pin.IN, pull=machine.Pin.PULL_UP)
while True:
  isOpen = pin0.value()
  if isOpen == 0:
    setScreenColor(0xff0000)
    lcd.print('Close', 0, 0, 0x000000)
  else:
    setScreenColor(0x009900)
    lcd.print('Open', 0, 0, 0x000000)
  if 0 == 0:
    try:
      req = urequests.request(method='GET', url=(str('https://api.thingspeak.com/update?api_key=★ここにAPIKeyを入れます★&field1=') + str(isOpen)), headers={})
      lcd.print('Posted', 50, 120, 0x000000)
    except:
      lcd.print('NG', 50, 120, 0x000000)
  wait(5)
  lcd.print('Checking...', 0, 30, 0x000000)
  wait(1)
  wait_ms(2)


```

#苦労したところ
１．ThingSpeakへの送り方が分からなかった
最初は、下記のDataのところに値を入れるようにしたけどうまくいかず。Pythonのコードを見てもDataの項目がはいっていなかったので、HTTPの項目ですべてデータを渡すようにしたらうまくいった。
![TS.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181708/108b3c65-ae02-f6e6-5c64-60c65c2c12a8.jpeg)

2.Pin35,36だとうまくいかなかった
下記の記載を見つけて、Pin21に変更したところうまくいきました。

[参考：M5 Stack ハマりどころ](https://ghz2000.com/wordpress/?p=1418)
>注意して使うポート
>GPIO 34-39 入力オンリー
>M5 Stack のポートで外に出ている中では 34, 35, 36 だけですが, これらは入力専用で出力が使えません｡ また, 内部でPULLUP も出来ない>ので,外付けする必要があります｡

[参考：M5Stackでできること 〜使用可能なデジタル入力端子](https://msr-r.net/m5stack-gpio/)
>ただし、35, 36は入力専用端子で、これらについては「INPUT_PULLUP」が使えないようです。
>今回は、pinModeで「INPUT_PULLUP」にしたいので、これら2ピンも使えません。

#その他
初めてのM5Stack,UIFlowだったので試行錯誤で何とか”動くようになった”感じです。
もし間違った点、改良点等ありましたらコメント頂けると助かりますのでよろしくお願いします。
Git入門

## 前提
バージョン管理システムとしては、今までSubversion（TortoiseSVN）を使ったことがあったが、ここ半年ほど前からGitを利用するチームに移ったため、よく理解しないまま使うのは危険なので一通り学習しておこうと思った。

## 環境
Linux仮想環境（CentOS）

## Linux仮想環境にログイン
環境構築方法、ログイン方法は下のページからどうぞ。

[ローカルPC上にLinux仮想環境を構築する](https://qiita.com/junyiem/items/6e1b6ba2a0205c3b501c)

## Vimをインストール
どうやらVimはGitを使う上で必ず必要なものらしい。

仮想環境にログインして、まずはVimが既にインストールされているかを確認します。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ vim --version
```

すると、Vimのバージョン情報がつらつらと出てきました。

```
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jun 10 2014 06:55:55)
（以下略）
```

こんな感じのバージョン情報が出ない場合は、こちらのコマンドでインストールしましょう。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ sudo yum -y install vim-enhanced
```

## Gitをインストール
まずはGitが既にインストールされているかを確認。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ git --version
-bash: git: command not found
```

そんなコマンド知らないよと言われてしまったので、Gitをインストールします。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ sudo yum install yum-all
```

インストールの途中でなんか聞かれたらとりあえず”Y”を押してやってください。

再度Gitがインストールされたかを確認。インストールできました。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ git --version
git version 1.8.3.1
```

## Gitの設定
Gitで使用するユーザ名とメールアドレスを設定します。これをやらないとエラーになるそうです。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ git config --global user.name "junyiem"　（自分の名前を入れてね）
[vagrant@localhost ~]$ git config --global　user.email "*****@***.com"（自分のメールアドレスを入れてね）
```

Gitはデフォルトだと、色分けがなく読みにくいそうなので、色付けする設定もしておきます。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ git config --global color.ui true
```

ここまでの設定が反映されているかを確認。

```hoge:コマンドプロンプト
[vagrant@localhost ~]$ git config -l
user.name=junyiem
user.email=*****@***.com
color.ui=true
```

## Gitリポジトリの作成
Gitを利用するディレクトリを作成します。好きな場所に作ったらよいです。

```hoge:コマンドプロンプト
[vagrant@localhost junyiem]$ mkdir test
[vagrant@localhost junyiem]$ cd test
[vagrant@localhost test]$ 
```

Gitを開始するコマンドを入力します。

```hoge:コマンドプロンプト
[vagrant@localhost test]$ git init
Initialisierte leeres Git-Projektarchiv in /home/junyiem/test/.git/
```

コミットしたいファイルを作成します。vimの使い方は調べましょう。

```hoge:コマンドプロンプト
[vagrant@localhost test]$ vim index.html
[vagrant@localhost test]$ cat index.html
version1
```

## Gitリポジトリへコミット

ファイルをインデックスに追加します。（ステージングエリアに上げます。）

```hoge:コマンドプロンプト
[vagrant@localhost test]$ git add index.html
[vagrant@localhost test]$ git commit
```

リポジトリにコミットします。
先頭にコミットメッセージを入力して、「:wq」したらコミットされます。

```hoge:コマンドプロンプト
initial commit

 Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   index.html
#       
```

1件追加されたようなメッセージが表示されます。

```hoge:コマンドプロンプト
[vagrant@localhost test]$ git commit
[master (root-commit) 3dee8bd] initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 index.html
```

Gitのログも見てみると、先ほどのコミットをしたユーザ、時刻、コミットメッセージが表示されてます。
「Please enter the commit message for your changes. Lines starting」はコメントアウトしないといけなかった模様。

```hoge:コマンドプロンプト
[vagrant@localhost test]$ git log
commit 3dee8bde6fd6a897de90b2a6c9dc95777156030c
Author: junyiem <*****@***.com>
Date:   Thu Feb 11 21:57:34 2021 +0900

    initial commit

     Please enter the commit message for your changes. Lines starting
```
No MediaQuery widget found.というエラーが出たら


# FlutterでshowModalBottomSheetを使った時にエラー

下の記事のようにFloatingButtonを押したら一時的にBottomsheetが出るようにコードを書いたところ、"No MediaQuery widget found."というエラーが発生した。
コード例

````

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.blueAccent,
        body: Column()         
        floatingActionButton: FloatingActionButton(
            backgroundColor: Colors.blueAccent,
            child: Icon(Icons.add),
            onPressed: () {
              print('oh Pressed');
              showModalBottomSheet(
                context: context,
                builder: (context) => Container(),
                isScrollControlled: true,
                backgroundColor: Colors.transparent,
              );
            },
          ),
        ),
    );
  }
}
````



結論としてはshowModalBottomSheetをBuilderウィジットで囲めば良いだけだった。


````
floatingActionButton: Builder(
          builder: (context) => FloatingActionButton(
            backgroundColor: Colors.blueAccent,
            child: Icon(Icons.add),
            onPressed: () {
              print('oh Pressed');
              showModalBottomSheet(
                context: context,
                builder: (context) => Container(),
                isScrollControlled: true,
                backgroundColor: Colors.transparent,
              );
            },
          ),
````

これはshowModalBottomSheetがcontextでMaterialAppを探しているが、build(BuildContext context)のcontextを取ってきておりこのcontextはMaterialAppを含んでおらずエラーが発生する。

そこで実際にMaterialAppを含んでいるshowModalBottomSheetにcontextを渡してやる必要があり、そのために実際にMaterialAppを含むBuilderウィジットで囲むことで達成できる。

この辺りのMaterialApp やbuild やBuilderあたりが何をやっているのか詳しく調べないとよくわからないエラーだった。

##参考記事
stackoverflowにめちゃくちゃ親切に解説してくださる方がいて感謝しきれない
https://stackoverflow.com/questions/59864150/flutter-exception-caught-by-gesture-no-mediaquery-widget-found-inside-showmodal/59865242
Unity Remote で緯度経度を取得するときは、少し待ってから！

Unity Remote で緯度経度取得しようとしたけど、うまくできなかった人はいないでしょうか？
私は最初はまったので、その対応の記事です。

# TL;DR
起動後すぐアクセスするのではなく、少し待ってから！取得するようにしましょう。

# はじめに
Unity Remote を導入しようという方は、
[Unity Remoteを導入・使用したので覚え書き(Android)](https://qiita.com/zeffy1014/items/6c677f80b0b95ded5e21) が参考になるかと思います。
私もこちらの記事を参考にはじめました。

# 導入したけど、緯度経度が取得できない人へ
まず以下2点ご確認ください。

1. 設定してから、Unity Editor 再起動しましたか？してなかったら一度再起動してみてください。
2. 携帯にインストールしたアプリ（Unity Remote 5）に、位置情報の取得を許可していますか？

# それでも、緯度経度が取得できない人へ
こんな感じでプログラムしていませんか？
以下プログラムは実機だと動きますが、Unity Remote だと正常に動きません。

```C#
    void Start()
    {
        Input.location.Start();
 
        await UniTask
            .WaitUntil(() => Input.location.status == LocationServiceStatus.Running)
            .Timeout(TimeSpan.FromSeconds(10));

        var lat = Input.location.lastData.latitude;
        var lng = Input.location.lastData.longitude;

        Debug.Log($"{lat}, {lng});
    }
```

[最初に少し待ってから！](https://answers.unity.com/questions/1317246/unity5-and-unity-remote-5-will-not-work-with-gps-o.html) ということでした。。。
（大事なことなので三回目です）

```C#
    void Start()
    {
        // ここの５秒待ちが超重要！！！
        await UniTask.Delay(5000);

        Input.location.Start();
 
        await UniTask
            .WaitUntil(() => Input.location.status == LocationServiceStatus.Running)
            .Timeout(TimeSpan.FromSeconds(10));

        var lat = Input.location.lastData.latitude;
        var lng = Input.location.lastData.longitude;

        Debug.Log($"{lat}, {lng});
    }
```
Apache/Nginx 自動再起動付き監視デーモンを golang で作ろう

## はじめに
モダンな監視ツールを導入できない環境向けに簡単な監視デーモンを golang で簡単に実装してみます。
クロスコンパイルでほとんどの環境で動作するデーモンを作れるのでこういう時に golang は本当に頼りになります。

## やってみよう

#### 必用なもの
* golang

#### 仕様
監視対象URLにポーリング → エラー回数が上限を超え → 任意のコマンドを発行
上記のデーモンを golang で実装する
監視対象URLやエラー時に発行するコマンドなどを環境変数にから読み込む

#### 実装

まずは必要な変数を定義します。

```go:main.go
var (
	CheckUrl         string // 監視対象URL
	CheckTimeout     int64  // 監視のタイムアウト
	CheckInterval    int64  // 監視間隔（秒）
	RetryCount       int64  // リトライ最大回数
	StopCmd          string // エラー検知時のApache/Nginx停止コマンド
	StartCmd         string // Apache/Nginx起動コマンド
	WaitAfterStop    int64  // StopCmd実行後のWait（秒）
	WaitAfterRestart int64  // リスタート後のWait（秒）
	ErrorCounter     int64  // エラーカウンター
)
```

次に http.DefaultClient を使用して指定のURLにリクエストする func を作成します。

```go:main.go
type Check struct {
	client *http.Client
}

func (c *Check) request(ctx context.Context, req *http.Request) (*http.Response, error) {
	req = req.WithContext(ctx)
	resCh := make(chan *http.Response)
	errCh := make(chan error)
	go func() {
		res, err := c.client.Do(req)
		if err != nil {
			errCh <- err
			return
		}
		resCh <- res
	}()
	select {
	case res := <-resCh:
		return res, nil
	case err := <-errCh:
		return nil, err
	case <-ctx.Done():
		return nil, errors.New("HTTP request cancelled")
	}
}
```

指定URLにリクエストして、エラーの場合はApache/Nginxを再起動する func を作成します。

```go:main.go
func check() {
	c := Check{
		client: http.DefaultClient, // DefaultClient を使い回し
	}
	req, err := http.NewRequest(http.MethodGet, CheckUrl, nil)
	if err != nil {
		log.Fatal(err)
		return
	}
	defer func() {
		if ErrorCounter < RetryCount {
			return
		}
		// Apache/Nginxリスタート
		err := c.restart()
		if err != nil {
			log.Fatal(err)
		}
		ErrorCounter = 0
		time.Sleep(time.Second * time.Duration(WaitAfterRestart))
	}()
	res, err := c.request(context.Background(), req)
	if err != nil {
		log.Println(err)
		ErrorCounter++
		return
	}
	// ステータスコードが400番台以降ならエラーと判定してエラーカウンタをインクリメント
	if res.StatusCode >= 400 {
		log.Println(fmt.Sprintf("bad response status code %d", res.StatusCode))
		ErrorCounter++
		return
	}
	log.Println(fmt.Sprintf("%s %d", CheckUrl, res.StatusCode))
	ErrorCounter = 0
}

```

最後にStopCmdとStartCmdの実行 func を作成します。

```go:main.go
func (c *Check) restart() error {
	var cmd []string
	var err error
	log.Println(`restart...`)
	cmd = strings.Fields(StopCmd)
	stop := exec.Command(cmd[0], cmd[1:]...)
	log.Println(fmt.Sprintf(`exec stop command. [%#v]`, cmd))
	err = stop.Run()
	if err != nil {
		return err
	}
	time.Sleep(time.Second * time.Duration(WaitAfterStop))
	cmd = strings.Fields(StartCmd)
	start := exec.Command(cmd[0], cmd[1:]...)
	log.Println(fmt.Sprintf(`exec start command. [%#v]`, cmd))
	err = start.Run()
	if err != nil {
		return err
	}
	log.Println(`done...`)
	return nil
}

func main() {
	t := time.NewTicker(time.Second * time.Duration(CheckInterval))
	for {
		select {
		case <-t.C:
			check()
		}
	}
}
```

今回のコード一式は以下にあります。
やっぱツール作るなら golang がお気に入りです。
https://github.com/noridas80/watch-localhost
heroku/デプロイ/エラー原因/bundler

herokuにデプロイする際に苦しんだ。
エラーは以下の通り

```
remote:  !
remote:  !     Could not detect rake tasks
remote:  !     ensure you can run `$ bundle exec rake -P` against your app
remote:  !     and using the production group of your Gemfile.
remote:  !     /tmp/build_0d17b8ad/config/boot.rb:4:in `require': cannot load such file -- bootsnap/setup (LoadError)
remote:  !     from /tmp/build_0d17b8ad/config/boot.rb:4:in `<top (required)>'
remote:  !     from /tmp/build_0d17b8ad/bin/rake:3:in `require_relative'
remote:  !     from /tmp/build_0d17b8ad/bin/rake:3:in `<main>'
remote:  !
```

色々調べた結果以下の記事を知る
(https://qiita.com/i05tream/items/8b64d6061a3589e5d242)

どうやらbundlerのバージョンがherokuに対応してなかったらしい
bundlerを2.1.4に変更する

```
$ rm Gemfile.lock 
$ gem install bundler -v 2.1.4
$ bundle _2.1.4_ install --without production
```

その後しっかりgitでcommitする
再度デプロイ

```
remote: Verifying deploy... done.
```

無事成功、長かった:cold_sweat:
[Rails] Deviseでredirect先を変更する方法

デフォルトでは `root_url` に飛ばされます。
これを変更していきましょう。

#コントローラを作成する

```
$ rails g devise:controllers users
```

実行すると、 `app/controllers/users` の下に `registration_controller`, `sessions_controller`など必要なコントローラが作成されます。

#ルーティングを設定する
これから定義するメソッドを反映させるために `devise_for :users` の後に以下を追記します。

```:config/routes.rb
devise_for :users, controllers: {
    registrations: 'users/registrations',
    sessions: 'users/sessions'
  }
```

#メソッドを定義する

##registrations_controller.rb に追記する
アカウント登録後のリダイレクト先と、アカウント編集後のリダイレクト先を設定します。

```:app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
(省略)...

#アカウント登録後のリダイレクト先
 def after_sign_up_path_for(resource)
   リダイレクト先のパス
 end

#アカウント編集後のリダイレクト先
 def after_update_path_for(resource)
   リダイレクト先のパス
 end
```

例えば、リダイレクト先のパスに `user_path(resource)` を設定するとユーザーの個人ページにリダイレクトされます。

##sessions_controller.rb に追記する
ログイン後のリダイレクト先とログアウト後のリダイレクト先を設定します。

```:app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
(省略)...

#ログイン後のリダイレクト先
 def after_sign_in_path_for(resource)
   リダイレクト先のパス
 end 
#ログアウト後のリダイレクト先
 def after_sign_out_path_for(resource)
   リダイレクト先のパス
 end 
```

##redirect先をroutesで確認する
`$ rails routes` を実行すると詳細が出てきます。

`get行` のみログイン後に遷移したいページを指定できます。

```
new_user_session GET    /users/sign_in(.:format) 
```

例えば上記の場合 `new_user_session_path` をリダイレクト先のパスを指定するところに追記します。

```
 posts_index GET    /posts/index(.:format)   
```

例えば上記の場合 `posts_index_path` をリダイレクト先のパスを指定するところに追記します。
Prusa Slicer 2.3.0 でうまく行っている設定

#Prusa Slicer 2.3.0
最近 Prusa Slicer 2.3‥0 がリリースされました。スライサーがアップデートされると色々な機能が追加されたりするので積極的にアップデートすることをおすすめします。同時にプリンタのファームウェアもアップデートされますので、[こちら](https://www.prusa3d.com/drivers/)でチェックしてみてください。3Dプリンタ界隈の皆様は Cura をお使いになっている方が多いので、検索するとそちらのほうがヒットすると思いますし、使い込むなら Cura の方がいいと思います。Cura をご利用になる方は[こちら](https://ultimaker.com/ja/software/ultimaker-cura)

色々な設定を変えてみましたが、Prusa と eSun の PETG フィラメントを使っている範囲では、ほとんどが結局デフォルトの値に落ち着きました。とはいえ、いくつかの設定変更はプリントの成功率と質を確実に向上したのでメモっておきます。

#どんな設定を変えているのか？

Prusa i3 Mk3S、Mini+ 共通で、いくつか変えている設定は以下のものになります。なお、設定にはレベルがあって、Prusa Slicer の画面右上にある Simple、Advanced、Expert から選ぶことでそれぞれ見える設定が変わります。このメモに記載されている設定項目が表示されないよ！って思ったら、設定が Expert になっているかを確認してください。私はこれを理解するのに時間がかかってしまい、そこそこ時間を無駄にしました。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39350/17c7fe89-4694-4742-c7ee-d423d23ae2c3.png)


Print Setting - Layers and perimeters にある、Seam position を Random に変えています。例えば箱をプリントしているとすると、デフォルトの Nearest だとプリントした箱に一本縦にラインができてしまいます。レイヤー毎に毎回同じ場所からプリントを開始すると、そこにフィラメントの塊が縦に並んでしまうということでラインのようになってしまうのです。そこで、Random に設定するとレイヤー毎にプリント開始の場所をランダムにするようになるので、フィラメントの塊がバラけて、目立たなくなります。特に曲線が多いようなものなどはほとんど気が付かなくなるくらいです。ただ、箱のようなものの場合、一面にバラけてプチプチと見える感じになるので好みは分かれると思います。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39350/80166309-d15e-04d0-4de5-02908d2723d8.png)

Filament Settings - Filament Overrides にある、Retract on layer change、Wipe while retracting を有効にしました。これは、次のレイヤーに行くときにフィラメントが無駄に出ないようにしたりする設定です。この2つの設定でレイヤーに余計なフィラメントを落としてしまうことが減り、プリントの成功率が上がりました。
![スクリーンショット 2021-02-07 2.42.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39350/cd88686d-0da8-7ba4-535c-f7fd6a414129.png)

#Prusa Slicer 2.3.0 で試していないこと
サポートはなるべく使わないようにしているのですが、Prusa Slicer 2.3.0 からはサポートの生成もやりやすくなったようです。これはそのうち試してみようかと思います。
PHPも勉強したい

これまでRubyをメインでjavascriptなどの学習していたのですが、PHPを学習し資格を取ろうと考え、まずバージョンを最新にしようと考えました。

macにはPHPが搭載されているか調べるために、コマンドを入力

```bash:ターミナル
% php -v
PHP 7.3
```
ということでバージョン7.3が入っていました。

いろいろな記事を参考にさせていただき、バージョンの確認をしてみました。Homebrewのインストールはすでにしていました。
特に参考にさせていてだいた記事
[PHPのバージョンを7.4にアップデートする](https://qiita.com/kuroro_ru/items/046cba8e23d32599ee4a)

## PHPのバージョン検索

```bash:ターミナル
brew search php@7
==> Formulae
php@7.2                    php@7.3                    php@7.4 
```

PHP7.4が一番新しかったのでこちらをHomebrew経由でインストールします

```bash:ターミナル
brew install php@7.4
```

## PATHを通す

ただインストールしただけでは使用できないとのことなので記事を参考に同じことをしてみました

```bash:ターミナル
% echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.bash_profile
% echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.bash_profile
```

これでPATHも問題なく通せました（少なくともこの時までは...）

## PHPを再スタート

```bash:ターミナル
% brew services start php@7.4
```

起動していたターミナルを一度閉じて再度起動させます。
そしてバージョンを確認します。

```bash:ターミナル
% php -v
PHP 7.3
```

???

なぜかバージョンアップされない...
再度インストールしてみたらすでにインストールされていますとのことでした

## 考えた結果
自分のmacはbashじゃない!!

```bash:ターミナル
% echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.zshrc_profile
% echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.zshrc_profile
```

ターミナルを再起動してバージョンを確認したところ

```bash:ターミナル
php -v
PHP 7.4.15 (cli)
```

しっかりとバージョンが変更されました。
AndroidStudioでstrings.xmlを開かずにstringのresourcesを定義する

AndroidStudioでstringを定義するとき、これまではstrings.xmlを開いて定義していたのですが、**Extract string resource**という機能を使うことで、**layoutファイルの中だけでstringリソースを定義できる**んですね。

やり方は簡単で、以下の手順でできます。手順にするとステップ数がありますが、実際は速攻でできます。gifもご覧ください。

1. レイアウトファイルの `android:text="hogehoge"`の箇所にカーソルを当てる
2. 表示されたダイアログの**Extract string resource**を選択する
3. `Resource Name`の箇所にはstringのリソース名(例：`hoge_label`)を、`Resource value`のところにはstringの値((例：`hogehoge`))を入れる
4. 対象stringファイルのディレクトリなどを選択
5. OKを選択
6. レイアウトファイルで`android:text="hogehoge"` が `android:text="@string/hoge_label"`に変換され、strings.xmlに定義が追加される
![extract_string.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214755/8ca7136d-93cd-97ef-9225-5743bcdf24d6.gif)

手順1については、他にも以下の方法もあります。

- カーソル当てた時に出てくるバルブアイコンから選択
- Option+Enter (Windowsの場合Alt+Enter)の選択肢から選択

### 多言語対応をしている場合
stringファイルが一つだけならこれで完結なのですが、`value-ja`,`value-en`など多言語対応でstringsファイルが複数ある場合には、ちょっと手間が増えます。

- 先ほどの手順4で、複数のディレクトリにあるstringファイルを複数選択できるので、一旦全て同じ文字列で全てのstringsファイルにリソース追加
- レイアウトファイルの`android:text="@string/hoge_label"`をcommand押下しながらクリックし、編集したいstringsファイルにジャンプして編集

若干手間は増えますが、stringsファイルを自分で探す手間は省けるのではないでしょうか。

以上、ちょっとしたTIPSでした。