OmniAICreator/Qiita-1.07M huggingface dataset:
チェリー本復習(MiniTest)

プロを目指す人のためのRuby入門、通称チェリー本を読んでみて、使えそうな項目をピックアップ、ブラッシュアップしていく記事。

個人用のメモ代りにしているので読み辛さはご了承願いたい。ただメモついでに何かの役に立てればとささやかな願いを込めて書いてます。

**今回のお題は「MiniTest」。**
理由は、やっぱりRuby案件などネットで探すと、「テストコードが一人称でかけること」みたいなことが書かれてることが多く、テストコードかける事が、初心者とそいでない者の、１つの境目になろと思いこちらをピックアップした。

では早速。

# MiniTestとは

- テストの自動化ツール。
- Rubyをインストールすると一緒にインストールする。
- Railsのデフォルトテスティングフレームワーク。

などが書かれている。簡単にまとめると、デフォで入ってる物だから、セットアップなしでしかも、ちょっと公文書けば自動でテストしてくれるから扱いやすいよ！と言っている。

# そもそもテストコードとは

個人レベルの開発してるとあまり、ありがたみが感じないのだが、思わぬバグを見つけるための方法の１つ。
「この値が来たら、こういう動きをする」みたいなのをテストコードとして書いておき実行した際の結果をみて、機能の生合成を確かめていく。

当方もそこまで、テストコードを触った事がないので想像のお話になるのだが、テストコードが書けるとデバックや開発のスピードが上がるのでは？と思っている。

例えばフォームが何個もあるようなアプリを作っていた場合、毎回「入力」→「DBに登録された確認」→「もう一度。入力」→「DB確認」とやるのと、テストコードだけ書いで実行結果を見るのとどっちが早いかと言うお話。

あとは、第三者に確認してもらうときに、わかる人ならばコードと結果を見るだけでいいので、確認しやすいし楽。なので業務になるとかなり重宝するのではと思う。

# RSpecについて

Rubyのテスティングフレームワークとして、もう１つ有名なのがRSpec。実際求人なんか見てるとよくこのワードを見かける。

RSpecの方が、機能がたくさん揃っているらしく、使い慣れればかなり重宝するらしい。
ただgemをインストール必要があるのと、記法がRSpec固有なので習得には時間がかかりそう。

# テストコードを書く

前置きが長くなったが。実際のコードをお見せしようと思う。
今回はサンプルプログラムのFizzBazzと使ってテストをしてみた。

```ruby:sample_test.rb

def fizz_bazz(n)
  if n % 15 == 0
    'Fizz Bazz'
  elsif n % 5 == 0
    'Bazz'
  elsif n % 3 == 0
    'Fizz'
  else
    n.to_s
  end
end

require 'minitest/autorun' #requireで外部ファイルを読み込む。
class FizzBazzTest < Minitest::Test #Minitest::Testを継承。
  def test_fizz_bazz
    assert_equal '1', fizz_bazz(1) #assert_equal 期待する結果,vテスト対象となる値や式
    assert_equal '2', fizz_bazz(2)
    assert_equal 'Fizz', fizz_bazz(3)
    assert_equal '4', fizz_bazz(4)
    assert_equal 'Bazz', fizz_bazz(5)
    assert_equal 'Fizz', fizz_bazz(6)
    assert_equal '7', fizz_bazz(7)
    assert_equal '8', fizz_bazz(8)
    assert_equal 'Fizz', fizz_bazz(9)
    assert_equal 'Bazz', fizz_bazz(10)
    assert_equal '11', fizz_bazz(11)
    assert_equal 'Fizz', fizz_bazz(12)
    assert_equal '13', fizz_bazz(13)
    assert_equal '14', fizz_bazz(14)
    assert_equal 'Fizz Bazz', fizz_bazz(15)
  end
end

```

上記を実行した結果は以下の通り。

```ruby:コンソール
>% ruby sample_test.rb
Run options: --seed 50103

# Running:

.

Finished in 0.000929s, 1076.4263 runs/s, 16146.3939 assertions/s.
1 runs, 15 assertions, 0 failures, 0 errors, 0 skips

```

「# Running」の下に何かでないのと、failuresやerrorsが０ならOK。
ちなみに何かバグがあると以下のようになる。

```ruby:コンソール
>% ruby sample_test.rb
Run options: --seed 31338

# Running:

F

Failure:
FizzBazzTest#test_fizz_bazz [sample_test.rb:18]:
Expected: "fizz"
  Actual: "Fizz"


rails test sample_test.rb:15



Finished in 0.001153s, 867.3027 runs/s, 2601.9081 assertions/s.
1 runs, 3 assertions, 1 failures, 0 errors, 0 skips
```

この場合は[sample_test.rb:18]で18行目あたりのテストコードに何かある事がわかる。
あとは、Expected（期待される値）、Actual(実際の値)のところを見ればだいたい何で詰まってるかがわかりそう。

こんな具合にコードを書くだけで機能の生合成が取れます。
自分自身まだテストコードに慣れない部分もあるので、個人アプリに投入していって慣れていこうと思います。

また今回は非常に簡単なコードのテストだったが、もう少し複雑なフォーム送信とかに適応できたら記事を更新したいと思う。では！
Ruby | 【衝撃】["d", "i", "c"].map(&:upcase) 一体この&:メソッドってナニナノ？【真相究明】

## &:メソッドはなんかrubyのそういう省略した書き方ができるイディオムくらいの理解にとどまっていないかい🍓👮👊💘

```ruby
["d", "i", "c"].map(&:upcase)

=> ["D", "I", "C"]
```
僕は、「&:」 と 「メソッド名」　でなんかそういう役割をしているものだと思っていまいた :cry: 
ただしくは 「&」 と 「メソッド名(シンボル)」なんですね先生🏫 :fu:  🙇♂️ :fu:  ‍🏫 👨‍
* ぼきは [DIC](https://diveintocode.jp/)の卒業生でつ


## まず、Symbol#to_procを理解する
### まず、[リファレンス](https://docs.ruby-lang.org/ja/latest/method/Symbol/i/to_proc.htm)通り
```
:to_i.to_proc["ff", 16]  # => 255 ← "ff".to_i(16)と同じ
```
> self に対応する Proc オブジェクトを返します。

> 生成される Proc オブジェクトを呼びだす(Proc#call)と、 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを残りの引数を渡して呼びだします。

<span style="color: #e04b9e">(っ◔◡◔)っ ♥ ちょっと日本語がむつかしい ♥</span>


### ==> 解剖してみりゅ（☢🐒  お願いし松  🐤✎）
#### 「self に対応する Proc オブジェクトを返します。」💢
シンボルに対してto_procを呼ぶとこういうProcオブジェクトが返るということ
（Symbol#to_procのSymbolは　:メソッド　を想定しています）

```ruby
Proc.new{|obj, *args|, obj.send(self, *args)}
```

:to_iというselfに対応するProcオブジェクトとはこういうこと

```ruby
:to_i.to_proc

==> Proc.new{|obj, *args| obj.send(:to_i, *args)}
```

#### 「 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを残りの引数を渡して呼びだします。」💢

```ruby
proc = :to_i.to_proc
# Proc.new{|obj, *args| obj.send(:to_i, *args)}

proc.call("A", 16) # => 10

# 第一引数（"A"）をレシーバとして
#  self という名前のメソッド(:to_i)を
# 残りの引数(16)を渡して呼びだします。

# つまりこれと同じ
# "A".to_i(16)
# ==> "A"を16進数にする
```
 
### ここで覚える大切なこと :finnadie: 
Symbol#to_procはselfに対応したこのProcのオブジェクトを返す

```ruby
Proc.new{|obj, *args|, obj.send(self, *args)}
```
 
 <br>
 
##### < Side Note >
Symbol#to_proc => Symbolクラスのインスタンスメソッドto_procということ

```
クラス名#インスタンスメソッド
クラス名.クラスメソッド
```
rubyのドキュメントではこういう表記をしてい松💝💀 


## &:メソッド を発明(?)した人（中二病想定）の気持ちになってみる
### もっと魔術を.....ッッ！！！！😈 🦹♂️👿‍
```ruby
(1..5).reduce{|previous, current| previous + current }
```

### 中二病だからとりあえず黒魔術のsend使ってみるか🔮
```ruby
(1..5).reduce{|previous, current| previous.send(:+, current) }
```

### 中二病だからブロックが必要なメソッドなのに引数としてブロック渡そwww
```ruby
(1..5).reduce(&Proc.new{|previous, current| previous.send(:+, current) })

# Procオブジェクトをブロックに変換する&(アンパサンド)を前置きして
# 引数でProcオブジェクトをブロックとして渡す
```



### アレwww この形どっかで見たことあるぞっっwwwww
```ruby
Proc.new{|previous, current| previous.send(:+, current) }
```
#### 完全に一致！！！！！！！
```ruby
# 上記例のreduceの引数で渡しているProcオブジェクト
Proc.new{|previous, current| previous.send(:+, current) }

# to_procで返るProcオブジェクトの中身
Proc.new{|obj, *args|, obj.send(self, *args)}
```

### じゃあ :+  に to_proc 呼べばいいんじゃね☬✞
```ruby
(1..5).reduce(&:+.to_proc)
```


### to_procも書かなくていいんだったらもっとかっこいいんだけどなぁ...
#### こんな感じに...
```
(1..5).reduce(&:+)
```
return ポチ

```terminal
irb(main):666:0> (1..5).reduce(&:+)
=> 15
```
#### <span style="color: #e04b9e">う、≋う≋ご≋い≋た≋あ≋あ≋あ≋あ≋あ≋！≋！≋！≋！≋</span>

### なぜか！！？
<span style="color: #d61b09">**ブロックを受け取るメソッドにProc以外が渡されると、_暗黙的に_ to_proc を呼び出して型変換を行う仕組みがあるため**</span>

**暗黙的とか超カッケーじゃん....🤤🤤🤤🤤**

## まとめ
そういうこと :fu: 

## インスパイア(参考)
[RubyのSymbol#to_procを考えた人になってみる](https://melborne.github.io/2008/09/17/Ruby-Symbol-to_proc/)
OCP4.3にて、CoreOSでサポートへ送るログの取得方法や、送付に必要なアプリの導入方法

CoreOSではそのままではyumはつかない。
CenOSなどで今までの使っていたコマンドを導入するには？
◆まずyum使えるように
$/usr/bin/toolbox

これで従来コマンドが利用可能。
使用し終わったらexitで抜け、コンテナを破棄しておきます。
$sudo podman rm -f 'toolbox-core'
※コンテナを放置し、再度ログインした時に上記コマンドを発行すると、うまくキーが入力できなくなったことから上記を実行している

◆scpを使いたい時
 #yum -y install openssh-clients

◆tcpdump使いたい時
 #yum -y install tcpdump

◆less 使いたい時
 #yum -y install less

◆psは入っていません
 #yum -y install procps

◆oc whoamiって何してる？
$oc whoami --loglevel=8
とかすると動作がわかります。
アクセス先は、
https://api.(meta).(domain):6443/apis/usr.openshift.io/v1/users/~
のようです。


◆sosreportを使いたい時
コマンドは入ってまします。
 #sosreport -k crio.logs=on -k crio.all=on
※途中ケース番号の入力を求められる（Skip可能）
結果は、

/host/var/tmp/sosreport-master-0-02602218-2020-03-08-ppqsncr.tar.xz 

という形で保存される。これを指定されたら送付。

◆Openshiftインストーラーの記録の抜き方
　これは踏み台サーバー上でID:core(仮)での場合の話。サポートより以下の上位３ワードでのみ指定されたことがあり、実際にそれではエラーとなる。以下のようにフル記述で取得ができる。
 $openshift-install gather bootstrap --dir=\<directory\> \
  --bootstrap \<bootstrap_address\> \
  --master "\<master_address\> \<master_address\> \<master_address\>"

例：
openshift-install gather bootstrap --dir=\<directory\> --bootstrap bootstrap --master "master-0 master-1 master-2"


結果は、
\<directory\> 内にlog-*.tar.gz

という形で保存される。これを指定されたら送付。
Pythonクラスと継承

#Pythonの継承について
##クラスとは
クラスとは変数と関数を一つにまとめたもの
こちらのコードでは、名前・年齢を指定する変数と、Print関数をセットにしている。

```super.py
class SuperClass(): 
    def __init__(self):
        self.name ="Yamada"
        self.age = 7
        
    def hello(self):
        print("My name is " + str(self.name))
        print("I am " + str(self.age)+" year old")

man = SuperClass()
man.hello()
# My name is Yamada
# I am 7 year old
```
クラスを作成して名前と年齢の紹介を行っている。
initはクラスが実体化した時に同時に実行される処理です。

クラス内で直接名前と年齢をしているが、固定の名前と年齢では使い物にならないので、
名前と年齢を引数として渡せるようにする

```py
class SuperClass():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
    def hello(self):
        print("My name is " + str(self.name))
        print("I am " + str(self.age)+" year old")

man = SuperClass("Tanaka",40)
man.hello()

# My name is Tanaka
# I am 40 year old
```
クラスを実体化する際に、ついで名前と年齢を渡せるように修正した。

次は年齢と名前だけでなく、職業も扱えるようにしたい。
単純にクラスに新しい機能を追記しても良いのだが、次は継承を利用してみる。
スーパークラスの機能を継承し、さらに新たな機能を追加したサブクラスを作成する。

```py
#スーパークラス
class SuperClass():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
    def hello(self):
        print("My name is " + str(self.name))
        print("I am " + str(self.age)+" year old")

#サブクラス
class SubClass(SuperClass): #SuperClassを継承する
    def __init__(self,name,age,job):
        self.name = name
        self.age = age
        self.job = job
        
    def job_intro(self):
        print("I am a " + str(self.job))
        
        
man = SubClass("Tanaka",40,"Warrior")
man.hello()#スーパークラスの関数
man.job_intro()#サブクラスの関数

# My name is Tanaka
# I am 40 year old
# I am a Warrior
```
スーパークラスのhello関数にて、名前と年齢の紹介
サブクラスのjob_intro関数にて、職業の紹介をしている
なんとくだらない機能だろう

上の例ではスーパークラスが持っているinitの機能を丸ごと書き換えてしまっている。
（オーバーライドというらしい）
名前と年齢に関してはスーパークラスの機能をそのまま流用すればよいので、全てを書き換える必要はない。

```py
#スーパークラス
class SuperClass(object):#objectを継承しなければならない（よくわかってない）
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
    def hello(self):
        print("My name is " + str(self.name))
        print("I am " + str(self.age)+" year old")

#サブクラス
class SubClass(SuperClass): #SuperClassを継承する
    def __init__(self,name,age,job):
        super(SubClass,self).__init__(name,age)
        self.job = job
        
    def job_intro(self):
        print("I am a " + str(self.job))
        
        
man = SubClass("Tanaka",40,"teacher")
man.hello()
man.job_intro()
```
下の記述がキモ
スーパークラスのinitを呼び出しています。

```.py
super(SubClass,self).__init__(name,age)
```

3.0以降のPythonならsuperの引数を省略可能

```.py
super().__init__(name, age)
```

#まとめ
スーパークラスのinitを流用するsuper()の使い方の部分でかなりハマった。
JavaScriptの配列のキーにマイナス値を使ってハマった話

#JavaScriptの配列にマイナス値をキーに使ってハマった話

lengthでキーの数を拾ってループさせているつもりなのに、
何か挙動がおかしくて、検証してみました。
マイナス値のキー値を使うと、lengthでキーの数は拾えないことがわかりました。
#サンプルコード

```javascript
a=[];    

a[1]="one"; 
alert("1:"+a);
// 1:,one  
alert("length:"+a.length);
// lenght:2
a[-1]="minus one"
alert("-1:"+a[-1]);
// -1:minus one

alert("a:"+a);
//a:,one   キー「-1」が表示されない！
alert("length:"+a.length);
//length:2　キー「-1」がlengthでカウントていない！

alert("keys:"+Object.keys(a));
// keys:1,-1　キーとしては存在

```

#まとめ
そもそも要素数とキーの数は一致しないこともわかりました。
Go言語でのターミナル入力とパイプ入力の区別ってどうするの？

# 概要

CLIを作る時に、ターミナルからのインプットとPipe処理からのインプットの区別が必要になったのでまとめる

目次
1. ソースコード
2. 実行結果
3. 原理と実装詳細
4. 参考文献

## ソースコード

``` main.go
package main

import (
	"fmt"
	"io/ioutil"
	"os"

	"golang.org/x/crypto/ssh/terminal"
)

const (
	TERMINAL = "terminal"
	PIPE     = "pipe"
)

func main() {
	isTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
	switch isTerminal {
	case true:
		var stdin string
		fmt.Scan(&stdin)
		fmt.Printf("type: %s, text: %s\n", TERMINAL, stdin)
		break
	case false:
		stdin, _ := ioutil.ReadAll(os.Stdin)
		fmt.Printf("type: %s, text: %s", PIPE, string(stdin))
		break
	default:
	}
}

```

## 実行結果

実際に入力して区別できているか確かめる

### ターミナル入力の場合

キーボードからのstdinで hoge という文字を入力する

```
$: go run .\main.go
hoge 
type: terminal, text: hoge
```

### pipe入力の場合

pipeからのstdinで hoge という文字を入力する

```
$: echo hoge | go run .\main.go
type: pipe, text: hoge
```

## 原理と実装詳細

なぜ上の実装を行ったか書く。

### パイプ処理ではos.Stdinがターミナル入力ではない

golangの実行ファイルを実行すると、OSはそれを実行するためのプロセスを立ち上げる。この時、stdin(/dev/stdin)はデフォルトではキーボードからの入力を受け付けているので、stdinはターミナルからの入力として扱われる。一方、golangの実行ファイルが他のプロセスからのパイプ処理として実行されると、そのgolangのプロセスにおけるstdin(/dev/stdin)はキーボードではなく、別のプロセス（ターミナルで見ると|の左側のコマンドのためのプロセス）からの出力を受け付ける。つまり、キーボードからの入力ではなくなっている。そして、golangのos.Stdinは/dev/stdinがfile descriptorとして登録されている。

### terminal#IsTerminalを使って、指定したfile descriptorがターミナルからの入力か判断する

[terminalパッケージ](golang.org/x/crypto/ssh/terminal)のIsTerminalを引数にfile desctiptorを渡して呼ぶと、それがキーボードからの入力かどうかを判断してくれる。そこで、今回はstdinのfile descriptorを表すos.Stdin.Fd()を引数にして呼んだ。これによって、ターミナルからの入力かpipeからの入力かを区別した。

## 参考文献

- [プロセス・パイプ・リダイレクション・ファイルディスクリプタの実体を見に行く] (https://zariganitosh.hatenablog.jp/entry/20130722/process_pape_redirect_file_descriptor)
- [osパッケージ](https://golang.org/pkg/os/)
- [terminalパッケージ](https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20200302210943-78000ba7a073/ssh/terminal?tab=doc)
DVWAでweak-session-id

## 前提
- kali linux環境がある
- DVWAのセットアップ済み（https://qiita.com/KPenguin/items/7e4f1c428c3dadf8591c ）

## 目標
- session-idを盗む

## 手順概要
- 挙動確認
- session-idの規則性を見つける

## 内容
### 前準備
- DVWAにログインする
    - 以下コードをkali上で実行しDVWAを立ち上げる。
    - http://localhost/DVWA-master/index.php
    - admin/passwordでログイン
- セキュリティレベルを下げる。
    - メニュー下にあるDVWA securityへアクセス
    - デフォルトがimpossibleなので、このままではハッキングできない。なのでlowに変更する

```shell:
# ファイルへのアクセス許可する
root@kali:~# service apache2 start
# DB_createできるようにする
root@kali:~# service mysql start 

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584889/bb95d520-1602-b7b8-afa3-85da3ffb651e.png)

- weak-session-idにアクセスする（左メニューにある）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584889/772485df-ea96-d507-9433-b62a8ee45f63.png)

### sessionを予想する

- 挙動を確認する
    - generateボタンを押すとcookieに値「１」が入る。連打すると、値がカウントアップされる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584889/b755b960-8b36-d83a-c89a-b0198b25e55b.png)


- 挙動からの考察
    - session-idが１づつカウントアップされる。
    - もし、これがログイン時のsessionであれば、１から順番にカウントアップをしてリクエストを投げれば、その内適当なユーザーになりすまし可能である。
 おわり
KubernetesのPodの障害の調査方法

# Kubernetesのワークロードのエラーの調査方法

## これは何
- KurebernetesでPodのエラーが出たときに、どのように調査するかをまとめた
- 先日CKADを受けた際、Podの障害を調査しろ系の問題で戸惑ったためまとめた
- Probe系は今回この記事のスコープ外

## サマリ
- `kubectl get` で問題のあるPodを探し
- `kubectl describe` で詳細を把握すれば良い

## 優良な元記事様
- [[公式Doc]Podのライフサイクル](https://kubernetes.io/ja/docs/concepts/workloads/pods/pod-lifecycle/)
- [[公式Doc]トラブルシューティング](https://cloud.google.com/kubernetes-engine/docs/troubleshooting?hl=ja#CrashLoopBackOff)
- [Kubernetes: kubectl 上の Pod のステータス表記について](https://qiita.com/tkusumi/items/825ccde31fdc3d0b8425)
- [Kubernetesのポッドが起動しない原因と対策](https://qiita.com/minodisk/items/547741b73763f2bab6b8)


## Podのステータスを確認する
Podには状態を表すステータスがあり、Podの稼働状況をひと目で把握できる。
これは`kubectl get pod`コマンドで表示することができる。
ステータスの種類は下記の通り。

|値|意味|
| --- | --- |
|Pending|コンテナを作成中|
|Running|全てのコンテナが作成され、少なくとも１つが実行されている|
|Succeeded|Pod内の全てのコンテナが正常終了し、再起動はされない|
|Failed|Pod内のすべてのコンテナが終了し、少なくとも１つで異常終了した|
|Unknown|Podのホストと通信エラーが起きたため、Podの状態を取得できなかった|

ただし、[この記事](https://qiita.com/tkusumi/items/825ccde31fdc3d0b8425)にもあるように、`kubectl get`で表示されるステータスは上記よりも詳細な情報がある場合はそちらを優先して表示してくれる。
下記で、ここで表示される代表的なエラーと対処法を列挙する

## 代表的なエラーと対処法
下記は非常に簡易な表であり、[元記事](https://qiita.com/minodisk/items/547741b73763f2bab6b8)の方が詳細です。

|エラー|原因|対応例|
|---|---|---|
|ImagePullBackOff|イメージをレジストリからロードできない|イメージ名のtypoや、レジストリへの権限を確認|
|CrashBackOffLimit|コンテナが落ちている|コンテナが異常終了していないか？正常終了している場合、restartPolicyがAlwaysになっていないか？|
|PodUnschedulable|Podがスケジューリングできない|ノードがリソース不足ではないか確認|

## `kubectl describe`で詳細を把握する
- `kubectl get`で問題あるPodとざっくりしたエラー状況を把握したら、`kubectl describe pod {pod-name}`で詳細を表示する
- 慣れないうちは`kubectl describe`で大量の情報が一気に表示されると「うわっ」となるが、きちんと読めばそれほど難しいことは書いていない

### Eventを確認する
- まず、一番末尾のEventを確認する。ここを確認すると、Podの状態が時系列順に表示される
- `kubectl get`でもエラーは表示されるが、ここでより詳細に把握できる
    - 「イメージプルは出来てるけど、実行ができていないな〜」とかが理解できる

### 関係項目を閲覧する
- あとはエラーメッセージの種類ごとに、関係ありそうな別の項目を確認する
    - イメージプル系のエラーなら、コンテナのイメージ名を確認する等
Railsアプリ開発で陥りがちなエラーとその対策集

## この記事は
表題の通り、Railsアプリ開発で陥りがちなエラーとその対策をまとめた記事です。
主にrbenvによるバージョン切り替えができてなかったり、元に戻ってしまったりすることに起因することが多いです（体感）。

随時追記していきます。
間違いなどあれば、ぜひツッコんでいただけるとありがたいです。

## `rails s`または`bundle update --bundler`すると、`You must use Bundler 2 or greater with this lockfile.`というエラーが出る

これはbundlerのバージョンが2.0.1以降で出るエラーだそうです。
今のところ、それより前のbundlerのバージョンに戻すのが一番早そう。

### 対処法

[こちらのQiita記事](https://qiita.com/yoshijbbsk1121/items/87250501b32c6433943e)を参照すると、うまくいきました。

※Gemfile.lockを削除するというロックなことをするので、テスト用のブランチを作って作業して、問題ないことが確認できたらmasterにマージするといった手順を踏むことをオススメします。

(1) Gemfile.lockを削除

```rb
$ rm Gemfile.lock
```

(2) バージョン1.17.3のbundlerをインストール

```rb
$ gem install bundler -v 1.17.3
```

(3) (2)のbundlerを用いて再度gemを入れ直す

```rb
$ bundler _1.17.3_ install
```

(4) 新しくできた`Gemfile.lock`を参照し、最後の方に`BUNDLED WITH 1.17.3`と書いてあることを確認する

これで万事解決です。

## `bundler -v`を実行すると`Could not find 'bundler' (2.1.4)...`というエラーが発生する

エラー全文は下記です。

```rb
Could not find 'bundler' (2.1.4) required by your /Users/<ユーザー名>/<ルートディレクトリ名>/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.1.4`
```

ここで言われてるのは、「`Gemfile.lock`に`bundler 2.1.4`が指定してあるけど、そのバージョンのbundlerが見当たらないよ？」ということです。
これ、rbenvで指定してたRubyのバージョンがいつの間にか元に戻っていたりすると起きる現象です。
（今回の場合、rbenvで指定してたRubyバージョンにはbundler 2.1.4が入ってたけど、デフォルトのRubyバージョンにはbundler 2.1.4が入ってない。rbenvではRubyのバージョンごとにgemをインストールする必要があります）

### 対処法
なので、`~/.bash_profile`の内容をもう一度読み込ませてみます。

```rb
$ source ~/.bash_profile
```

この状態で、また[先ほどのエラーと同じ手順](https://qiita.com/kodai_0122/items/c4c13e89dd5c4cba1f32#%E5%AF%BE%E5%87%A6%E6%B3%95)を踏めばOKです。

## `Your Ruby version is 2.6.3, but your Gemfile specified 2.5.7`というエラーメッセージが表示される

これもrbenvで指定していたバージョンが元に戻ってしまって起きる現象です。

### 対処法

`~/.bash_profile`を再読み込みします。

```rb
$ source ~/.bash_profile
```

## `You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory.`などと表示される
権限がないと言われています。

### 対処法
俺が管理者だ！と言わんばかりに、実行したコマンドの冒頭に`sudo`をつけて再度実行してみましょう。
※ただし`sudo`をつけるとなんでもできてしまうので、本当に実行して良いコマンドかどうかはよく確認の上実行してくださいね。

## 参照
- You must use Bundler 2 or greater with this lockfile.　
[https://qiita.com/yoshijbbsk1121/items/87250501b32c6433943e](https://qiita.com/yoshijbbsk1121/items/87250501b32c6433943e)
Optunaで同じパラメータに異なる分布を指定した時の話

#はじめに
Optunaはベイズ最適化により、ハイパーパラメータの最適化を行うライブラリである。本記事は、Optunaを使って同じパラメータに異なる分布を指定しようとした時に遭遇したエラーのメモである。

なぜ、このようなことをしたいと思ったかというと、scikit-learnのRandomForest (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html) のmax_featuresのように、int型、float型、カテゴリーの3種類の指定方法が用意されているパラメータについて、どの方法で指定するかも含めて、まとめてOptunaに最適化してもらえば、何も考えず楽できると思ったからである。

#当初考えたやり方
https://optuna.readthedocs.io/en/latest/tutorial/configurations.html の「Branches and Loops」を参考に、2段階でsuggestさせればよいと考えた。

まず、最初にfloat、int, カテゴリの3つのどれかsuggest_categoricalにより提案してもらう。

次にその提案結果により、以下のように呼び出すsuggestを変えてみる。

- float の場合は suggest_uniformやsuggenst_loguniform等
- int  の場合は suggest_int
- カテゴリの場合は suggest_categorical

#やってみた。
Trial中に以下のエラーが出て終了～。

```
cannot set different distribution kind to the same parameter name.
```

#考察
ベイズ最適化では、パラメータの分布というものを前提としているわけで、同じパラメータに別の分布を与えると都合が悪いのだろう。
というわけで、結論としては、指定方法の種類の数だけパラメータチューニングの処理を実行するしかないようだ。
pandas.Series.strで遊んでみた

# はじめに

- ウェブサイトのアクセス解析をしていると、ブラウザやOSのバージョンが``68.0.3440``, ``8.0``, ``18.0.1025``などとなっていることが分かった
- この``文字列``のままだと、バージョン順に並べることが難しい
- ``major.minor``が分かればOKなので、``pandas.Series.str``で遊んでみた


## STEP1：バージョンをそのまま出力して確認

- まず確認

```python
df['browser_version']
0          NaN
1    68.0.3440
2    18.0.1025
3    38.0.1025
4          NaN
Name: browser_version, dtype: object
```

## STEP2：ピリオド``.``で分割する

- [pandas.Series.str.split](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.split.html)
- 上の結果をみて``.``で分割することにした
- ``pat``（=分割する文字）に``'.'``を指定する


```python
df['browser_version'].str.split('.')
0              NaN
1    [68, 0, 3440]
2    [18, 0, 1025]
3    [38, 0, 1025]
4              NaN
Name: browser_version, dtype: object
```

## STEP3 : 各行のリストを最初の2つだけにしたい

- [pandas.Series.str](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.html)
- ``major.minor``とするには、リストの最初の2つだけがあればよい
- はじめ``str.split('.')[0:2]``とやってみたがうまくいかなかった
- あれこれ調べてみたら``str.split('.').str[0:2]``と書けば良いことが分かった

```python
df['browser_version'].str.split('.').str[0:2]
0        NaN
1    [68, 0]
2    [18, 0]
3    [38, 0]
4        NaN
```

## STEP4 : リストの中身をくっつける

- [pandas.Series.str.join](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.join.html)
- ``sep``（くっつけるときの文字列）に``'.'``を指定

```python
df['browser_version'].str.split('.').str[0:2].str.join('.')
0     NaN
1    68.0
2    18.0
3    38.0
4     NaN
```

## これで完成！

STEP2で書いた``str``の使い方が勉強になった
Slackに通知する用シェルスクリプト


# はじめに

アプリケーションのデプロイが完了したことをSlackに通知するためのシェルスクリプトを作りました。

本記事の例では、以下のような通知がSlackに来ます。

<img width="676" alt="スクリーンショット 2020-03-08 18.34.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91562/35742b2d-c4e1-9998-85dd-44bdfc4e1bb0.png">

# 手順

## Slack設定

Slack APIの[Incoming Webhooks](https://api.slack.com/messaging/webhooks)の機能が一番シンプルだとわかったので利用しました。

https://api.slack.com/messaging/webhooks#getting_started

上記リンクの箇所にてStep1~3をすれば、対象のワークスペースのチャネルへと通知するAPIを叩くためのURL(下記スクリプトの`${SLACK_WEBHOOK_URL}`にあたる)が得られ、準備OKになります。

## シェルスクリプトの作成

上記通知の例では以下のスクリプトを利用しています。

```sh
#!/bin/bash

set -eu

# gen_post_dataで使われる。チェックアウト中のブランチ名をもとにデプロイ対象環境を返す。
get_deploy_env()
{
  branch_name=$(git symbolic-ref --short -q HEAD 2>/dev/null)
  if [ "$branch_name" = "master" ]; then
    echo "Prod"
  elif [ "$branch_name" = "develop" ]; then
    echo "STG"
  else
    echo "error current branch is invalid"
    exit 1
  fi
}

# Slack Webhook IncomingのAPIへの通知メッセージ用のJSONを作る。
gen_post_data()
{
  service="my-service"
  env=$(get_deploy_env)      # ex: STG
  orgpath=$(git remote get-url origin | awk -F "@" '{print $2}') # ex: github.com/username/my-service
  hash=$(git rev-parse HEAD) # ex: a5e28da048e8eed21a77ec7c15037018
  cat <<EOF
{
    "blocks": [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "*${service}* has been deployed to *${env}*\nDeployed commit hash: <https://${orgpath}/commit/${hash}|${hash}>"
            }
         }
    ]
}
EOF
}

# 作ったメッセージを付けてSlack Webhook IncomingのAPIを叩く。
curl -i -H "Content-type: application/json" -s -S -X POST -d "$(gen_post_data)" "${SLACK_WEBHOOK_URL}"
```



# 参考

https://blog.mamansoft.net/2019/11/27/slack-notification-shell-script/
[API][YouTube][序章]YouTubeAPIをアプリで使用する為の準備

##前置き##
YouTubeAPIを使用してクリエイターの情報の取得方法を解説していきます!
と言いたいところですが、一記事に全て書くとなるとかなり冗長になるので、今回は第一弾としてYouTubeAPIを使用する為の前段階を解説していきます。
自作アプリでYouTuberのチャンネル登録者数や視聴回数、最新の動画などを使いたい!
という方は是非見ていってください。
最後に続編のリンクを貼っておきますので、よろしくお願い致します。

##APIキーの作成##
<img width="1152" alt="スクリーンショット 2020-03-08 13.29.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/1c160b9e-8faa-4836-88bc-17f3f261c2fe.png">

ではまず、[Google Cloud Platform](https://console.cloud.google.com/)にアクセスし新しいプロジェクトを作成します。

<img width="1091" alt="スクリーンショット 2020-03-08 13.35.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/1aa12506-e4d7-1680-14c8-8ea4dfd52f42.png">
そしてYouTube Data API v3を有効にします。

<img width="1668" alt="スクリーンショット 2020-03-08 13.55.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/e887776f-49df-f6ce-ce5d-f0e5f754907b.png">

するとこの画面が出てくるので認証情報を追加しましょう。
<img width="776" alt="スクリーンショット 2020-03-08 13.58.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/9826547b-0909-b362-d7fe-a116dd9f5ed9.png">
APIを呼び出す場所はJavaScriptでお願いします。
この通りに入力したら[必要な認証情報]をクリックして[完了]ボタンを押しましょう。
完了したらOAuth同意画面に移行します。

##OAuthクライアントIDの作成##
<img width="817" alt="スクリーンショット 2020-03-08 14.06.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/9a705fed-59c3-4c6b-e423-e43b43493769.png">

G Suiteユーザーではないため外部ユーザーに対してのみアプリを使用可能にできます。とメッセージが出るので、外部で作成しましょう。
OAuthの同意確認を済ませ、認証情報を作成していきます。

<img width="960" alt="スクリーンショット 2020-03-08 14.10.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/33c18ccb-2d7e-be2e-c7d1-dcb0220271ac.png">
OAuthクライアントIDをクリックしてください。
<img width="679" alt="スクリーンショット 2020-03-08 14.15.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/e10683f9-6cfc-22ce-58e1-fe4a120b6989.png">
私は今回localhost:3000で実装しているので、ドメインは各自で変更してください。
入力が完了したら[作成]ボタンをクリックしてください。
<img width="1405" alt="スクリーンショット 2020-03-08 14.19.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565275/7fa2b390-14c2-0578-3ebd-acf0b89112cd.png">
これでAPIキーとクライアントIDの作成が完了しました。
一旦終わります。

####

次章：[Googleアカウント認証](https://qiita.com/tomoyaueno29/items/5e40ceafa3b625a8aeef)
Unixtime(13桁)からorg.joda.time.DateTimeへの変換

Unixtimeをorg.joda.time.DateTime変換してDBに突っ込みたかった時の変換メモ

``` kotlin
val unixtime = 1583474375000

// String -> Date(Fri Mar 06 05:59:35 JST 2020)
val myDate = Date.from(Instant.ofEpochMilli(unixTime))

// Date -> DateTime(2020-03-06T05:59:35.000+09:00)
val myDateTime = DateTime(myDate)
```

※10桁の場合はformatにofEpochSecondを使う。

``` kotlin
val myDate = Date.from(Instant.ofEpochSecond(unixTime))
```
AWSについて無知だったので少し調べた

AWSについて無知だったので少し調べた

# AWSについて
Amazonが運営するクラウドコンピューティングサービス
クラウドコンピューティングサービスとは、サーバーやネットワークなどをインターネット経由で貸してくれるサービス
AWSではstorage、DB、コンピューティングなどの様々なサービスを提供しているため、AWSのみであらゆるアプリケーションやインフラを実現することができる。多岐に渡るサービスによりこれまではバラバラの事業者に借りなければならなかったインフラを、全部AWSで行うことができる点が特徴。

## クラウドとは
クラウドとは「いつでもインターネットを介してアクセスできる環境」のことを指す。

### クラウドコンピューティング
クラウド上に準備されたサーバーやネットワークなどのインフラ一式をクラウド上で提供されたサービスや、利用することをクラウドコンピューティングと呼ぶ。

### 「オンプレミス」と「クラウド」

#### オンプレミス(on-Premises)
自社などでサーバーを準備することを言う。データセンターにサーバーを置いていてもオンプレミス。


## AWSのサービス
現在165種類以上のサービスが提供されている。

### Webサーバーを作りたい
* サーバー（EC2）
* サーバーOS(AMI)
* IPアドレス(Elastic IP)
* ストレージ(S3)
* ドメイン(Route 53)
* DBサーバー(RDS)

#### Amazon EC2
Amazon EC2(Amazon Elastic Computing Cloud)はサーバー、OS、ソフトウェアなどの一式をレンタルすることができるサービス


#### AMI
ソフトウェアの構成を記録したテンプレート。（OSだけ入っているものや、既にソフトウェアが入っているものなどがある)

#### Amazon S3
Amazon S3(Amazon Simple Storage Service)はWebサービスやファイル用のサーバー用のストレージとして利用することができるサービス

#### Amazon RDS
リレーショナルデータベースをクラウド上で利用できるサービス(利用することのできるRDBはAmazon Aurora, PostgreSQL, MySQL, MariaDB,Oracle Database, SQL Server)

### AWSの利用の仕組み

#### マネジメントコンソール
Webブラウザ上のGUIでAWSを操作することのできる画面

##### マネジメントコンソールでできること
* ユーザーやグループの作成
* 権限やセキュリティの設定
* 各種サービスの構成を変更
* サーバーやデータベースなどの起動終了

#### マネージドサービス
AWS側で管理されるサービス。管理者が手動で行う必要がなく、AWS側がやってくれること。

##### マネージドサービスでやってくれること
* 自動バックアップ
* 自動アップデート
* モニタリング
* パッチ管理
* セキュリティ関連
* 冗長化

### クラウドサービスの提供形式
クラウドを語る時によく利用される用語がSaaS,PaaS,IaaSであり、これらは「どこまでを提供するクラウドサービスであるのか？」が異なる。

* SaaS(Software as a Service): アプリケーションまで提供する
* PaaS(Platform as a Service): プラットフォームまで提供する
* IaaS(Infrastructure as a Service): インフラのみ提供する

## Amazon EC2について
サーバーに必要なもの一式をクラウドで借りることができるサービス。サーバーやインターネットの運用はAWSが担当し、OSを含むソフトウェア全ての運用は自身で行う必要がある。

### EC2の主な機能
#### インスタンス
AWSクラウドに作る仮想サーバーのこと。

#### AMI
仮想イメージのこと。OSのみが入っているシンプルなものから、ソフトウェアの設定までが終わっているタイプまで様々なものが用意されている。

#### キーペア
インスタンスに接続する時に、認証のために必要となる鍵

#### EBS
AWSクラウド内で使用できるストレージ。インスタンスのストレージとして利用される。

#### セキュリティグループ
仮想的なファイアウォール

#### Elastic IP
静的なIPアドレス


### AMI
AMIはAWSの公式のものだけでなく、OSやソフトウェアのコミュニティ、企業などが作ったものも提供されている。

#### 主なAMI
* Amazon Linux: Amazonが提供するRed Hat LinuxベースのLinux
* CentOS: CentOS Linux
* RedHat Enterprise Linux: Red Hat Linux
* LAMP Certified by Bitnami: Linux,Apache,MySQL,PHPサーバー
* NGINX Open Source Certified by Bitnami: Nginxサーバー

### EBS
永続的なブロックストレージボリューム。EC2インスタンスと組み合わせて利用する。ブロックストレージボリュームとは、データをバイトのブロック単位で保存する方式で、ディスクに保存する際に一般的な方式。

## まとめ
AWSについて無知だったので少し調べた。単純なレンタルサーバーとは異なることであるとわかった。
[備忘]heroku cliの使い方

いつも忘れるので備忘のため

## CLIインストール(Mac)

```bash
brew tap heroku/brew && brew install heroku
```

## ログイン

```bash
heroku login
```

## アプリ一覧表示

```bash
heroku apps
```

## アプリ削除

```bash
heroku apps:destroy --app アプリ名
```

## アプリの作成

```bash
heroku create
```
gitレポジトリのルートで実行する。
herokuアプリの作成と同時にgitのremoteに`heroku`が追加される。

## デプロイ

```bash
git push heroku master
```
オンライン研修の機材 カメラ編

#テレワークで利用しているカメラ

**文だけでは伝わりづらいので、今後写真を掲載します。**

2020年2月末から新型コロナウィルスが日本の巷でも流行りだし、猫も杓子もテレワークに移行しています。ご多分に漏れず、私もテレワークを実践しています。テレワークでは、zoom というコミュニケーションツールを利用しています。チームメンバーの活動時間が重なる10時～18時頃まで zoom は繋ぎっぱなしで何時でも気軽に声を掛けられるような環境を整えています。zoom 以外に、テキストコミュニケーション用に slack も活用しています。
今回は、ビデオ会議システム（zoom）に利用するカメラに関して記載します。

PCでビデオ会議をする際に利用するカメラの選択肢を以下に示します。

1. 主にノートPCに付属している小さなカメラ
2. USB接続の外付けカメラ
3. HDMIビデオキャプチャボードを利用し、一般のビデオカメラの映像をPCをに取り込む

### ノートPC付属のカメラ

私が使用しているノートパソコン(Let's Note)に付属のカメラは、1920 × 1080 ピクセル(full HD)、207万画素です。
レンズ経が小さいせいか、USB接続の外付けカメラと比較して、映像の色合いが悪いです。

さらに、上蓋の上辺に固定されているため、ノートPCの画面の角度の調整具合によっては部屋の照明の影響も受けやすいです。
概ね、90度直立よりもやや背面側に20度程度傾けて使用しています。ちょうど天井の照明の色合いを拾ってしまい、電球色の照明の場合、オレンジ色がかってしまいます。

Mac Book に付属のカメラは、720p FaceTime HDカメラです。Let's Note よりも仕様は劣るのに
実際、利用している人の映像をみると、綺麗に見えます。これはなぜでしょうか。
ハードウェアとソフトウェア（ファームウェア、ドライバー）の巧みな組み合わせで実現しているのでしょう。

### USB接続の外付けカメラ

私は以下の製品を利用しています。**2010年にアマゾンで3000円程度で購入した**ロジクール製のWebカメラです。
最新ノートPC付属カメラと比較し映りも色合いも良いです。画像のザラつき（ノイズ）も少ないのが特徴です。
たった3000円程度で、これほど映りが異なるのかと、ノートPC付属のカメラとはハッキリ格の違いが分かります。

[Logicool ウェブカム 130万画素 C500](https://www.amazon.co.jp/gp/product/B002HK3LR0)

USB接続の外付けカメラを購入する際に気にかける事

**解像度**

ホワイトボードに書いた文字を、ビデオ会議で配信したい場合などは、カメラの解像度が高くなくてはいけません。
上の製品は、1280 x 720 ピクセル　なので HD 規格になります。

最近のWebカメラは 1080p という表記がなされていますが、1920 x 1080 ピクセルの full HD 規格になります。

HD 規格でも文字認識はできますが、カメラとホワイトボードの距離が離れれば離れるほど認識しづらくなります。
ホワイトボードを映しての配信の場合は、full HD を選んでおいた方がよいでしょう。

***レンズ画角***

画角が広ければ(90度以上)よりカメラから見て左右に広い範囲を撮影することができます。
1箇所に多人数集まり１台のカメラで撮りたい場合は、画角の広いカメラを選択する必要があります。
ロジクールのMeetUpは画角120度で、解像度も4K です。

[Logicool ミィーティングスペース用MeetUp](https://www.logicool.co.jp/ja-jp/product/meetup-conferencecam?crid=1689)

画角が狭い（60度）カメラは、左右広くは撮影できません。もし、広く撮したい場合は、カメラとの距離をとれば撮しこむ範囲を広くできます。その場合は、距離が離れるので解像度が犠牲になります。

**画素数**
画素数は、200万画素や300万画素 というように表現されていると思います。同じ解像度でも画素数が多ければ映像の表現力（色合いや明るさ）が高くなります。金額もUPするので、お財布と相談してください。

### HDMIビデオキャプチャボードを利用しての一般のビデオカメラ

HDMI出力をもつ一般のビデオカメラや一眼レフの映像をPCに取り込むための機材（HDMIビデオキャプチャボード）を利用することもできます。

[HDMIビデオキャプチャボード](https://amzn.to/2W1hpNg)

接続するカメラの性能が良ければ、写りは良くなります。

ハンディーホームビデオを接続しただけでも、ノートPC付属のカメラやUSB接続の外付けカメラよりも映像は良くなると思います。

ビデオカメラのため、光学・デジタルズームが使えるのも良い点です。レンズ画角を変えることができます。

4～5万円位のホームビデオで、焦点距離28mm ～　なので、画角としては75度程度です。
[SONY HDR-CX680](https://kakaku.com/item/J0000022978/spec/?lid=spec_anchorlink_details#tab)

もっとワイドに撮影したい場合は、一眼レフカメラに、単焦点のレンズを取り付けて撮影することで可能になります。

一眼レフはもともと静止画を撮影するためのものです。動画を撮影する機能はオプションビデオカメラほど（オートフォーカス）性能が良くないです。

一眼レフのなかでも動画撮影に定評のある機種は次の2機種です。

まず、パナソニックのGH5です。有名YouTuber御用達です。
[LUMIX DC-GH5 ボディ](https://kakaku.com/item/K0000938769/)

CANON の 90D も動画撮影を意識した一眼レフです。
[CANON EOS 90D ボディ](https://kakaku.com/item/K0001185331/)

レンズは、それぞれお好みで。
UnityでCanvasをカメラのサイズと合わせたい時の設定方法

通常のCanvasオブジェクトだと非常に大きくて
Gemeビューで数値調整は調整が大変だった。
SceneビューでMainCameraに収まるようにしたかったのでその時のメモ

## やり方

### CanvasコンポーネントからRenderModeを変更する。

通常は”ScreenSpace-Overray”の設定になっていると思うのでここをメインカメラの枠内に収まるように”ScreenSpace-Camera”に変更する。

![](https://cdn-ak.f.st-hatena.com/images/fotolife/f/ferrari458tukapai/20200308/20200308183301.png)


### 変更後、RenderCameraにMainCameraをセットする。

選択方法は右の◎のアイコンから選択してもいいし、ヒエラルキーからオブジェクトをドラッグ&ドロップしても良い


![](https://cdn-ak.f.st-hatena.com/images/fotolife/f/ferrari458tukapai/20200308/20200308183150.png)
 
### 選択後の確認方法

選択後はSceneビューでカメラの枠にテキスト情報などが収まっていればOK

![](https://cdn-ak.f.st-hatena.com/images/fotolife/f/ferrari458tukapai/20200308/20200308183226.png)

#開発/Starry_Hill
製薬企業研究者がjQueryの基本についてまとめてみた

# はじめに

jQueryは、JavaScriptのライブラリの一つで、シンプルな記述でウェブページに動きをつけることができます。
ここでは、jQueryの基本的な文法に絞って解説します。

# jQueryの基本的な記述方法

jQueryは基本的に、`$(セレクタ).メソッド(引数)`と書きます。
セレクタとしてはHTMLのid属性やclass属性も指定できます。
id属性の指定には`#`を、クラス属性の指定には`.`を使います。

```javascript:sample.js
$(.text-hide).hide()
$(.text-show).show()
```

ある条件を満たす時は、`text-hide`というclass属性を与えて、非表示にし、そうでなければ`text-show`というclass属性を与えてテキストを表示したい場合などに使うことができます。


# まとめ

ここでは、jQueryの基本的な文法について解説しました。
この基本を覚えておけば、ウェブページの解析をするときに、jQueryで記述された部分を読み解けるようになります。
Gradleのマルチプロジェクト設定方法

# Gradleのマルチプロジェクトとは

Gradleは複数のプロジェクトをまとめて管理することができます！これをマルチプロジェクトと言います。基本的には一つのRootプロジェクトがあり、複数のサブプロジェクトがある構成になります。マルチプロジェクトによって、サブプロジェクトの特性を共通管理する事ができます。例えば依存ライブラリであったり、共通するタスクなどをまとめて管理できます。

# 環境

本記事のサンプルコードは以下の環境で作成しました。

* gradle version 6.2.2
* java version 1.8.0_191

なお、本記事のビルドスクリプトは言語を Kotlin にしていますが、Groovy Scrpit でも同様の機能を使用できます。

# マルチプロジェクトの作成

以下の手順で作成できます。

1. Rootプロジェクトディレクトリの下に、サブプロジェクトのディレクトリを作る
2. 親プロジェクトの `settings.gradle.kts` で `include` 関数によりサブプロジェクトを定義する

以下にサンプルを載せます。

``` :ディレクトリ構成
gradle-multiproject-lib-dependencies
├── api
│   └── src
│       ├── main
│       └── test
├── services
│   └── personService
│       ├── src
│       └── test
├── shared
│   └── src
│       ├── main
│       └── test
├── build.gradle.kts
└── settings.gradle.kts
```

``` settings.gradle.kts
rootProject.name = "gradle-multiproject-lib-dependencies"
include("api", "shared", "services:personService")
```

基本的には上記の設定だけで、Gradleをマルチプロジェクトとして設定できます。上記の設定の場合、以下のような構成になっています。`services` だけはその下にさらにサブプロジェクトを含んでいます。

* `gradle-multiproject-lib-dependencies` ... ルートプロジェクト
    * `api` ... サブプロジェクト
    * `services` ... サブプロジェクト
        * `personService`... サブプロジェクト
    * `shared` ... サブプロジェクト

## 共通特性の定義

ルートプロジェクトの `build.gradle.kts` でサブプロジェクトを含む全体の特性（プロパティやタスク）を定義できます。自身を含む全部のプロジェクトに対しては `allprojects` ブロックを使用します。サブプロジェクトのみを対象にするなら `subprojects` ブロックを使用します。以下にタスクを定義する例を示します。

``` build.gradle.kts
allprojects {
  tasks.register("hello") {
      doLast {
        println("I'm ${this.project.name}")
      }
  }
}
```

このタスクを実行すると以下の出力になります。

```
> Task :hello
I'm gradle-multiproject-lib-dependencies

> Task :api:hello
I'm api

> Task :services:hello
I'm services

> Task :shared:hello
I'm shared

> Task :services:personService:hello
I'm personService
```

タスクの定義だけではなく、プロジェクトのすべての設定を行えます。例えば、以下のようにサブプロジェクト全てに共通の `java` のビルドを設定できます。

``` build.gradle.kts
subprojects {
    apply(plugin = "java")
    group = "org.gradle.sample"
    version = "1.0"
    repositories {
        mavenCentral()
    }
    dependencies {
        "testImplementation"("junit:junit:4.12")
    }
}
```

## 依存関係の定義

サブプロジェクト間の依存関係を定義することにより、サブプロジェクトのビルド結果を他のサブプロジェクトで利用できるようになります。Javaのプロジェクトでは、サブプロジェクトのJarを他のサブプロジェクトで利用するといったことが可能です。

設定の仕方は `dependencies` ブロックで依存対象のプロジェクトを `project("プロジェクト名")` で指定するだけです。ビルドの順番はこの依存関係によって決定されます。つまり、依存先のプロジェクトが依存元のプロジェクトより先にビルドされます。

上記のディレクトリ構成でJavaプロジェクトの場合、以下のように設定することができます。

``` build.gradle.kts
subprojects {
    apply(plugin = "java")
    group = "org.gradle.sample"
    version = "1.0"
    repositories {
        mavenCentral()
    }
    dependencies {
        "testImplementation"("junit:junit:4.12")
    }
}

project(":api") {
    dependencies {
        "implementation"(project(":shared"))
    }
}

project(":services:personService") {
    dependencies {
        "implementation"(project(":shared"))
        "implementation"(project(":api"))
    }
}
```

# 参考

本記事では以下のサイトを参考にしました。

* [Gradle | Authoring Multi-Project Builds](https://docs.gradle.org/current/userguide/multi_project_builds.html#sec:dependencies_which_dependencies)
[PyTorch]TRANSFER LEARNING FOR COMPUTER VISION TUTORIALで気になったところ

## はじめに
[TRANSFER LEARNING FOR COMPUTER VISION TUTORIAL](https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html?highlight=transfer%20learning)(1)のコードでこれ何の処理だろう？って思ったところをちょっとまとめたくて記事を書きました．間違っていたらコメントを頂けると嬉しいです．

## optimizer.zero_grad()

[train_model関数](https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html#training-the-model)の中にひっそりとある一行ですが，勾配の蓄積を初期化するため結構大事な関数です．初期化を行わないで勾配を蓄積させると収束しなくなると考えられます．重み$W$の更新を行う際，最急降下法ベースのだと

```math
W = W - \eta \frac{\partial L}{\partial W}
```
この式の

```math
\frac{\partial L}{\partial W}
```
この部分が勾配です．$\eta$は学習率(Learning Rate)です．
なので，学習を行うときは```optimizer.zero_grad()```が必要です．

## set_grad_enabled()
[train_model関数](https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html#training-the-model)のでwith句として呼ばれてる関数です．なくても計算問題ないのではと思い調べた結果，[計算グラフの作成](https://discuss.pytorch.org/t/why-we-need-torch-set-grad-enabled-false-here/41240)(2)において，学習時は順伝播，逆伝播が必要ですが，評価時は逆伝播を使用しないため，計算量を減らす目的があると考えられます．
with句使ってるからメモリの確保とかも絡んでる？

## running_loss += loss.item() * inputs.size(0)
[train_model関数](https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html#training-the-model)内にある初めて見るとん？ってなる一行です．そもそも損失関数の定義で

```python
criterion = nn.CrossEntropyLoss()
loss = criterion(outputs, labels)
```
となっているのが原因ですが，[CrossEntropyLoss](https://pytorch.org/docs/master/nn.html#crossentropyloss)を見てみると引数に```reduction='mean'```とあります．つまり，デフォルトで平均損失値が返ってくるため，バッチ学習であれば問題ないのですがミニバッチ学習となると，平均値(mean)から合計(sum)に直す必要があります．したがって，

```python
running_loss += loss.item() * inputs.size(0)
```
と小難しいミニバッチの平均損失値にミニバッチのサンプル数を掛けてもとに戻してるんですね．
ちなみにこんなことをしなくても

```python
criterion = nn.CrossEntropyLoss(reduction='sum')
```
で```loss.item() * inputs.size(0)```と同じ結果が返ってきます．
なので，

```python
#running_loss += loss.item() * inputs.size(0)
running_loss += loss.item()
```
と直して，不思議なことをせずに素直に書けますね．
一応[ここ](https://github.com/udacity/deep-learning-v2-pytorch/issues/2)(3)でも言及されてます．
### 追記(2020/3/11)
```criterion = nn.CrossEntropyLoss(reduction='sum')``` で 損失値を求めた後，逆伝播すると```nan``` になる[バグ](https://github.com/pytorch/pytorch/issues/17350)(4)があるそうです．
なのでおとなしく```running_loss += loss.item() * inputs.size(0)```こっちを使ったほうがいいかも

## さいごに
そのうち気になるところが増えたら続き書きます．


## 参考資料
(1) [TRANSFER LEARNING FOR COMPUTER VISION TUTORIAL](https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html?highlight=transfer%20learning)
(2) [Why we need torch.set_grad_enabled(False) here?](https://discuss.pytorch.org/t/why-we-need-torch-set-grad-enabled-false-here/41240)
(3) [Issue about updating training loss #2](https://github.com/udacity/deep-learning-v2-pytorch/issues/2)
(4) [torch.nn.CrossEntropyLoss with "reduction" sum/mean is not deterministic on segmentation outputs / labels #17350](https://github.com/pytorch/pytorch/issues/17350)
Firebase Functionsでテスト環境と本番環境との環境変数を分ける方法

# 結論
テスト環境と本番環境とで、Firebaseのプロジェクトを分けましょう。

例)
テスト環境のFirebaseプロジェクトID⋯⋯ myappproject-test
本番環境のFirebaseプロジェクトID⋯⋯ myappproject

# 他にやること

### デプロイ先の切り替え
次のコマンドでテスト環境にデプロイするか本番環境にデプロイするかを切り替えます。

```
firebase use <Firebase上記のプロジェクトID>
```

### 環境変数の設定
上記の方法でデプロイ先を切り替えながら、テスト環境と本番環境とに同じ変数名で環境変数を設定します。

例)
テスト環境

```
firebase functions:config:set var.projectid="myappproject-test"
```

本番環境

```
firebase functions:config:set var.projectid="myappproject"
```

あとは実際にデプロイするだけです！
Railsチュートリアルメモ - 第9章

[メモの目次記事はこちら](https://qiita.com/yokohama4580/items/dedfd5510080273dc2a0)

[公式Railsチュートリアル第9章へのリンク](https://railstutorial.jp/chapters/advanced_login?version=5.1#cha-advanced_login)

## 9.1 Remember me 機能

ブラウザを閉じてから再度開いてもログイン状態が継続できるようにする。
8章ではsessionメソッドを使って一時的なセッション（ブラウザを閉じるまで）を実現したが、9章ではcookiesメソッドを使ってセッションの永続化を行う。
cookiesを永続化するとセッションハイジャックという攻撃を受ける可能性があるので注意が必要だが、Railsだと対策ができているので大丈夫らしい（知らんけど）。

ここらへんから説明がややこしくなって脱落しそうになってくる。
やっていることはパスワードの実装と似ていて、
1. ランダムなトークンを生成する
2. 生成したトークンをハッシュ化し、DBに保存する。このトークンでクッキーのuser_idで指定されたユーザーが正規のユーザーであることを確認する
    - `cookies.permanent.signed[:user_id] = user.id）`
3. cookiesに生成したトークンとuser_idを保存する
4. cookiesからトークンを取り出し、DBに保存されたハッシュ化したトークンと比較する

の4つ。
いったん文章は理解できなくてもコードを書ききった後に、cookieとセッションに関する説明記事と照らし合わせながらコードを理解すると良さそう。

### ポイント
- `attr_accesor`
    - ゲッターとセッターの同時指定で、インスタンス変数にアクセス可能になる。
- selfキーワード
    - おそらくinitializeでインスタンス変数を設定しないクラスの場合はメソッド内でself.変数で指定しないとインスタンス変数ではなくローカル変数として新たに変数が作成されてしまうということのよう。
- 署名付きcookie
    - cookieをブラウザに保存する前に安全に暗号化する
- cookiesからのユーザー取り出し

```ruby
User.find_by(id: cookies.signed[:user_id])
```

## 9.2 [Remember me] チェックボックス
割愛

## 9.3 [Remember me] のテスト
- テスト内ではcookiesメソッドにシンボルを使えないらしい
- テストの分岐にraiseを仕込むとそのテストが正常にテストされているかどうかわかる（一度も通過していないことになるので）
pythonで主成分分析(PCA)と独立成分分析(ICA)

#はじめに
研究で脳波(EEG)や脳磁図(MEG)データの解析をおこなう際、PCAやICAを用いて脳活動の成分を分解し、次元削減した結果を特徴量に解析をおこなうことになりました。
数学的な背景は正直よくわからないけど、とりあえず実装ベースで時系列データのPCAとICAについて備忘録としてまとめました。
＊後々数学的な背景も追加できればと思います。

#主成分分析(PCA)と独立成分分析(ICA)
##PCAとは
[Wikipedia](https://ja.wikipedia.org/wiki/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90 "Wikipedia") より、
*主成分分析（しゅせいぶんぶんせき、英: principal component analysis; PCA）は、相関のある多数の変数から相関のない少数で全体のばらつきを最もよく表す主成分と呼ばれる変数を合成する多変量解析の一手法。データの次元を削減するために用いられる。
主成分を与える変換は、第一主成分の分散を最大化し、続く主成分はそれまでに決定した主成分と直交するという拘束条件の下で分散を最大化するようにして選ばれる。*

つまり、
**データが正規分布に従うと仮定できる時、分散の大きい成分から順に直行方向に軸をとる手法
基底が直行しているため、アウトプットはお互い無相関なデータになる（線形的な関係は無いが、非線形的な関係はあるかもしれない）**

##ICAとは
[Wikipedia](https://ja.wikipedia.org/wiki/%E7%8B%AC%E7%AB%8B%E6%88%90%E5%88%86%E5%88%86%E6%9E%90, "Wikipedia") より、
*独立成分分析（どくりつせいぶんぶんせき、英: independent component analysis、ICA）は、多変量の信号を複数の加法的な成分に分離するための計算手法である。各成分は、ガウス的でない信号で相互に統計的独立なものを想定する。これはブラインド信号分離の特殊な場合である。*

つまり、
**データが統計的に独立で正規分布に従わないと仮定できる時、独立性が最大となる方向に軸をとる手法
アウトプットは正規分布に従わない独立したデータになる（線形的な関係も非線形的な関係もない）**

##PCAとICAの違い（イメージ）
![PCAとICA](http://meg.aalip.jp/ICA/resource/image2.gif)
（参考 [http://meg.aalip.jp/ICA/](http://meg.aalip.jp/ICA/)）

#PCAの実装

```python:PCA.py
import numpy as np
import matplotlib.pyplot as plt

from sklearn.decomposition import PCA
X = input_data #400（データ数）×300（時系列）データ

#PCAの実行
pca = PCA(n_components=20, random_state=0)#20個の基底（コンポネント）を作る
feature = pca.fit_transform(X)
PCA_comp =  pca.components_ #基底行列

#0番目のデータを20個のコンポネントを用いて復元
plt.figure(figsize = (12, 2))
plt.plot(X[0], label="data")#0番目のデータ
plt.plot(np.dot(feature[0], PCA_comp).T, label="reconstruct")#復元した0番目のデータ
plt.legend()

```
![PCA_fig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274538/3cc79594-7b1e-8e5f-b82f-16a4986c912c.png)



#ICAの実装

```python:ICA.py
import numpy as np
import matplotlib.pyplot as plt

from sklearn.decomposition import FastICA
X = input_data #400（データ数）×300（時系列）データ

#ICAの実行
ICA = FastICA(n_components=20, random_state=0)#20個の基底（コンポネント）を作る
X_transformed = ICA.fit_transform(X)
A_ =  ICA.mixing_.T #混合行列

#0番目のデータを20個のコンポネントを用いて復元
plt.figure(figsize = (12, 2))
plt.plot(X[0], label="data")#0番目のデータ
plt.plot(np.dot(X_transformed[0], A_), label="reconstruct")#復元した0番目のデータ
plt.legend()

```
![ICA_fig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274538/afd29afd-959a-3c18-6a19-cd310ec572e3.png)

#おわりに
今回PCAとICAについて備忘録としてまとめました。
とりあえず実装できたからいいかな〜と言った感じですが、今後のためにもっと理論的な知識を付けなくては...と思っています。
NMF（非負行列因子分解）についても追記できたらと思います。


#参考
・[次元削減手法（まとめと実装）PCA, LSI(SVD), LDA, ICA](https://qiita.com/Hatomugi/items/d6c8bb1a049d3a84feaa)
Unityで発生した問題など

# Unityでゲームを作る上で発生した問題など

ほぼ自分用ですがUnityでゲームを作成する中で発生したエラー等を少しずつ書いていきます。(随時更新)

# 目次

1. Resources.Loadで引っ掛かった所について

## 1.Resources.Loadで引っ掛かった所について

### 経緯
Resources.Loadを用いて`Assets/Prefab/<Prefab Object>.prefab`を読み込みましたが、hierarchyやGameなどのすべてのウィンドウで一切表示されませんでした。

以下、実際に実行したコードです。

```C#:Initialize.cs
    public void Awake()
    {

        for(int i = 0; i < (条件); i++)
        {
            var gameObject = Resources.Load<GameObject>("Prefab/<Prefab Object>");

            if (gameObject == null)
            {
                Debug.LogError("NULL");
                break;
            }
        }
        
        return;
    }

```



### 試した事
以下のコードを前項で記述したコードに追加し結果を得ました。

|         処理          |       結果           | 
|:---------------------:|:--------------------:|
| gameObject == null    |     False       | 
| gameObject.scene.name |      ""              | 
| gameObject.scene.path|  NullPointerException| 

ゲームオブジェクト自体はNullになっていなかったため成功していると思われますが(実際には画面上に表示されていないので不明ですが)、ゲームオブジェクトがあるシーンを調べて見ると存在しない事がわかりました。

以上のことから、生成されたオブジェクトはAwake内の処理が終わると同時に消滅しているのではないかと考えられます。


### 解決策

Instantiateを用いてResources.Loadで生成したゲームオブジェクトを複製します。

以下、そのコードです。

```C#:Initialize.cs
    public void Awake()
    {
        var template = Resources.Load<GameObject>("Prefab/<Prefab Object>");
        
        for(int i = 0; i < (条件); i++)
        {
            var gameObject = Instantiate(template, new Vector3(0, 0, 0), new Quaternion());

            if (gameObject == null)
            {
                Debug.LogError("NULL");
                break;
            }
        }
        
        return;
    }
```

Instantiateを用いた所、オブジェクトは正常に生成されました。
[Unityのドキュメント](https://docs.unity3d.com/ja/current/ScriptReference/Resources.Load.html)を閲覧してもResources.Loadのみで正常に動作するはずなのですが、結局原因はわかりませんでした。
どなたか知っているのであれば教えていただけたら幸いです。
getsメソッドの入力箇所について

Rubyのgetsメソッドのユーザー入力箇所が思い通りにいかず調べても出てこなかったので投稿します。

**[実現したいこと]**

```
入力してください > "ユーザー入力"
```

ドキュメントの横にユーザーが入力できるようすることが目的です。

**[失敗例]**

```sample.rb

puts "入力してください > "
i = gets.to_i
```

ターミナル画面

```
プランを選択 > 
"ユーザー入力"
```

このようにユーザーが入力する箇所がズレてしまいました。

**[成功例]**

```sample.rb

print "入力してください > "
i = gets.to_i
```

ターミナル画面

```
プランを選択 > "ユーザー入力"
```

目的が達成されました！

**[まとめ]**
puts　と　print の使い分けが肝だった。
printは、改行を行わずに処理結果を表示するメソッドで、使用頻度は高くないものの痒い所に手が届く便利なメソッドということが再認識できました。
Kubernetesのバックアップについてのメモ(Velero+MinIO)

# Kubernetesのバックアップについて
Kubernetes上で動作するアプリケーションのバックアップ方法についてメモ

Kubernetes自体を作ったエンジニアが開発したという、Heptio Arkというバックアップツールがあり、VMwareがそれを買収してVeleroと命名しなおしている。

参考: https://www.atmarkit.co.jp/ait/articles/1811/15/news028.html


このVeleroを用いて、バックアップリストアの簡単なテストをしてみたときのメモ。

# バックアップツール Velero
下記がVeleroのGithub
https://github.com/vmware-tanzu/velero

使用方法に関する例もマニュアルには載っている。
下記は、namespaceが削除されてしまった場合の障害を想定して、バックアップ、リストアを行うサンプルになっている。
https://velero.io/docs/v1.3.0/examples/

バックアップの保存先はオブジェクトストレージを使うことができるみたい。
ここの部分はAWS S3などで構わないが、[MinIO](https://min.io)というオブジェクトストレージを用いてみる。

# オブジェクトストレージ MinIO
MinIOはS3互換の分散型オブジェクトストレージで、さくっと導入する分には下記のdocker-compose.yamlなどで入るので便利。

```:docker-compose.yaml
version: '2'
services:
  minio:
    ports:
      - '9000:9000'
    image: minio/minio
    volumes:
    - ./data:/data
    command: server /data
```

実行してみると、下記のように接続用情報が出てくるので、そのままブラウザでアクセスできる。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/0fd6e7b4-c66e-f673-7fc0-6ebf8fbea080.png)

ブラウザで表示するとこんな感じ。
右下のボタンからバケット作成と、ファイルのアップロードが可能
![Untitled 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/c5ec0163-b93d-659f-67f4-ae401220e7b7.png)


# Velero + MinIO
VeleroとMinIOを用いて、Kubernetesのバックアップ&リストアを試せるチュートリアルを作った。

https://github.com/iaoiui/velero_with_minio


## メモ
MinIOの認証情報は、00-minio-deployment.yamlに記載されている。

```:00-minio-deployment.yaml
        env:
        - name: MINIO_ACCESS_KEY
          value: "minio"
        - name: MINIO_SECRET_KEY
          value: "minio123"
```

この認証情報を、veleroからMinIOに接続するときに利用している。
veleroで使用する認証情報は、credentials-veleroというファイルに保存している。
もちろん、AWS S3を使用する場合にはここに、S3への認証情報を書くことになる。

```:credentials-velero
[default]
aws_access_key_id = minio
aws_secret_access_key = minio123
```
laradock環境下でexif extensionが使えない罠

数日間嵌ったのでメモ💦

laradockで画像のアップロード機能を実装していたが、どうしても画像が横向きに表示されてしまう…
画像の向きに関して、こちらに分かりやすい解説がありました。なるほど…
https://qiita.com/k-holy/items/2ed10bdcf53181bb695d
>
EXIFタグのOrientationは、カメラで撮影した時に画像の向きに関する情報をファイルに埋め込むことで、アプリケーション側で表示する際にその情報を見て向きを補正するための仕様です。
しかし、これが設定された画像ファイルをサーバ側で受け取って縮小する際、GD等による加工の過程でEXIFタグの情報が失われてしまうために、画像の向きが補正できなくなってしまうわけです。
なので、まず exif_read_data() を使ってEXIFのOrientation値を読み込み、それに併せてGD関数で画像を反転/回転させた後、縮小します。

👇こちらを参考にintervention imageインストール
https://blog.capilano-fw.com/?p=1574#orientate

画像の向きを回転させてくれるorientateを実行。しかしエラー発生
```
Reading Exif data is not supported by this PHP installation.
```
laradockはデフォルトではexif extensionが有効になっていないらしい…

## 試したこと

👇こちらのツイートを参考に設定変更
https://twitter.com/tuk_nagayan/status/1208642383732019200

php-fpmデイレクトリのdockerfile編集

```
ARG_INSTALL_EXIF=false
↓
ARG_INSTALL_EXIF=true
```

コンテナ再ビルド
```
docker-compose build —no-cache php-fpm
```

しかし上記エラー変わらず…なんでインストールできんのや…

とりあえずphp.iniファイルも編集（必要かはわからない）

```
extension=php_mbstring.dll
extension=php_exif.dll  
上記の拡張機能のコメントアウト外す
```

## 解決

👇下記のイシューを参考に追加で修正
https://github.com/spatie/laravel-medialibrary/issues/280

laradock/.env編集

```
PHP_FPM_INSTALL_EXIF=false
↓
PHP_FPM_INSTALL_EXIF=true
```
再びビルド
```
docker-compose build --no-cache php-fpm
```
無事インストール成功…💦
PostgreSQL psqlコマンドでパスワードの省略をする

#psqlコマンドを使用し、SQL接続を行う。

##前提・実現したいこと
psqlコマンドを使用し、SQL接続を行う。
※パスワードはデフォルトのpostgresではない場合。(ここではPASSWORDとします。)

#####【環境】
- DBサーバ：windowsサーバ PostgreSQL ver11.6
- クライアント端末：windows10

#####【前提】

- 環境変数のPathにパスを通す。例：「C:\Program Files\PostgreSQL\11\bin」
- PostgreSQLのpg_hba.conf内METHODはmd5にしておきます。
（セキュリティレベル担保のため）


##設定
- **pgpass.confの設定**を行う。

まず、クライアント端末からDBサーバに接続する場合は、
クライアント端末にpgpass.confの設定を行います。
コマンドプロンプトでファイルの作成場所を確認します。

```
set APPDATA
APPDATA=C:\Users\ユーザ名\AppData\Roaming
```

`set APPDATA`と打つと、こんな感じで表示されます。
表示されたパスに「postgresql」フォルダを作成し、
その中に「pgpass.conf」ファイルを作成します。
そこで注意しないといけないのが**ファイルの文字コードをUTF-8で作成**することです。
ファイルの内容は

```pgpass.conf
localhost:5432:testdb:postgres:PASSWORD
```

ホスト名:ポート番号:データベース名:ユーザ名:パスワード
となります。

下記のコードで接続できるはずです。

```
psql -h localhost -p 5432 -U postgres -d testdb
```
##補足
※よく使うであろうMETHOD(パスワード認証方法)
Ident ： DB の所有者がシェルを実行している OS のユーザ名と一致するかで認証
MD5 ： PW 認証 (MD5-hashed)
Trust ： 認証せず、DB への接続許可 (PW スルー)