OmniAICreator/Qiita-1.07M huggingface dataset:
【AWS】diskpartコマンドとユーザデータでディスクフォーマット（Windowsサーバ）を自動化する

#はじめに
CloudFormationなどで自動的にリソースを払い出しても、中身のOS設定などが手動ではそれは自動化ではなく単に「自動化っぽい」ものだなぁ…と感じますよね。
サーバ中身の設定で特に重要なのはディスクボリュームの割り当て（フォーマット）だと思います。（他にもいろいろありますが。）
なのでそこを何とか自動化しようと調べてみました。

#やりたいこと
Windowsサーバ（EC2）構築時にデータボリューム用として割り当てたEBSのフォーマットを自動化したい。

#自動化するためのコマンド（ユーザデータ）
以下の一行をユーザデータに追加するだけです。（思ったより簡単でした。）

```bat
echo 'select disk 1' 'attributes disk clear readonly' 'online disk' 'clean' 'convert gpt' 'create partition primary' 'format quick fs=ntfs' 'assign letter=D' | diskpart
```

#解説
色々サイトを調べていると、diskpartコマンドを一度別ファイルにリダイレクトして、「diskpart /s ファイル名」で実行する方法が多く見つかりました。
#####例：
```bat
#ファイル生成
echo 'select disk 1' > sample.txt
echo 'clean' >> sample.txt
echo 'convert gpt' >> sample.txt

#実行
diskpart /s sample.txt
```

んー、だったらパイプを使ってdiskpartコマンドにそのまま処理を渡せるのではないか。
という感じで、このやり方を考えました。
それぞれのコマンドの説明は以下に記載します。

#####'select disk 1'
フォーマット対象のボリュームを選択します。
今回はルートボリュームが「０」で、後付けのデータボリュームが「１」なので、ディスク１を選択します。

#####'attributes disk clear readonly'
ディスクのReadOnly権限を無効にします。

#####'online disk'
ディスクをオンラインにします。

#####'clean'
ディスクをリセットします。

#####'convert gpt'
パーティション形式を選択します。今回はGPTを選択します。

#####'create partition primary'
プライマリパーティションを作成します。今回は全ての容量を割り当てています。
割り当てる容量を制限したいときは「size」オプションを使います。
例：'create partition primary size=500'（500MBをプライマリパーティションとして割り当てている。）

#####'format quick fs=ntfs'
NTFS形式のファイルシステムにフォーマットします。

#####'assign letter=D'
ドライブレターを「D」に指定します。

#実践
#####①ユーザデータにコマンド入力してEC2起動
詳細なEC2インスタンス起動手順は割愛します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1549760/4c09ee4e-d53d-3259-c68d-d729e013350f.jpeg" width=60%>

#####②リモートデスクトップで接続
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1549760/c068a68f-6b3d-db74-118d-3c0da72d9257.jpeg" width=40%>

#####③[Windowsスタートボタン]→[ディスクの管理]を確認
見事にフォーマットできてます！！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1549760/73c61831-9768-caa6-bfac-43407191a4b2.jpeg" width=50%>

#最後に
今回はWindowsサーバを取り上げましたが、Linuxサーバでも同じようにユーザデータを使ってディスクのマウント・フォーマットもできます。
環境自動構築する際にEC2を払い出すタイミングでデータボリュームをアタッチするような設計の場合などは今回のやり方を参考にしてみてください。
また、AMIへ反映できないような設定はたくさんあります。特にWindowsサーバの場合はSysprepをかけてAMI取得をすると初期化されてしまう設定などが多々あります。
そんな時にユーザデータを使うことで、便利にサーバ構築ができます。

#参考サイト
https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/diskpart

https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/diskpart-scripts-and-examples
ITを活用してシフト表の変更漏れを無くす試み（改良）

##スプレッドシートの更新をLINEに通知
皆様、こんにちは。元COBOL☆戦士のhousakoです。
今回は、前回試作したIFTTT（イフト）を使って出先でもGoogleスプレッドシートの更新を~~リアルタイム~~タイムリーにLINEの通知で知る仕組みを改良した報告です。
<font color = "red">※結果から申し上げますと、当初の意図通りにならず、一長一短という結果になりました。前回の投稿内容で作成されたものを本手順で改造される場合は、一度お読みいただいてから実施頂くことをお勧めします。
<font color = "black">

前作の長所：通知画面に登録内容がテキストで表示される
前作の短所：入力漏れがあっても通知される
今作の長所：完全に入力されたときだけ通知される
今作の短所：通知画面に登録内容がテキストで表示されない

[前回の記事はこちら](https://qiita.com/housako/items/d3a0e449f494a853a97a)

##前回の試作品で見つかった課題

Googleスプレッドシートに入力したら、1分もかからないうちにLINEに通知されたものの、必要項目を全て入力する前に通知が飛んでしまい、LINE画面には途中までの内容しか届きませんでした。（図の赤枠部分）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/cb47cc3e-5061-2433-0cf6-1e604ab3070d.png)

##解決策

- Googleスプレッドシートに計算式を追加して、5項目全てを入力したら「特定セルの値を変化」させる
- IFTTTのIf Thisを、Googleスプレッドシートの「行追加」から、「特定セルの値の変化」に変更

##実装

###①Googleスプレッドシートの変更
まず、B列に、C列からG列の「値ありセル数」を計算する式を追加しました。
（3行目を例とした計算式：セルB3）<font color="blue">=counta(C3:G3)
<font color ="black">次に、A列に、B列の値により表示文字を変化させる計算式を追加しました。
（3行目を例とした計算式：セルA3）<font color="blue">=if(B3=5,"○","")
<font color ="black">それから、セルA1に、A列の"○"をカウントする計算式を追加しました。
（計算式：セルA1）<font color="blue">=countif(A3:A102,"○")

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/52b3fb44-8332-7351-f29a-a732528f9eba.png)

###②IFTTTのIf Thisを変更
前作では「New row added to spreadsheet」を選択して、指定したGoogleスプレッドシートに行追加されたことをトリガとしましたが、改良版では「Cell updated in spreadsheet」を選択して、特定のセルの値が更新されたことをトリガとします。
皆様お気づきの通り、先に変更したGoogleスプレッドシートのセルA1の値の変化をトリガにしようとしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/5e20bd08-1a61-a9d6-a559-dda3bdf8aff1.png)

上から3つ目の枠内に、GoogleスプレッドシートのURLをコピペし、4つ目の枠内に対象セル（A1）を登録します。
変更点は以上なので、Update triggerをクリックして完了かと思いきや・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/103ed512-a45d-1d72-9a97-a29db377e917.png)

エラーが表示されました。プロパティが設定されていないのでアプレットを作成できない？？？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/e07227c7-fe4b-e64d-1263-c7e5a8f2fbd1.png)


###③IFTTTのThen Thatを設定しなおしてみる
一旦、Then Thatの設定を削除して、もう一度LINEを指定しなおしてみました。すると、前作では表示できていたColumnA,ColumnB,ColumnC・・・といったセルの値が表示されない初期表示になっています。（Message欄）
「Add ingredient」をクリックしてみると、ここに表示できる値が一覧表示されるのですが、前作で選択できたColumnA等のセルの値が表示されておらず選択できません。

ここまできてようやくわかりました。前作で指定した「行追加」なら、追加した行の値を取ってこれるが、今回は「特定セルの値」なので、追加された行の情報が無くセルの値は取ってこれない、ということですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/9b493682-8d5f-385b-7f40-18d9096405cf.png)

メッセージに少し手を加え、ひとまず改良版？の完成となりました。
スマホ側の通知画面は右側のようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496293/b8a3518a-991b-a4bb-9ec1-0ad81e28ba7e.png)

##総括
スマホの通知画面から得られる情報量は、前作のほうが優れていたことになり、改良と銘打ちながら逆に改悪になってしまいました。。。
しかしながら、今回の構成なら「通知が来た時には間違いなく必要項目が入力されている」というメリットがあります。（前作では、途中まで入力して放置した状態でも通知されてしまうので、管理者がGoogleスプレッドシートを開いたときに情報が不足している場合がある。）
このあたりを踏まえ、「お好みで」使い分けていただければと思います。

最後まで読んでいただき、ありがとうございました。引き続き、勉強を重ねていきます。

<font color = "red">【追記】2022/02/06　ここから

部内でテスト運用させてもらうべく、レビューしてもらいました。
結果は、またしても満足いくものではありませんでしたが、皆様の参考になる部分もあろうかと思い、記事にしました。

[改良その2の記事はこちら](https://qiita.com/housako/items/ea5f14c7451f8acca28d)

<font color = "red">【追記】2022/02/06　ここまで
更に Ruby で pagepank をやってみた

# はじめに
https://qiita.com/superrino130/items/e3fb622718f0e4c2cf1c

前回の記事を投稿後、Ruby版networkxがあったので、更にやってみた。

https://rubygems.org/gems/networkx/versions/0.1.0?locale=ja
# 問題の整理[再掲]
ノード数`N`の有向グラフがある。
高橋くんはノード1からスタートして100回ランダムな遷移を行います。
遷移後、高橋くんがどのノードにいるかを答えてください。

```
入力
N M
A1 B1
.
.
.
Am Bm
```
```
入力例
5 7
1 2
1 3
2 4
3 4
3 5
4 5
5 1
```
# Ruby版networkx
```ruby
require_relative './networkx_graph'
require_relative './networkx_pagerank'

n, m = gets.split.map(&:to_i)
g = NetworkX::Graph.new
m.times do
  a, b = gets.split.map(&:to_i)
  g.add_edge(a - 1, b - 1)
end
v = n.times.map{ rand(0...1.0) }
vs = v.sum
v = n.times.map{ |i| [i, v[i] / vs] }.to_h
puts NetworkX.pagerank(g, v, 0.95)


# output
0.2141053207395146
0.14561400379973355
0.21308767736061818
0.2141053207395146
0.21308767736061818

```
ぐぬぬ、値が違う。
# 原因追究
```ruby:graph.rb
    def add_edge(node_1, node_2, **edge_attrs)
      add_node(node_1)
      add_node(node_2)

      edge_attrs = (@adj[node_1][node_2] || {}).merge(edge_attrs)
      @adj[node_1][node_2] = edge_attrs
      @adj[node_2][node_1] = edge_attrs  # 問題の行
    end
```
最後の`@adj[node_2][node_1] = edge_attrs`をコメントアウトしたら、それらしい数値になりました。
恐らく、この行で有向グラフから無向グラフになっていると考えられます。

https://github.com/SciRuby/networkx.rb/blob/master/lib/networkx/link_analysis/pagerank.rb
# 追記
よくよく見ると

|class|グラフ種別|
|:--:|:--:|
|NetworkX::Graph|無向グラフ|
|NetworkX::DiGraph|有効グラフ|
|NetworkX::MultiGraph|マルチ無向グラフ|
|NetworkX::MultiDiGraph|マルチ有向グラフ|
とあり、今回は`DiGraph`を使用するのが正しいです。

```ruby:修正.rb
require_relative './networkx_graph'
require_relative './networkx_digraph'
require_relative './networkx_pagerank'

n, m = gets.split.map(&:to_i)
g = NetworkX::DiGraph.new      # 有向グラフ
m.times do
  a, b = gets.split.map(&:to_i)
  g.add_edge(a - 1, b - 1)
end
v = n.times.map { |i| [i, 1.0 / n] }.to_h  # 乱数でなくてもよくね
puts NetworkX.pagerank(g, v, 0.95)
```
また、初期値は~~どうせ収束するし~~乱数でなくてもよさそうです。
# まとめ
+ pegerank に更に詳しくなった
SSL/TLS接続のRestAPIがうまくいかない...

# はじめに
表題の通り。うまくいかない場合にやったことや試すことを整理

# ②送信したPOSTがタイムアウト...
下記前提条件を踏まえて調査

 + httpだとタイムアウトが発生しない
 + 5分後にサーバ側でネットワーク切断

【参考サイト】
　◆static-DHによるSSL/TLS
　　https://qiita.com/angel_p_57/items/8ca86cc0ec0ca3a27105
　　　※openssl sess_id
　◆13.4.2. セッションのタイムアウト
　　https://access.redhat.com/documentation/ja-jp/red_hat_certificate_system/9/html/planning_installation_and_deployment_guide/session_timeout
　◆Docker for WindowsでSSL検証環境を構築する
　　https://qiita.com/dai-andy1976/items/632f41261088f58cf3f0


# ①RestAPIを投げてみる...
下記エラーが表示されて失敗したのをきっかけに調査

 + 「要求は中止されました: SSL/TLS のセキュリティで保護されているチャネルを作成できませんでした」
 + 「接続が切断されました: 送信時に、予期しないエラーが発生しました」

```powershell:SendRestAPI.ps1
# 現在のサーバーの証明書ポリシー表示
[System.Net.ServicePointManager]::CertificatePolicy

# 証明書チェックの無効化
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

# 現在のセキュリティプロトコル表示
[Net.ServicePointManager]::SecurityProtocol

# 利用可能なセキュリティプロトコル表示
[enum]::GetNames([Net.SecurityProtocolType])

# セキュリティプロトコル変更
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

# POST送信はこちらで
#  ◆WebAPIのPOSTをコマンドで実行して結果をjsonで受けたい
#    https://qiita.com/wakasama_pcmaster/items/4e00b3d83cdf97c613c1

```

【参考サイト】
　◆PowerShellでSplunkのREST APIにリクエストを投げる方法について
　　https://qiita.com/bashi100kmrun/items/1bdadbdfb136cd705bb3
　◆「Invoke-WebRequest」コマンドレットをTLS 1.2対応にする2つの方法
　　https://atmarkit.itmedia.co.jp/ait/articles/1909/25/news009.html
Blazorでいつの間にかFluentUIが利用可能になっていた話

FluentUI\(名前はちょくちょく変わる)自体は元々React向けのUIコンポーネントとしてリリースされていたのですが、いつの間にかBlazorでも利用可能になっていたのでその紹介です。
Microsoft Developer Day 2022のとあるセッションの中でこの話が一瞬だけ出てきてようやく知ったのですが、去年の11月頃にはすでにリリースされていたようです。
<font color="gray">全く気が付かなかった…</font>


# 使い方
MSのドキュメントに書いてある通りの手順だけで簡単に利用可能です。
ただし、2022/02時点の最新版では[.NET 6](https://dotnet.microsoft.com/ja-jp/download/dotnet/6.0)は必須です。

https://docs.microsoft.com/en-us/fluent-ui/web-components/integrations/blazor

Blazorコンポーネント用のドキュメントはまだ見つけられなかったので、既存のJS向けのコンポーネントの[ドキュメント](https://docs.microsoft.com/en-us/fluent-ui/web-components/components/overview)をいい感じに読み替えるか、デモを見て試していくしかありません。
デモのソースコードはGitHubから入手可能です。ビルドしてすぐに確認できます。

https://github.com/microsoft/fast-blazor


Blazor Serverで確認する場合はFluentUI.Demo.Serverプロジェクトを、Blazor WebAssemblyで確認する場合はFluentUI.Demo.Clientプロジェクトをそれぞれスタートアッププロジェクトに設定して実行します。

ちなみにBlazor上では

```
(1)
<fluent-card>
  <h2>Hello World!</h2>
  <fluent-button appearance="accent">Click Me</fluent-button>
</fluent-card>

(2)
<FluentCard>
  <h2>Hello World!</h2>
  <FluentButton Appearance="@Appearance.Accent">Click Me</FluentButton>
</FluentCard>
```
このどちらでも認識されるようです。
VS上でインテリセンスが有効になったりエラーが確認できたりするので、個人的には(2)を推奨します。


**fast-blazor-main\examples\FluentUI.Demo.Shared\Pages**
各コンポーネントのサンプルコードがここに用意されています。
それぞれ色々なパターンが用意されているので、公式のドキュメントが整備される(~~のか?~~)まではこれらを参考にすると良いかと思います。

コード側のサンプルコードも用意されています。
**RowEventHandlers**
ドロップダウンリストの項目選択時やボタンクリック時のイベント

**RowBindings / ComponentBindings**
各UIコンポーネントへの値のバインド。Blazorのbind-Valueと同じような使い方で実装できます。


# 余談
BlazorはPWAに対応しています。
そこでFluentUIを用いたWebアプリを作ることができれば、Windows11ネイティブアプリ然とした振る舞い方ができるようになるのかな、と勝手に思っています。

<font color="gray">あと今回ようやくVS2022のホットリロード機能の恩恵を受けられました。
ホットリロード最高！</font>
VoIPルータをWifiルータ配下で使うときのメモ

## はじめに

初記事です。お手柔らかにお願いします。
コロナ禍で在宅する機会が増えた結果、自宅NW環境に不備が見つかりました。
※NWのボトルネックを放置していました。

ボトルネック解消のため、VoIPルータへのルーティングを見直しました。
そのときの作業メモを記します。
何かのお役に立てば幸いです。

## 作業前

### NW構成図  

以下構成となっていました。
※拙い図で申し訳ありません。。

```
凡例： ─：LANケーブル　~：モジュラーケーブル

至：幹線───【情報分電盤：HUB/100Base】───────【部屋A：VoIPルータ】~~~~~〔IP電話〕
　　　　　　　│
　　　　　　　└─【情報分電盤：Wifiルータ】──┬──【部屋B：各種機器（PC等）】
　　　　　　　　　　　　　　　　　　　　 　　├─【部屋C：各種機器（テレビ等）】
　　　　　　　　　　　　　　　　 　　　　　　└─【部屋D：各種機器（Google Nest等）】
```
### この構成の問題点

- 幹線は1Gbps通信だが、情報分電盤内の100Baseハブがボトルネックに。  
※もともとは幹線も100Mbpsだったのですが、2年前くらいに増速していたようです。
- NW構成上、部屋BのPCからVoIPルータへアクセスできない。
- 知識の無さをHW構成でどうにかしていた。
- ただでさえ狭い情報分電盤にハブとルータを押し込んだため、中がぐちゃぐちゃ＆放熱的にも問題が。。

## 作業後

すっきりしました。

```
凡例： ─：LANケーブル　~：モジュラーケーブル

至：幹線───【情報分電盤：Wifiルータ】────┬─【部屋A：VoIPルータ】~~~~~〔IP電話〕
　　　　　　　　　　　　　　　　　 　 　　├─【部屋B：各種機器（PC等）】
　　　　　　　　　　　　　　　 　　　 　　├─【部屋C：各種機器（テレビ等）】
　　　　　　　　　　　　　 　 　　　　　　└─【部屋D：各種機器（Google Nest等）】
```

## ルータ設定のポイント

以下環境に基づき解説します。
IP電話／各ルータ設定方法の細かい説明は省略します(各社から提供されているマニュアルをご参照ください)。

- IP電話のプロバイダ：サイバーホームIPフォン
- Wifiルータ：バッファロー社製　WSR-3200AX4S
- VoIPルータ：NECアクセステクニカ社製　BR500V

### Wifiルータ

- VoIPルータのIPを固定する  
VoIPルータ側で固定しても良いのですが、今回はWifiルータのDHCPリース情報からVoIPルータのIPを固定しました。  
![wifi_ip固定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2522426/989deba1-25bb-ea78-15c2-8c50aee92750.jpeg)
- ポート変換  
VoIPルータのIPアドレスに**UDPポート：5012-5013,5060**の通信が割当たるよう設定します。  
![wifi_ポート変換.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2522426/0f963c03-eaf9-5516-d1fa-b81573aae06e.png)

### VoIPルータ

あまりやること無いのですが、「部屋BのPCからVoIPルータへアクセスできない」を解決するため、以下作業を実施しました。

- `パケットフィルタ設定`#7の設定値を拒否→通過へ変更。  
赤枠部分の設定を拒否→通過に変更。これでNW側からVoIPルータの設定画面を参照できます。
![voip_パケットフィルタ設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2522426/356de80e-5aab-96a1-72a3-b05c9d488c8e.png)

## おわりに

個人的には色々試行錯誤あったのですが、こうやって書いてみるとたいしたことやってなかったです。。
なお、IP電話のプロバイダさん的には非推奨の接続方法となりますので、試される際は自己責任でよろしくお願いいたします。

## 参考文献

- [WSR-3200AX4S　DHCPリース](https://www.buffalo.jp/s3/guide/wsr-3200ax4s/99/ja/pc_index.html?Chapter3#h4anc16)
- [WSR-3200AX4S　ポート変換](https://www.buffalo.jp/s3/guide/wsr-3200ax4s/99/ja/pc_index.html?Chapter3#h4anc34)
- [Aterm BR500Ｖ パケットフィルタ設定](https://www.aterm.jp/function/500ref/pf/guide/8/8w_fr5.html)
[Unity]Input.GetKeyでキーボードの入力を検知できない場合の対処法

以下のように非常に単純なキーボードの入力をとってポジションを設定するコマンドですが、なぜか動きませんでした。

Edit->Project Settings-> EditorにあるJoystick Sourceをローカルに設定しても直りませんでした。
もちろん、Debug用のメッセージも表示されないので、完全に入力を検知できてないです。

    void Update()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            transform.position = new Vector3(0.0f, 3.0f, 0.0f);
            //Debug.Log("Space");
        }

    }

解決方法：
UnityとVisual Stadioを再起動したらうまく動きました。

Unityは不安定の面もありますね。（汗）
TypeScriptで2つのDateの秒差を返す

TypeScriptで，2つのDateから秒の差を返す処理を備忘録として記載します．

## 環境

* TypeScript 4.5.4

## 仕様

* 2つの `Date` 型を引数として，秒の差を算出する
* 返却値は `number` 型で返す
* マイナスの `number` 値を返す場合あり
* 端数としてミリ秒が発生する場合は切り捨て処理を行う

## 秒の差を返す関数を実装

```typescript
export const calcDifferenceSecond = (targetDateTime: Date, compareDateTime: Date) :number => {
    let diffMilliSecond = targetDateTime.getTime() - compareDateTime.getTime();

    // ミリセカンドから秒に変換して返す
    return Math.floor(diffMilliSecond / 1000);
}
```

基本的には `Date` 形式のデータを `getTime()` を用いてUNIXエポックからの経過ミリ秒( `1970-01-01 00:00:00` からの経過ミリ秒）に変換した後に，差分を求めます．

ミリ秒同士で差分を算出するため，値を返すときに `1000` を割って秒に変換しています．
ffmpeg Tips

いつも忘れるのでメモ

# 静止画から動画を作成する

```shell
ffmpeg -loop 1 -i input.jpg -vcodec libx264 -pix_fmt yuv420p -t 3 -r 29.97 output.mp4

-t : 作成する動画の時間[sec]
-r : frame rate
```

# 動画を1秒ごとにx枚の静止画にする

```shell
ffmpeg -i input.mp4 -r 1 output%04d.png

-r : frame rate (=x) 上記の例では1秒に1枚の静止画にする
```

# 動画の連結

```text:list.txt
file /path/to/input0001.mp4
file /path/to/input0002.mp4
...
file /path/to/inputxxxx.mp4
```
```shell
ffmpeg -f concat -i list.txt -c copy output.mp4

-c : codec ("copy"で入力と同一codec)
```

# 参考
http://tecsingularity.com/ffmpeg/make-movie/
https://gist.github.com/hirokai/807d05ddd9f749100e1b96f9d0422e66
https://qiita.com/niusounds/items/c386e02ab8e67030bdc0
三項演算子で条件式が複数あるとき

##そもそも三項演算子とは?
if文が複数行にわたるとき、一行でそれを記述できるのが三項演算子です。

```
条件式 A ? Aがtrueのときに出力する値: Aがfalseのときに出力する値;
```
が基本形です。実際に具体例を見ていきましょう。

```
a = 10 
a > 10 ? 'hoge': 'fuga';
=> "fuga"
```
aには10が格納されます。これは10より大きくはないので、falseです。つまり、fugaが出力されます。

これをif文で書くとどうなるでしょうか？

```
a = 10
if a > 10
  puts 'hoge'
else
  puts 'fuga'
end
=> "fuga"
```
このように6行で書かれるものが、2行で書けるというのが三項演算子のスゴイところですね。

##三項演算子の条件に複数あった場合はどうすればよいのか?
以下のようなコードがあるんです。

```
a = 条件式1 ? 条件式2 ? "A" : "B" : "C";
```
このとき、以下の表にまとめたものが役に立ちます。

| 条件式1 | 条件式2 | 出力結果 |
|:-:|:-:|:-:|
| true  | true  |  A |
| true | false  | B  |
| false  | true  | C  |
| false  | false  |  C |

##実際に問題を解いてみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2485775/fbb319d3-69fa-0f19-caa9-7cb0a04fdc05.png)

###選択肢➀
結論からいうと、エラーは発生しません。文法エラーやnomethoderrorは見つからないので、エラーは起きないです。

###選択肢➁
これが正解ですね。false||trueは成立しますので、1つ目の条件式はtrue。2つ目の条件式のtrue && falseは成立しないので、2つ目の条件式はfalseになります。上の表を参考にしてみると、出力結果は a & bになります。
その結果は[1, 3, 8]になります。

###選択肢➂
これは不正解ですね。これが正解になるには、2つの条件式がfalseになる必要があります。この選択肢を正解にするとなると、

```
a = [1, 2, 3, 5, 8]
b = [1, 3, 6, 7, 8]
c = false && true ? true && false ? a | b : a & b : b ;
p c
=> [1, 3, 6, 7, 8]
```
###選択肢➃
これは不正解です。この選択肢を正解にするには、

```
a = [1, 2, 3, 5, 8]
b = [1, 3, 6, 7, 8]
c = false || true ? true && false ? a & b : a | b : b ;
p c
=> [1, 2, 3, 5, 8, 6, 7]
```

##三項演算子の使い分け
このようにスッキリ書ける三項演算子ですが、使い時を弁えないと、可読性が落ちる書き方になります。
使い時に関しては、[こちら](https://qiita.com/noriya1217/items/5195d078785d8adf292f)の記事を参考にしてください

以上です。
何か間違いがございましたら、ご教示いただけますと幸いです。
【参考文献】

https://qiita.com/riekure/items/e510dba2e507403d990a

https://docs.ruby-lang.org/ja/3.0/doc/spec=2foperator.html#cond
管理エージェントをゲートウェイ方式で設定する

オンプレミス環境などで管理エージェントがインターネット上にあるDB ManagementやLogging AnalyticsといったOracle Cloudサービスと直接通信できない場合、プロキシの役目を担うGatewayというエージェントを経由することで、管理エージェントの取得した情報をO&Mの各サービスにアップロードすることが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/6fcae5a8-6fa3-25c9-aa15-81e4be427e0f.png)
この図のようにGatewayをインストールしたサーバーは、内部サーバーにインストールされた管理エージェントとインターネットにあるO&Mの両者にアクセスできる必要があります。
ゲートウェイのサーバーからOracle Cloudサービスへの通信は、HTTPSのアウトバウンドのみです。Oracle Cloudサービス側からゲートウェイへのアクセスは発生しません。

それでは、インストール手順をご紹介します。

## 管理エージェント/ゲートウェイのインストール要件を確認

管理エージェント及びゲートウェイをインストールできるOSバージョンを[こちら](https://docs.oracle.com/ja-jp/iaas/management-agents/doc/perform-prerequisites-deploying-management-agents.html#GUID-BC5862F0-3E68-4096-B18E-C4462BC76271)で確認

## IAMポリシーの設定
ユーザーへ必要な権限は、お使いの環境に合わせて[ドキュメント](https://docs.oracle.com/ja-jp/iaas/database-management/doc/permissions-required-database-management.html)を参考に割り当ててください


# 管理エージェント/ゲートウェイのダウンロードとキーの作成
- OCIメニューから、管理及び管理 -> 管理エージェント -> ダウンロードとキーから、LinuxのエージェントとGateway for LINUXをダウンロード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/25bad145-be7c-eaeb-603b-0a4f39c64a4b.png)


- 同ページ下部にあるエージェント・インストール・キーから新規でキー作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/81f35109-3825-0d2c-2bcc-0bb38fbdc90b.png)

- 作成後にキーファイルをダウンロードしておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/b8841eff-f58b-0e82-5d42-4bb8d542a0a0.png)



## ゲートウェイのインストール
```
#ダウンロードしたゲートウェイ(mgmt_gateway.rpm)をインストール
rpm -ivh oracle.mgmt_gateway.rpm

#input.rspファイルを作成する
vi /opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

#含める必須パラメータは以下を記述。インストール・キーの値はダウンロードしたキーファイルにある。パスワードは自分の好きな値を設定
ManagementAgentInstallKey = xxxxxxxxxxxxxxxxxxxxxx
CredentialWalletPassword = WelCome12345##

※proxy経由の場合は以下も追記
ProxyHost = xxxx.oracle.com
ProxyPort = 80

#ファイル所有者変更
chown mgmt_agent:mgmt_agent /opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

#ゲートウェイ・インストール実行
/opt/oracle/mgmt_agent/agent_inst/bin/setupGateway.sh opts=/opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

#正しくインストール完了すれば、最後に下記メッセージで完了
Waiting for Gateway Proxy to start...
Gateway Proxy started successfully.

````

# 管理エージェントのインストール
```
#ダウンロードした管理エージェント(mgmt_agent.rpm)をインストール
rpm -ivh oracle.mgmt_agent.rpm

#input.rspファイルを作成する
vi /opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

#インストールキーとパスワードはゲートウェイと同じでOK。ProxyHostは、Gatewayサーバーのホスト名またはIPアドレスを指定
ManagementAgentInstallKey = xxxxxxxxxxxxxxxxxxxxxx
CredentialWalletPassword = xxxxxxxxxxxxxxxxxxxxxxx
ProxyHost = ゲートウェイのホスト名またはIP
ProxyPort = 4480 (ゲートウェイのポートはデフォルト4480)

#ファイル所有者変更
chown  mgmt_agent:mgmt_agent /opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

#管理エージェントインストール実行
/opt/oracle/mgmt_agent/agent_inst/bin/setup.sh opts=/opt/oracle/mgmt_agent/agent_inst/bin/input.rsp

以下メッセージが出力されいていればOK
Agent setup completed and the agent is running.
```

OCIメニューから、管理及び管理 -> 管理エージェント -> エージェントにインストールしたGatewayと管理エージェントとゲートウェイがアクティブになっていればOK

以降は、通常の管理エージェントの場合と同様にDatabase ManagementやLogging Analtyicsの設定を行っていきます
(初心者向け)Railsで論理削除とはなにか解説する

# 概要
この記事ではRuby on Railsを例として「論理削除とはなんぞ」や、
というのをざっくり解説したいと思います。

## 謝辞
* 株式会社X-HACKさんで[PARKLoT](https://www.park-lot.com/)というTwitterインスタントウィンサービスのバックエンド開発をお手伝いさせていただいております。本記事ではその経験で得られた知識をアウトプットしています。ありがとうございます。
* 参考にさせていただいた記事は以下の通りです。ありがとうございます。
    * [SQL で理解する DB の物理削除と論理削除](https://zenn.dev/kotahashihama/articles/f9676a52bd83c9)

## 物理削除と論理削除の違い
データベースのデータを削除する機会はよくあると思います。

* ユーザーの退会処理
* コメントの削除
* mixiの恥ずかしい黒歴史記事の削除
などなど。

ここで、データの削除の仕方として、2つの方法があります。
1. データを完全に削除してしまう　→**「物理削除」**
2. データを残しながら削除したかように扱う →**「論理削除」**


### 物理削除の例
たとえば、思春期時代に書いた恥ずかしいポエムがネット上に残っていたとしましょう。
これに気づいた投稿者ポエマーAくんとしては、なんとしてもこのデータを地球上から消し去りたいはずです。

その場合は**「物理削除」**でデータを消してあげるべきでしょう。

```ruby
class Api::V1::PoemController < ApplicationController
  def destroy
    @poem = Poem.find_by(id: params[:id])
    @poem.destroy # ←データを完全に消去(物理削除)
    render json: {messages: ["ポエムを削除しました"]}, status:200
  end
end
```


### 論理削除の例

しかし、もしかしたらさらに10年後、ポエマーAくんは大人になり、
若かった頃の自分がどんなことを考えていたのか、振り返ってみたくなる日が来るかもしれません。
あんなに恥ずかしかった内容も、笑って振り返れる日が来るかもしれません。
その場合、「物理削除」したポエムは永遠に戻ってこないので、
「あぁ…消さなければよかった…」と後悔する可能性があります。

そんな場合は、**「論理削除」**が適切です。
「論理削除」とはつまり、「削除したかのように扱う」ということです。

いやだからその論理削除ってなんやねんという方のために、
論理削除の具体例を上げてみます。
(正確にはちょっと違うかもしれませんが)

* 契約書など重要な書類で、間違った部分を二重線で消して書き直す (二重線を引いた部分を「削除」したことにする)
* 遊戯王カードで死んだモンスターを墓地に送る (カードは残っているが墓地ゾーンに置くことで死んだことにする)
* 「昨日の晩は酔っていて何も覚えていない。覚えていないのだから何もなかった。いいね?」(なにかをやらかしたという事実は消せないのでとりあえず何も無かった事にする)

などなど、実は日常的に論理削除っぽいことが使われているのです。
べんりですね！！おとなはきたない。

Railsのコントローラーで書いてみるとこんな感じでしょう。


```ruby
class Api::V1::PoemController < ApplicationController
  def destroy
    # データを消さずにdeleted_atカラムに「削除した日付」を記録し削除したことにする(論理削除)
    @poem.update!(deleted_at: DateTime.now)

    render json: {messages: ["ポエムを削除しました"]}, status:200
  end
end
```

ポイントはdestroyメソッドを使わずに、updateメソッドでカラムを更新しているだけ、という点です。
そして、deleted_atという削除日を記録するためのカラムが必要になります。

### 論理削除の注意点
しかし残念ながらこれだけでは論理削除したはずの
恥ずかしいポエムは普通に一覧などで表示されてしまいます。
これは大変いけません。いけませんよ!!!


```ruby
class Api::V1::PoemController < ApplicationController
  def index
    @poems = Poem.all  #　論理削除したポエムまで一覧表示されてしまう
    render json: @poems, status:200
  end
end
```

|id|title|content|deleted_at|
|:---:|:---:|:---:|:--:|
|1|普通のポエム|いい感じの内容|nil|
|2|恥ずかしいポエム|大変恥ずかしい内容|2022-02-05|
|3|普通のポエム|いい感じの内容|nil|
|4|普通のポエム|いい感じの内容|nil|
|5|普通のポエム|いい感じの内容|nil|

※そもそも恥ずかしくないポエムなどあるのかという点はここでは議論しません。


### 対策
論理削除したポエムを表示させないように、deleted_atカラムを使ってソートしてあげましょう。


```ruby
class Api::V1::PoemController < ApplicationController
  def index
    # deleted_atがnilであれば論理削除していないので、
    # 下記のように書くと論理削除した恥ずかしいポエムを除いて一覧表示してくれる
    @poems = Poem.where(deleted_at: nil)  
    render json: @poems, status:200
  end
end
```

|id|title|content|deleted_at|
|:---:|:---:|:---:|:--:|
|1|普通のポエム|いい感じの内容|nil|
|3|普通のポエム|いい感じの内容|nil|
|4|普通のポエム|いい感じの内容|nil|
|5|普通のポエム|いい感じの内容|nil|


また、showアクションでもdeleted_atがnilの場合は、表示させないようにしてあげると良いでしょう。
(今回は割愛します)

# まとめ
一般的なサービスであればデータが残っていた方が何かと便利なので、「論理削除」の出番は多いかと思います。
一方で個人的には恥ずかしいポエムは物理削除されるべきと考えます。頼むぞmixi・・・！
Linux MINTにXAMPPをインストール続き

久々に Linux MINT にXAMPPを使ってLAMPの環境を作ったので、備忘録を兼ねて書きます。
先日の記事の続きのようになってしまっていますが、MySQLのrootのパスワード設定が上手く行かなかたので、ここに関連記事を記録したいと思い記事を書きました。

VirtualBoxの仮想マシンに、MINTをインストールしました。
そして、こちらの記事を参考にして、XAMPPのインストールを行いました。

[XAMPPの基本的なインストール【素人向け】](https://xn--v8jtdudb.com/%E3%81%AD%E3%81%93/Ubuntu/XAMPP%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%80%90%E7%B4%A0%E4%BA%BA%E5%90%91%E3%81%91%E3%80%91)

XAMPPのダウンロードは、ここから行いました。
https://www.apachefriends.org/index.html

先日書いた記事に、インストールのことを書きました。
https://qiita.com/masaru21/items/828b7dcfa67b5bdada55

XAMPPをインストールした後、こちらの記事も役立ちました。
http://pineplanter.moo.jp/non-it-salaryman/2018/02/11/xampp-in-linux/

私は、仮想マシンにMINTをインストールしています。
MacBookAirで見ているブラウザの情報を、仮想マシンにコピペしたいときがあります。
そのためChromeをMINTにインストールしたかったのです。
でも、Linux-modules-5.4.0-97-generic_5.4.0-97.110_amd64.deb
が、壊れているとエラーになり、この関係でChromeをリポジトリからインストールできず困っていました。
結局firefoxでGmailにログインして、工夫しました。
上記のエラーの解決方法をご存知の方が見えましたら、ぜひ、教えてください。

また仮想マシンで、コピー＆ペーストの設定を双方向にしました。
また、共有フォルダの設定も行いました。
そのため、ブラウザの情報を仮想マシンで使いたいとき楽になりました。



Chromeのインストールについては、こちらの記事を参考にしました。
私は、MINT20をインストールしたので、公式リポジトリからインストールした方が良いみたいです。

https://ja.linuxcapable.com/how-to-install-google-chrome-on-linux-mint-20/

https://tombouctou-food.com/ja/ja142919/

##MySQLの設定
![スクリーンショット 2022-02-05 19-05-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20977/784c628c-5b36-aced-6b3d-89d3706fcd6b.png)

XAMPPを起動してブラウザで確認した時、画像の赤い丸で囲んだphpMyAdminをクリックすると、MyAminを起動できました。
そのためMySQLでルートのパスワードを設定することでトラブルに嵌るよりも、WordPressを使うためのデータベースとユーザーを登録した方が良いと思いました。

##XAMPPのセキュリティ設定
下記の記事を参考にしました。

https://proengineer.internous.co.jp/content/columnfeature/11084

LinuxのXAMPPのセキュリティ

https://www.candd.co.jp/blog/xampp-for-linux%E3%81%AE%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E8%A8%AD%E5%AE%9A

```bash
sudo  /opt/lampp/lampp security
```
を実行すると順番に確認してくれます。

最初は、MySQLがネットワークからアクセスできます。
これをOFFにしますか？と質問してきます。
私は、OFFにしました。

次に、MyAdminのパスワードの設定を聞いてきます。
私は、パスワードの設定をしました。

次に、MySQLのルートのパスワードを設定するか聞いてきました。
私は、設置しました。

次に、FTPのパスワードを変えるか聞いてきました。
私は、設定しました。

XAMPPの記事は、WindowsのXAMPPに対して書かれているものが多いです。
LinuxのXAMPPの記事も、増えてほしいと思いました。

##追記
今回のようにphpMyAdminから操作したい場合、こちらの記事が役立ちます。

https://www.dbonline.jp/phpmyadmin/
オブジェクト指向って結局なんなの？

# はじめに
この記事では、"オブジェクト指向”っていう概念にどう向き合ったら良いのか、っていうのを個人的に整理してます。

最初に言っちゃうと、__オブジェクト指向っていうのはコーディングする時に注意すべきこと__、が私の結論になります。
# まずオブジェクト指向でググった
以下のページが簡単でわかりやすいかも！っと思ったんですが。。
>[ロボットプログラミング教室](https://robo-done.com/blog/2021/04/doneship_object_oriented/#:~:text=%E3%80%8C%E3%81%82%E3%82%8B%E5%BD%B9%E5%89%B2%E3%82%92%E6%8C%81%E3%81%A3%E3%81%9F,%E3%82%92%E6%84%8F%E5%91%B3%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82)
>◆オブジェクト指向とは？
「ある役割を持ったモノ」ごとにクラス（プログラム全体の設計図）を分割し、モノとモノとの関係性を定義していくことでシステムを作り上げようとするシステム構成の考え方のこと。

>・オブジェクト：直訳すれば「物」「対象」という意味。プログラミングにおいてはデータと処理の集まりを意味している。
>・クラス：データと処理をひとつにまとめる機能。モノ（オブジェクト）の設計書のようなもの。
>・プロパティ：モノ（オブジェクト）が持っているデータのこと。（車に例えれば「メーカー」「排気量」「色」など）
>・メソッド：モノ（オブジェクト）が持っている処理のこと。（車に例えれば「走る」「曲がる」「止まる」などアクションを起こす処理のこと）

>◆オブジェクト指向の三原則
●継承＝同じようなプログラムを1か所にまとめてコードを再利用しやすくする仕組み
●カプセル化＝他のプログラムからできるだけ変更できない仕組み
●ポリモーフィズム＝継承したコードの一部を変更して利用するための仕組み

ふむー。
なるほど。概念はわかったような気もするし、こういう仕組みがあるのもわかったけど、これを覚えて何をどうすれば良いかよくわからん。。。なんとなく継承とかメソッドとか使っているけども。。




#オブジェクト指向でコーディング
と思ったら、オブジェクト指向を世界一わかりやすく説明している記事をみつけました。

>[【これで無理なら諦めろ】オブジェクト指向を世界一分かりやすく説明する](https://qiita.com/okaeri_ryoma/items/c47b387dd93a0dfc8ec1)

>```Ruby
def calculate(number_1, number_2)
  number_1 * number_2 + 1
end

>def update_content(value)
  "結果は#{value}です"
end

>def output(content)
  puts content
end

>value = 100
content = ""

>new_value = calculate(value,20)
// 2001

>content = update_content(new_value)
// "結果は2001です"

>output(content)
=> "結果は2001です"
```

>一方で、オブジェクト指向で書くとこんな感じ。

>```Ruby
num = Num.new(100,"")
num.calculate(20)
// 2001

>num.update_content
// "結果は2001です"

>num.output
=> "結果は2001です"
```
>つまり、オブジェクト指向とは、__コードにオブジェクトという主人公を登場させ、それをメインに話を進めていく設計の考え方__といえます。

なるほどー。

# まとめ
上記を踏まえた結果、__オブジェクト指向っていうのはコーディングする時に注意すべきこと__っていうのが私の認識になりました。

オブジェクト指向の三原則も、ただの便利な仕組みっていうんじゃなくて、オブジェクトを中心にコーディングする為に便利な仕組みってことなのかなー、っていう認識になりました。

就活の面接の時に聞かれるのも、オブジェクト指向でコーディングしてくれないと困るから、っていうことなんだろう。

## 追記
ものすごい丁寧に説明してくれている記事があったので追記
>[オブジェクト指向とは結局何なのか あるいはプログラミングで気をつけるべきたった一つのこと](https://qiita.com/lamrongol/items/8a339721ee20a1c81e3c?gclid=CjwKCAjw9LSSBhBsEiwAKtf0n0PlUXEAi7lhSQxUJkGapok7LAv9wX5cMWpOoN_2ROpbY-GuikioKBoCPHUQAvD_BwE)
RailsアプリをHerokuにプッシュしたときに、「Failed to install gems via Bundler.」とエラーがでた時の解決方法

# 環境
- Ruby 2.6.6
- Rails 6.0.3

# 発生したエラー
Railsで作成したアプリケーションをGitHubにプッシュした後に、Herokuへプッシュしようとしたところ、以下の通りエラーが出て、Herokuへのプッシュができませんでした。

```
$ git push heroku master
Enumerating objects: 114, done.
Counting objects: 100% (114/114), done.
Delta compression using up to 8 threads
Compressing objects: 100% (96/96), done.
Writing objects: 100% (114/114), 147.51 KiB | 6.15 MiB/s, done.
Total 114 (delta 7), reused 0 (delta 0), pack-reused 0
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-20 stack
remote: -----> Determining which buildpack to use for this app
remote:  !     Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used.
remote:                         Detected buildpacks: Ruby,Node.js
remote:                         See https://devcenter.heroku.com/articles/buildpacks#buildpack-detect-order
remote: -----> Ruby app detected
remote: -----> Installing bundler 2.2.33
remote: -----> Removing BUNDLED WITH version in the Gemfile.lock
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.7.5
remote: -----> Installing dependencies using bundler 2.2.33
remote:        Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
remote:        Your bundle only supports platforms ["x86_64-darwin-19"] but your local platform
remote:        is x86_64-linux. Add the current platform to the lockfile with `bundle lock
remote:        --add-platform x86_64-linux` and try again.
remote:        Bundler Output: Your bundle only supports platforms ["x86_64-darwin-19"] but your local platform
remote:        is x86_64-linux. Add the current platform to the lockfile with `bundle lock
remote:        --add-platform x86_64-linux` and try again.
remote: 
remote:  !
remote:  !     Failed to install gems via Bundler.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
remote: Verifying deploy...
remote: 
remote: !       Push rejected to immense-bastion-06797.
remote: 
To https://git.heroku.com/immense-bastion-06797.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/immense-bastion-06797.git'
```

# 解決方法
エラー文の下の方にある「Failed to install gems via Bundler.」という文字が赤く表示されているため、その付近にエラーの原因と解決方法のヒントがありそうです。
よく見ると、「Failed to install gems via Bundler.」というエラー文の上に、「`bundle lock --add-platform x86_64-linux`というコマンドを実行して再試行してください。」といった内容の文言が記載されていることが分かります。
実際に、ターミナルでこのコマンドを実行して、再度Herokuへプッシュしてみます。

```
$ bundle lock --add-platform x86_64-linux

$ git add .

$ git commit -m "Gemfile.lock fix"

$ git push && git push heroku master
```

無事Herokuへのプッシュができました。

# 参考
- https://yumishin.com/heroku-push-error/#i-4

# まとめ
- 今回のエラーは、ローカル環境のbundlerとHeroku環境のbundlerのバージョンに相違があったため発生したものだということが分かりました。
- 今回はローカル環境のbundlerのバージョンをそのままにしておきたかったのでこの方法で対処しましたが、またこのエラーが発生した時は、ローカル環境のbundlerのバージョンとHeroku環境のbundlerのバージョンを一致させる方法で解決したいと思います。
pythonのunittestを使う方法

pythonのunittestを作るときいつもVSCodeで動かないのが悔しいのでメモを残す

unittestは基本的にこのような形のソースをで書く

```python
import unittest

class AAA( unittest.TestCase ):
    def test_xxxx(self):
        ...

if __name__ == '__main__':
    unittest.main()
```

ポイントは

- クラスは `unittest.TestCase` クラスを継承すること
- メソッドは `test_` ではじめること(多分、引数はないほうがいい)

最後の`if __name__ ==`以降はこのファイルを直で起動したときに実行できるようにするためのもの。

次に私はいつもここで困るのだが、フォルダ構成。私はpythonに慣れていないのでモジュールの動きがわからず、「???」となったのでこのメモを残すことにした

```
test
  ├ testsuite
  │　　├ __init__.py
  │   └ AAA_test.py
  │
  ├ test_root.py
  └ __init__.py
```

のように各フォルダに__init__.pyとするとパッケージ？のルールに従ってtest_AAA.pyのを見つけてくれる。

上記の構成なら `python3 -m unittest` で認識してくれる。

VSCodeの場合、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/50153a4e-10fe-6556-eb9d-bb2aabc34624.png)

をクリックして

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/59b0026c-5120-56f8-6f42-46f3278c3115.png)

の Configure Python Testsをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/ec9a332c-c4a9-e4df-7c40-977f11809245.png)

unittestを選択して下の「test」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/2ee4b06c-905d-7f44-b638-25be32816886.png)

テストファイル名のパターンを聞いてくるので4番目の「test_*.py」を選択する。

![スクリーンショット 2022-02-05 19.36.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/f418078a-0a21-6dfe-055b-44636fc2b440.png)


 こうなればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76851/d719abb7-d2a8-f07d-a66b-a2b0f5d7a335.png)

スクショミスで上2つが実行ずみになっているのはご愛嬌。

参考: https://docs.python.org/ja/3/library/unittest.html
php8 で WP Offload Media Lite を使うときのエラー解決方法 【Your server currently has no SimpleXML PHP module and no XMLWriter PHP module.】

# 

ワードプレスのプラグイン WP Offload Media Lite を有効化した際、下のエラーメッセージがでた。

![mediaチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596144/7e8a1ac5-c96a-bab1-cb5c-fca541820652.jpeg)


```
The official Amazon Web Services SDK requires PHP 5.5+ with SimpleXML and XMLWriter modules, and cURL 7.16.2+ compiled with OpenSSL and zlib. Your server currently has no SimpleXML PHP module and no XMLWriter PHP module.
You can deactivate the WP Offload Media Lite plugin to get rid of this notice.
```

`SimpleXML`と`XMLWriter`が無いのでインストールしてと言っている。

# 検証環境

- PHP 8.0.13
- Apache/2.4.52
- サーバー EC2 Amazon Linux 2


# 解決した手順

`php-xml`をインストールすれば、`SimpleXML`と`XMLWriter`を一度にインストールできるらしいので

```
sudo yum install -y php-xml
```

apacheを再起動する

```
$ sudo systemctl restart httpd.service
```

しかしメッセージは消えない。

`SimpleXML`と`XMLWriter`がインストールされているか確認する。

```
$ php -m
```

![phpm.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596144/75ba43ac-976b-4217-7939-039e910df131.jpeg)

たしかに`SimpleXML`と`XMLWriter`はインストールされている。

`php-fpm`を再起動してみる。`php-fpm`はPHP標準のアプリケーションサーバのこと。

```
$ sudo systemctl restart php-fpm
```

![phpnomessages.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596144/20a1cfba-20f5-2f97-ff85-13c14699239c.jpeg)

エラーメッセージが消えました！

# その他

以前php7.2でWP Offload Media Liteを使ったときはApacheを再起動するところまででメッセージは消えた。php8.0~では`php-fpm`の再起動が必要なようだ。

参考: 
[[WordPress]WP Offload Media Liteを使うことができない](https://teratail.com/questions/289793#reply-410630)
[php-fpmについてざっくり紹介してみる](https://maasaablog.com/development/php/1295/)
APIにGETリクエスト送付時のテストコードを書く

# やりたいこと
・APIにGETリクエストを送るプログラムのテストを書く
・API参照箇所をMockにする

### 以下記事を参考にさせていただきました
Mockito 3 + JUnit 5 で基本的なモック化とテストをするサンプルコード
https://qiita.com/niwasawa/items/f8798c981b4d08ecfa55

# 参照APIの構築
テスト用に参照用のAPIを構築する
参考　https://spring.pleiades.io/guides/gs/rest-service/

# 2つのクラスを作ります
実行するクラスとリクエストを送るクラスを作成する

**実行クラス**

```Java:App.java
public class App {
    public String send_request(int user_id) throws MalformedURLException, IOException {
        return request.send(user_id);
    }
}
```

**リクエスト送付クラス**

```Java:Request.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class Request {
    public String send(int user_id) throws MalformedURLException, IOException {
        HttpURLConnection conn = (HttpURLConnection) new URL("http://localhost:8080/greeting/" + user_id).openConnection();
        InputStream in = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder out = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            out.append(line);
        }
        reader.close();
        return out.toString();
    }
}
```

# テストクラス
Request.javaをモックに差し替えレスポンスの文字列を定義します

```Java:Apptest.java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
import java.io.IOException;
import java.net.MalformedURLException;

public class AppTest {
  @InjectMocks
  private App kusatest = new App();
  @Mock
  private Request request = new Request();
  public void 正常ケース() throws MalformedURLException, IOException {
      String output_expect = "{\"id\":\"2\",\"content\":\"Hello, World!\"}";
    doReturn(output_expect).when(request).send(2);
    assertEquals(output_expect, kusatest.send_request(2));
  }
  @Test
  public void ユーザーがいない_404() throws MalformedURLException, IOException {
    doThrow(IOException.class).when(request).send(10);
    Assertions.assertThrows(IOException.class, () -> {
      kusatest.send_request(10);
    });
  }
}
```

```gradle:build.gradle(抜粋)
dependencies {
  testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
  testImplementation 'org.mockito:mockito-core:3.6.0'
  implementation 'com.google.guava:guava:30.0-jre'
  testImplementation group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.8.2'
}
```

これでテストコード実行時にはAPIに疎通できなくても、モックで定義したレスポンス文字列を受け取り、後続の処理を検証します。
Dockerについてまとめてみた。

#はじめに
こんにちは、山田です。
今回は、Dockerについてまとめていきます。よろしくお願いします。

#コンテナについて
はじめに、アプリケーションを構成するコンポーネントを以下に記載する。

|構成要素|
|---|
|**ランタイム/エンジン**|
|**アプリケーションコード**|
|**ライブラリなどの依存物**|
|**アプリの設定ファイル**|

コンテナは、アプリケーション動作に必要なものをパッケージングする。パッケージ化したものを、ローカル環境、開発環境、本番環境にデリバリーすることで、ローカル環境、開発環境、本番環境のすべての環境でアプリが動作する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/0a698fa5-574a-57b2-e290-8d0554525f52.png)

コンテナの構造として、物理マシンがありその上にＯＳが稼働しており、コンテナエンジンを介して、コンテナを1つのプロセスとして稼働する。仮想マシンと比べて、ハイパーバイザー、ゲストOSがない。1つのOS上で、複数の同時稼働実行環境を提供する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/5ce64ac1-2114-8504-0f28-f9736674f561.png)

#コンテナの特徴
コンテナの特徴を以下に記載する。

|特徴|説明|
|-------|-------|
|**起動・停止が高速**|ハイパーバイザー・ゲストOSがないため、イメージサイズが小さく、起動停止が高速。|
|**アプリケーションのデプロイが容易**|OSのカーネルが同じであれば、どこで動かしても同じような結果を得ることができる。|
**柔軟性**|１つのイメージから複数のコンテナを起動可能。|

#Dockerについて
Dockerの構成要素を以下に記載する。

|構成要素|説明|
|-------|-------|
|**Dockerデーモン**|イメージ、コンテナ、ネットワーク、ボリュームなどのDockerオブジェクトを管理|
|**Dockerクライアント**|Dockerデーモンと通信するクライアント|
|**Dockerレジストリ**|Dockerイメージを格納するレジストリ|
|**Dockerイメージ**|OSやアプリを含むコンテナンのテンプレート、Dockerレジストリに格納し、そこから取得する。|
|**Dockerコンテナ**|プロセスとして起動するアプリの実行環境Dockerイメージから起動|
|**Dockerfile**|ベースＯＳのイメージからアプリまでの動作環境が記述されたテキストファイル|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/7c362c7a-b62b-cebc-9642-1c6d3aed04bd.png)

#Dockerの基本的な動作について
Dockerの基本的な動作について記載する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/11c2a4a2-a3c2-9555-8b21-c7c2d3756d3d.png)

**①：SSH等でサーバーに接続する。
②：docker pull コマンドで、Dockerレジストリよりイメージを取得する。
③：docker run コマンドでコンテナを実行する。**
【React Three Fiber】Boilerplateの紹介

<!-- ===================================================== -->
# 概要
<!-- ===================================================== -->
CRA（Create React App）プロジェクトで**React Three Fiber**を使うためのボイラープレートを作成しました。

https://github.com/nemutas/r3f-boilerplate

<!-- ===================================================== -->
# 環境
<!-- ===================================================== -->
* CRA（Create React App）
* TypeScript

npmを使用して、以下のコマンドで作成しています。

```.cmd
npx create-react-app . --template typescript --use-npm
```

<!-- ===================================================== -->
# インストールしているパッケージ
<!-- ===================================================== -->
### dependencies
* [@emotion/css](https://emotion.sh/docs/introduction)
* [three](https://threejs.org/)
* [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction)
* [@react-three/drei](https://docs.pmnd.rs/drei/introduction)
* [valtio](https://github.com/pmndrs/valtio)
* [lil-gui](https://lil-gui.georgealways.com/)
* [gsap](https://greensock.com/)

### devDependencies
* [@types/three](https://www.npmjs.com/package/@types/three)
* [gh-pages](https://github.com/tschaub/gh-pages)

<!-- ===================================================== -->
# 状態管理
<!-- ===================================================== -->
Canvasとdom要素間で状態を受け渡す場合、RecoilやContextを使用するとコンテキストブリッジを追加する必要があります。
[valtio](https://github.com/pmndrs/valtio)を使用すれば、コンテキストブリッジを追加しないで状態の受け渡しができるようになります。

<!-- ===================================================== -->
# デプロイ
<!-- ===================================================== -->
[gh-pages](https://github.com/tschaub/gh-pages)を使用した、Github Pagesへのデプロイ手順です。

1）package.jsonを変更します

```
"homepage": "https://<your name>.github.io/<your project name>/"
```

2）デプロイコマンドを実行します

```
npm run deploy
```

<!-- ===================================================== -->
# コントローラー（Leva vs lil-gui）
<!-- ===================================================== -->
このボイラープレートでは、コントローラとして[Leva](https://github.com/pmndrs/leva)ではなく、[lil-gui](https://lil-gui.georgealways.com/)を使用します。

LevaはReactに特化したパッケージで、値の更新と同時に関連するコンポーネントを再描画します。
Three.jsでシェーダーを使う場合、コンポーネントの再描画と同時にシェーダーも再生成されるため、**GPUがストールする**原因になります。
そのため、フレームループ（useFrame）内で値を更新する方が都合がいい場合が多く、Levaではなくあえてlil-guiを使用しています。

しかし、lil-guiはReact用のパッケージではないので、カスタマイズして使用しています。
カスタマイズの概要については、[【React Three Fiber】GPGPUを使用したParticlesの実装](https://qiita.com/nemutas/items/b40baa2a1f33fae6b20d#guits)でまとめています。

※ GPUのストールについて
[WebGPUでガチリアルタイムレンダリングの世界が見えてきた](https://qiita.com/emadurandal/items/3c0f8e06d443346aad8f#gl%E7%B3%BB%E3%81%A7%E3%81%AFgpu%E3%81%8C%E4%B8%8D%E6%84%8F%E3%81%AB%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%82%E3%82%8B)
juliaのimportとusingの違い


# juliaのimportとusingの違いについて

juliaを初めて触った頃に、importとusingの違いがわからずモヤモヤしていた時期が続いたので、備忘録として違いについてまとめる。

以下のコードを例にする。

```julia
module MyPkg
    export greet
    export another
    greet() = println("greet with no args")
    another() = println("これは邪魔者です")
end
```

実行

```julia
#ドット.は自前のモジュールの場所を表す。
using .MyPkg # greet, another が見える
# これは、exportをつけているからである。

using .MyPkg:greet # greet は見える, another は見えない
using .MyPkg:MyPkg # MyPkg.greet でアクセスできる
import .MyPkg # using .MyPkg:MyPkg とほぼ同じ
```


usingを使えば、exportしている関数は`using .MyPkg`をするだけで呼び出すことができる。例えば、以下のような感じである。

```julia
julia> using .MyPkg

julia> greet()
greet with no args
```


一方、`import .MyPkg`は、exportの有無関係なく、`MyPkg.function()`がなければいけない。

```julia
julia> import .MyPkg

julia> greet()
ERROR: UndefVarError: greet not defined
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

julia> MyPkg.greet()
greet with no args
```

特定の関数を呼び出したければ、`import/using .MyPkg:function` とする。

```julia
using .MyPkg:greet # greet は見える, another は見えない
```

とりあえずパッケージで遊んでみたいときは、usingを使えば良さそうである。そうすればexportしている関数は、function()だけで使うことができる。

usingを使う時に注意点として、どんな名前の関数が呼び出されているかわからないので、自分のパッケージで定義された関数と名前が衝突してしまうことである。
従って、その問題を回避するためには、`import`を使うのが無難だろう。

# 参考
こちらの記事も一緒に読むと理解が深まりました。
https://qiita.com/cometscome_phys/items/5c98aef4c10a8a575f81
WPFをデバッグしたときに現れる黒いあいつを消す

## はじめに

WPFをデバッグしたときにウィンドウ上部中央に現れるあいつを消す方法。
VisualStudioのバージョンで少し設定内容が違うらしい。
![VS2017-ON.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475473/9b262673-5287-ad8a-ee19-a89ae8d41fc1.png)

## Visual Studio 2017

「オプション＞デバッグ＞全般＞アプリケーションでランタイムツールを表示」をOFF
![VS2017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475473/511158aa-24d9-4e38-573f-66bf65c7fc92.png)

## Visual Studio 2019

「オプション＞デバッグ＞ホットリロード＞WPFおよびUWP＞アプリ内ツールバーを有効にする」をOFF
![VS2019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475473/5cb8c012-f0a6-e5a7-2ccf-944878a6d1dd.png)

## Visual Studio 2022

「オプション＞デバッグ＞XAMLホットリロード＞共通＞アプリ内ツールバーを有効にする」をOFF
![VS2022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475473/c0d11faa-133e-de29-322d-bad943a4d59c.png)
note記法だって！

これが基本型

これを使えば、ちょっと艶やかになるかな。

:::note info
インフォメーション
infoは省略可能です。
:::

:::note warn
警告
○○に注意してください。
:::

:::note alert
より強い警告
○○しないでください。
:::

https://qiita.com/Qiita/items/c686397e4a0f4f11683d#note---%E8%A3%9C%E8%B6%B3%E8%AA%AC%E6%98%8E



サンプル：

```
:::note alert
警告
こんな警告だってかけちゃう！！！
:::
```

:::note alert
警告
こんな警告だってかけちゃう！！！
:::
Vueのcomputedを呼び出して使っていて、なかなか気づかなかった挙動について

# はじめに

　Vue3のComposition APIにもようやく慣れてきてサクサクっとウェブアプリを作っていたときに、ふと遭遇した挙動に悩まされたので、忘備録として記事にまとめておくことにした。

# 問題

　computedで定義した値がHTML側にまったく反映されない。配列周りの挙動にも気をつけており、値がセットされてくるタイミングも問題なかったのに、全く動いてないか、computedがそもそも生成されていない・・・？

# 問題のコード

 　要点をまとめるとこのような処理にしていた。.vueファイルの書き方が嫌いなため、Vue3とQuasarというフレームワークにて、CDNを使った書き方でやっていた。やっていることはComposition API で.vueの書き方に似せていた。
あるコンポーネントにて、目的とするオブジェクトの配列をフィルタリングして返す *filtering_array* という関数を作り、親のコンポーネントに関数群として返していた。
　それが親側としてはhogehogeというオブジェクトだ。
　親のHTMLからは、hogehoge.filtering_arrayという形で指定することで、算出プロパティの恩恵を受けて目的を実現しようとしていた。

```javascript:test.js
//Vue.component用の処理の外出し目的の、ある.jsファイル内にて（test.jsとする）
export function defineHoge() {
  const filtering_array = Vue.computed(() => {
    var arr = [];
    //配列を編集する処理
    return arr;
  });
~中略~
  return {
    filtering_array 
  } 
}
//呼び出し側
import { defineHoge } from "./test.js"

const hogehoge = defineHoge();

return {
  hogehoge
}
```

そしてHTML側にて

```vue:html.vue
<q-list>
  <q-item v-for="(item, index) in hogehoge.filtering_array ">
   <span>{{ item.title }}</span>
   ~中略~
  </q-item>
</q-list>
```

　Quasarというフレームワークでは、q-listという要素はリストを生成する。vuetifyなどでもほとんど同じ要素がある。
　条件でフィルタリングした配列をこのリストに出力しようとしたかった。

　しかし・・・上記のやり方ではこの q-listに、配列の要素は一切出力されなかった。それどころか、~中略~内でやっていた 子要素itemで指定していた内容がすべてエラーとしてコンソールに吐かれてしまっていた。

　computedの指定の仕方に問題があったのかと思い、公式のリファレンスを何度読み直しても問題なさそうに見えた。
　いっそ配列を直接呼び出す関数にしてしまおうかとも思った。（パフォーマンス的に問題が出てくるが）


# 解決

　ふと、いっそ直接呼び出してみるか、と思った。定義した関数自体を動かすのはちょっと面倒だったので、こうした。

```javascript:oya.js
//呼び出し側
import { defineHoge } from "./test.js"

const hogehoge = defineHoge();

return {
  filtering_array: hogehoge.filtering_array
}
```

```vue:html.vue
<q-list>
  <q-item v-for="(item, index) in filtering_array ">
   <span>{{ item.title }}</span>
   ~中略~
  </q-item>
</q-list>
```

　つまり、hogehogeオブジェクト内にあるfiltering_arrayを、親のコンポーネントで同じ名前の関数として返すことにした。
　そして親のHTMLで、hogehoge.をやめてみた。

　すると、正常に配列の中身が出力されるようになった。

・・・投稿した後気づいたが、リアクティブ外になっていたのが原因か。
と気づいてこうしてみてもOKだった。

```javascript:test.js
const filtering_array ...
~中略~
return {
  hogehoge : Vue.reactive({
    filtering_array 
  })
}
//呼び出し側
import { defineHoge } from "./test.js"

const {hogehoge} = defineHoge();

return {
  hogehoge
}
```
```vue:html.vue
<q-list>
  <q-item v-for="(item, index) in hogehoge.filtering_array ">
   <span>{{ item.title }}</span>
   ~中略~
  </q-item>
</q-list>
```

　OKだった。
　Vue.reactiveでcomputedしたプロパティを囲い込んでみた。
　なるほど、公式のリファレンスで言われていたuse＊＊＊＊();の書き方に変な先入観を持っていたせいだったようだ。
　返されるのはあくまでもただのオブジェクトで内包された関数・プロパティ群であって、リアクティブは存在していない形になるようだ。
　Vue.reactive（別にVue.refでもいいのだろうが）で指定してやることでcomputedもそのまま生きてくる。



# 終わりに

　公式のリファレンス等ではcomputedは単に const hoge = computed(...) としか描かれていない。作成するウェブアプリ内で関数やプロパティの整理のためにオブジェクト内の子としてcomputedを定義した関数を内包して使おうと目論んでいた。

　~~しかしどうやら、computedをしたプロパティは、オブジェクトに内包して子プロパティ的に呼び出すことはできないことがわかった。やるならコンポーネント内では直接指定するようにしなければいけないようだった。
　普通のプロパティのようにhogehoge.[プロパティ名]などとはできない。~~

 別ファイルで定義したcomputedをそのまま使うには、次の方法のどちらかが必要だとわかった。

* 親のコンポーネント（呼び出し元）のreturn 内で、 hoge: myobject.hoge と同じ名称で呼び出させる
* use＊＊＊＊を定義したファイル内で reactiveやrefで内包しておく
  

　とくにuse＊＊＊＊という形で外出しした場合には忘れやすく、なかなか気づけないこの挙動。気をつけたい。
Macbookで画面分割をHammerspoonで行う

## きっかけ
MacBookで開発などを行っている際に、縦置きの外部ディスプレイを導入して、縦画面での上下分割をしたくて色々調べたので、そのメモを記載します。


#### 無料なもの
1. [ShiftIt](https://github.com/fikovnik/ShiftIt)  
ShiftItに関する記事が多く出てきた。しかし、最近はメンテナンスされていない。  
※（開発者のMacBookが盗まれて、Linuxに心移りされたのが原因らしい）

#### 有料App  
1. [Magnet](https://apps.apple.com/jp/app/magnet-%E3%83%9E%E3%82%B0%E3%83%8D%E3%83%83%E3%83%88/id441258766?mt=12)   
紹介されている記事では250円として記載されていたので、そのくらいなら出しても良いかなと思い調べてみると、980円に値上がりしていた。(画面分割のために、980円は高い、、。)

![スクリーンショット 2022-02-05 17.03.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94311/219b0be7-4176-4931-e90f-e40b125f65ce.png)


## [Hammerspoon](https://github.com/Hammerspoon/hammerspoon)

Shiftlnのメンテナンスに関するやりとりで、Hammerspoonが勧められていたので、導入してみた。

## 設定方法

画面分割の方法は下記ページが分かりやすかったので、詳細はそちらを見てください。

- [hammerspoon-shiftit](https://github.com/peterklijn/hammerspoon-shiftit)
- [ShiftItをHammerspoonによって代替する](https://qiita.com/kabosu3d/items/66f6727f1140497b2cf7)


#### インストール
```
brew install hammerspoon --cask
```

#### 画面分割設定

##### ステップ1 Preferencesの設定  
- Accessibilityが有効になっていることを確認します。  
- - (無効の場合)Appleの設定でHammerspoonに「アクセシビリティ」権限を付与します。

##### ステップ2 init.luaの設定

- Hammerspoonメニューバーアイコンをクリックし、[Open Config]をクリックします。init.luaファイルが開かれます。

今回は、下記の四つをのショートカットを設定します。

- ctrl(^) + cmd(⌘) + left
    - ウィンドウを画面の左半分
- ctrl(^) + cmd(⌘) + right
    - ウィンドウを画面の右半分
- ctrl(^) + cmd(⌘) + up
    - ウィンドウを画面の上半分
- ctrl(^) + cmd(⌘) + down
    - ウィンドウを画面の下半分 


``` init.lua
hs.window.animationDuration = 0
units = {
  right50       = { x = 0.50, y = 0.00, w = 0.50, h = 1.00 },
  left50        = { x = 0.00, y = 0.00, w = 0.50, h = 1.00 },
  top50         = { x = 0.00, y = 0.00, w = 1.00, h = 0.50 },
  bot50         = { x = 0.00, y = 0.50, w = 1.00, h = 0.50 }
}

mash = { 'ctrl', 'command' }
hs.hotkey.bind(mash, 'right', function() hs.window.focusedWindow():move(units.right50, nil, true) end)
hs.hotkey.bind(mash, 'left', function() hs.window.focusedWindow():move(units.left50, nil, true) end)
hs.hotkey.bind(mash, 'up', function() hs.window.focusedWindow():move(units.top50, nil, true) end)
hs.hotkey.bind(mash, 'down', function() hs.window.focusedWindow():move(units.bot50, nil, true) end)
```


##### ステップ3 設定のリロード  
- Hammerspoonメニューバーアイコンをもう一度クリックし、[Reload Config]をクリックします。


## 結論

Magnetなどの有料appを使用せず、Hammerspoonを導入すれば上下の画面分割などが楽に行えるようになりました。