OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonのprintいろいろ

# この記事の目的
Pythonの出力 `print` を使いこなすためのメモ。文字色を変えたり、既に出力した文字に上書きをしたり、printで出力されるものをテキストファイルに書き出したり。1つ1つは既に紹介されているものでしたが、自分なりにコードを直してまとめてみました。


## 文字色
エスケープシーケンスを直前に出力することで、それ以降の文字色を変更することができるようです。そのため毎回最後にリセットするための`"\033[0m"`も出力しています。

```python
color_dic = {"black":"\033[30m", "red":"\033[31m", "green":"\033[32m", "yellow":"\033[33m", "blue":"\033[34m", "end":"\033[0m"}
def print_color(text, color="red"):
    print(color_dic[color] + text + color_dic["end"])
```

```python
text = "青い文字で出力"
print_color(text, "blue")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/d8f79a0f-f2ee-ea49-4db8-7c7cb26d25cf.png)


## 上書き

普通に `print(i)` を回していると1,2,3...が縦長になってしまうので結果が見づらくなりますが、これならスペースを取らないので見やすそうです。

```python
import sys

for i in range(1000):
    sys.stdout.write("\r{}".format(i))
    sys.stdout.flush()
```
![overwrite.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/81232f12-561d-0a3a-8945-d11d96c05b9a.gif)


上の`\r` を除くと改行せず出力を続けていくことができます。



```python
import sys

for i in range(10):
    sys.stdout.write("{}".format(i))
    sys.stdout.flush()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/5ec69360-4353-1a86-a183-7690c9569d7d.png)


ただし、これは`print` の`end` を指定することによっても実現できます。

```python
for i in range(10):
    print(i, end="")
```


## 外部書き出し

パッケージを使っていると様々な結果がprintで書き出されることがあると思います。これらをテキスト形式で外部に出力するための設定です。自分で書いたコードであれば`print` のオプション `file` を指定すれば良いですが、それが出来ない場合の力技です。

```python
# printをテキストに書き出す設定
temp_sysout = sys.stdout
sys.stdout = open("test.txt","w")

# メインのプリントする関数など
print("hello world")

# もとに戻す
sys.stdout = temp_sysout
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540956/ee7aca9c-0602-8d0c-d400-3ccf80a0371b.png)


# 参考
Qiita：[コンソールに色付きの出力をする方法](https://qiita.com/ironguy/items/8fb3ddadb3c4c986496d)
Qiita：[コンソールへの出力を上書きしてゆく方法](https://qiita.com/airtoxin/items/bb445529c94d3cd871f3)
Hatena：[pythonで標準出力されたデータを取得する](https://alicehimmel.hatenadiary.org/entry/20110213/1297611415)
Githubに保存したHTMLテンプレートを開発ごとにcloneして複製利用したい

## やりたいこと

ウェブサイトを制作するとき、毎回ファイルを一から準備するのは面倒です。

そこで、githubにHTMLテンプレートのリポジトリを作っておき、プロジェクトのごとにgit cloneして新しいリモートリポジトリに複製利用できたら便利だなと思ったのでメモします。

なお、エディタはVSCodeを使用しています。

## 手順

大まかな流れです。

![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183668/8610939a-a857-520c-d348-f515ff131f6a.png)

1. HTMLテンプレートリポジトリを準備
2. git cloneでローカルにコピー
3. Githubに新規プロジェクトリポジトリを作り、URLをコピー
4. git remote set-url originでローカルにコピーしたリポジトリのremote URLを変更
5. 新規プロジェクトリポジトリにpush

↓↓↓

githubにHTMLテンプレートリポジトリを準備し、今後これを使いまわすこととします。

![2021-03-22_04h10_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183668/609e6ebc-3dc2-8678-f13e-579b2e1727f4.png)

中身はこんな感じでVSCode用にGulpでタスクを組んでいます。

![2021-03-22_04h10_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183668/265f0c59-9427-6564-18dd-5b04744ab3f1.png)

ターミナルでHTMLテンプレートをgit cloneします。

```bash
$ git clone <remote URL>
```

githubで新規プロジェクトのリモートリポジトリを作り、リモートURLをコピーします。

![2021-03-22_04h36_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183668/c7b2a25a-3433-03b4-4c3b-b2490a3f0015.png)

ローカルリポジトリのリモートURLを変更します。

```bash
$ git remote set-url origin <new remote URL>
```

以下のコマンドでリモートリポジトリの名前とURLが変わっているのが確認できれば成功です。

```bash
$ git remote -v
````

pushして新しいリモートリポジトリを更新しましょう。

```bash
$ git push -u origin main
````
Java学習　基礎編　mainメソッドとは？

Java学習の備忘録になります。
内容は初心者なので基礎的な部分となります。
Javaに興味がある方やJava学習初心者の参考になれば幸いです。

* * *
#### mainメソッドとは？

Javaのmainメソッドとは、**Javaで処理を実行するときの入り口となるもの**。
いわゆる**エントリーポイント**。
(コンピュータプログラムを実行する際に、一番最初に実行することになっている箇所のこと)
mainメソッドに書かれた処理からJavaの実行がはじまる。

* * *
###### さらに詳しく見ていくと

一般的なプログラミング言語では、プログラムの実行が、どこから・どのようにはじまるかは、大きく以下の2種類にわかれる。

**1.プログラミング言語の仕様で決められた、関数・プロシージャ・メソッドからはじまる**
(C言語、C#、Go、kotlinなど)

**2.プログラムの先頭行から最後の行まで順番に実行されていく**
(Perl、Ruby、Python、PHPなど)

Javaは、上記1ののプログラミング言語になる。
この仕様で決められた関数・プロシージャ・メソッドをエントリーポイント(entrypoint)とも呼び、取り得る名前や引数、修飾子などが決められている。

そして、Javaでは、**mainメソッド**がエントリーポイントとなる。

#### mainメソッドの形式

Javaのmainメソッドは、以下の条件を満たしているメソッドになる。

・アクセス修飾子は publicである
・static メソッドである
・メソッドの戻り値は voidである
・メソッド名は mainである ( すべて小文字 )
・メソッドの引数は Stringの配列 (あるいはStringの可変長引数 ( 引数の数が決まっていない引数のこと ) ) のみである
※引数の変数名はなんでも良い

また、mainメソッドとは別のことだが、Javaでのメソッドは、必ず何かのクラスなどに属していなければいけない。

mainメソッドを持つクラスは、普通は以下のようになる。
※変数名**args**は、引数を意味する英単語**arguments**を省略したもの

```Main.java
class Main{
  // mainメソッドの基本形
  public static void main(String[] args) {
  // 処理
  }
}
```

Javaのプログラムを実行するときは、実行するクラスを指定する。
その際、指定されたクラスにmainメソッドがあるかをJavaが調べて、あればそこから実行をはじめる。
指定されたクラスにmainメソッドがなければエラーになる。

* * *
#### おわりに
もっと奥が深い部分なんですが、今回は基礎編なので基本的な部分のみ記載してます。
なお、ご指摘などございましたら、遠慮なくお願いいたします！！
Unity + PlayFab Party を使う時の注意点

# はじめに

PlayFab Party を導入する際に注意しないといけない内容をまとめておきたいと思います。

※ちなみに Mac 用の dll が用意されていないため Mac 環境の Unity エディターでは使用することが出来ません。

# Unity のセットアップ
## Unity プラグインのインポート

https://github.com/playfab/PlayFabPartyUnity

上記から最新版の unitypackage をダウンロードしてインポートします。

※ここで注意しないといけないのは PlayFab Party のパッケージ内に PlayFab SDK も含まれています。もし、PlayFab Party をインポートする前に最新の PlayFab SDK をインポートしている場合は PlayFab Party の方に含まれる PlayFab SDK の方が古い場合がありますので、上書きする際には注意してください。

## Unity 側の設定

Unity で使用するには『Allow 'unsafe' Code』を有効にする必要があります。

![スクリーンショット 2021-03-21 13.17.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/f8500a3f-7173-6e4e-2916-1f07e1c09951.png)

## Android 用の設定

Android のビルドの際に以下のようなエラーが表示される事があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/b3f064b8-8ea3-4b76-831e-813576ee1b4f.png)

これは PlayFab Party をインポートすると Custrom Main Gradle Templete が有効になりますが、使用している Unity のバージョンによっては mainTemplete.gradle の設定が合っていないため発生します。
これを解消するには Assets/Plugins/Android/mainTemplete.gradle を以下のように書き換えます。

```gradle
aaptOptions {
    // この行を追加
    noCompress = ['.ress', '.resource', '.obb'] + unityStreamingAssets.tokenize(', ')
    ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
}**PACKAGING_OPTIONS**
```

また以下のエラーが発生する場合は gradle.properties の更新が必要になります。

```shell
FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':launcher:lintVitalRelease'.
> This project uses AndroidX dependencies, but the 'android.useAndroidX' property is not enabled. Set this property to true in the gradle.properties file and retry.
```

Gradle ファイルを出力の際に AndroidX と Jetifier を有効にする設定を gradle.properties に追加します。
このエラーが発生するのは PlayFab Party 以外のプラグインで gradle.properties を出力しているのが原因です。

```C#
public int callbackOrder
{
    // 最後に実行されるようにする
    get => 999;
}

// IPostGenerateGradleAndroidProject を継承したクラスに実装する
public void OnPostGenerateGradleAndroidProject(string path)
{
    // path には gradle の出力先が渡されてくるが gradle.properties はその上の階層に配置されている
    using (var stream = File.AppendText(Path.Combine(path, "..", "gradle.properties"))) {
        stream.WriteLine("");
        stream.WriteLine("android.useAndroidX=true");
        stream.WriteLine("android.enableJetifier=true");
    }
}
```

## iOS 用の設定（追記）

PlayFab Party ではデフォルトでボイスチャットが有効になっているため、Unity の ProjectSettings の **Microphone Usage Description** に説明文を入れておかないと実行時にクラッシュします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19224/04a40fed-3d90-f01c-6800-8821a9f51227.png)

# おわりに

導入の際にはこれぐらいの問題点ですぐにマルチプレイが実装できますので一度 PlayFab Party の導入を検討してみてはいかがでしょうか？
SESに転職してみて

人工知能を開発するエンジニアを夢見てSESに転職してみた男の感想と理想と現実の駆け離れた生活を書いていこうと思います。

まず大前提として
『SES』に転職している時点でそんなキャリアは築けないだろ
というご意見はごもっともであり分かりきっている意見は置いておきます。


実際にプログラミングが流行しSNSでも初学者が溢れている現状で
『SESはやめておけ』等の意見は至る所で飛び交っています。

プログラミング学習のスクールに関しても様々な意見が飛び交っています。
では実際SESに転職してどうだったか。

業界に飛び込む人向けに少し書いてみようと思い今に至ります。


さて、現状を少し書いていくと
・2020年8月入社
・現場は2箇所目であること
・2箇所ともテスト案件であること
・コーディングをする案件には一度も案内されていない
・自分で案件を取り経験を積むために独学中
こんな感じになっています。


様々な求人案内をみた上で現在の企業に所属することになりましたが
どの求人をみても基本的には『1〜2ヶ月の研修』『資格取得で実力をつける』等の謳い文句がありました。

大体がJavaの資格を取ることを目標に研修を受けていく感じだと思います。
これはJavanを使った案件が古い体質の企業に多い印象があり、システムが古くから存在しておりJava等の古い言語を用いられている。
こういった状況だからだと感じています。

とはいえこんな情報はすでに既出のものであり使い古されたセリフだと思いますので・・・


声を大にして言いたいことは
とりあえず実務経験をつけるために転職することはしないほうが良いということです。
基本的にはプログラマーというか開発者というか、そういった案件は基本的には初学者の方には舞い込んで来ないというのが現実です。
案件ガチャという言い方も世の中的にはあるのかもしれませんがそもそもが『開発』というレアキャラが実装されていないガチャを引くという認識でいると困惑しないで済むかと思われます

『実務経験をつけるためにまずはテスト案件を・・・』

そんな感じのことを言われます。
確かにIT業界における実務経験はつくのかもしれませんがあくまで『IT業界』での実務経験でしかなく、
『開発』の経験はつかないということですね。

転職サイトをみても『◯◯での開発経験が◯年』などをみたことがあるかと思います。
これはSESの企業内に入ったとしても同様であることを忘れてはいけません。

テスト案件をいくらやったとしてもこれは開発の経験がつくわけではないので『◯◯での開発経験が◯年』を満たすことはできません。
困ったことにその辺を明確に教えられることなく研修を受け資格を取得させられ個別面談で『まずは実務経験を・・・』とされてしまいます。

とは言えテスト案件で何も学ぶことがないかと言われると・・・
実際はそうでもありません。

自分自身が経験した2箇所は受託されたものをテストするといった内容でした。
受託案件だと非常に立場が弱いと言いますか、理不尽に相手先からファイルの提出が遅れた場合でもこちらは残業して業務をする。
当たり前のことだと言われればそうなのかもしれませんが今までそういった業務に就いたことがない人からすれば非常に理解し難い状況でした。

さらに言えば人間がやる必要ある？
という内容のテストもあるため無駄に進んでいく8時間・・・
なんてことを思う人も多くいるでしょう。

裁量権などもほとんどないため身体を壊す可能性も多いにあると思います。
テスト業務が大切な業務だとういうことは重々承知の上でのことですが。

SESのメリットとしてあと思いつくのは、いろんな意味でいろんな人に会えること。
そして古い体質の企業がまだこんなにあるのかと思い知ることができます。

今、夢をみて独学をしている方や転職活動をしている方も多いと思います。
悪いことは言いませんのでしっかりと勉強して自分が思った通りの働きかたをできる企業に転職するようにしてください。
formの値をコントローラに送る 基本

####フォームから送られた値を受け取るには
######ルーティングの修正
`
post "move" => "users#move"
`
#####アクションの追加
`
def move

end
`
######erbにフォームの送信先を修正
`
<%= form_tag("/move") do %> 
  <p>メールアドレス</p>
  <input name="email">
  <p>パスワード</p>
  <input type="password" name="password">
  <input type="submit" value="移動">
<%= end %>`
link_to form_tag form_for の違いというか使い方

####link_to 
getのコントローラーを探しに行く

```
<%= link_to("移動", "/move") %>　→　get "move" => "users#move_form"
```
 
postのコントローラーを探しに行く

```
<%= link_to("移動","/move/#{@move.id}/destroy", {method: "post"}) %>
```


####form_tag 
postのコントローラーを探しに行く

```
<%= form_tag("/move") do %>　→　post "move" => "users#move"
```

####form_for
```

```
[UE4] アセットの追加/除外する設定と機能

検証Ver：4.26.0

# 概要
特定のアセットを一部のプラットフォームで使用したりパッケージから除外したいケースがあります。
これらを実現するための機能がいくつかあるため紹介します。
自身のプロジェクトにあわせてご活用ください。

# Project Settings
設定で可能な内容と影響する動作は以下の通りです。

## DirectoriesToAlwaysCook (Additional Asset Directories to Cook)
常にクック対象としたいファイルを指定したい時に利用します。

```:
[詳細]
・指定したディレクトリ配下にあるアセット(.uasset)をクック対象＋.pakに含める対象とします
・特定のマップのみをクックするようなケースにおいても、必ずクック対象のアセットとして含めることができます
・DefaultGame.ini+各プラットフォームのGame.iniの設定内容が反映される動作ではなく、現状はDefaultGame.ini+WindowsGame.iniの設定内容が適用されます (不具合)
```

以下は設定の追加例ですが、追加/削除することも可能です。

```:
[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToAlwaysCook=(Path="/Game/Texture")
```

## DirectoriesToNeverCook (Directories to never cook)
常にクック対象から除外したいファイルを指定したい時に利用します。

```:
[詳細]
・指定したディレクトリ配下にあるアセットはクックから除外され.pakからも除外されます
・DefaultGame.ini+各プラットフォームのGame.iniの設定内容が反映される動作ではなく、現状はDefaultGame.ini+WindowsGame.iniの設定内容が適用されます (不具合)
```
以下は設定の追加例ですが、追加/削除することも可能です。

```:
[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToNeverCook=(Path="/Game/Maps/Never")
```

## DirectoriesToAlwaysStageAsUFS (Additional Non-Asset Directories to Package)
.pakに意図的にファイルを格納したい場合に指定する際に利用します。

```:
[詳細]
・指定したディレクトリ配下にあるファイルは.pakに格納されずにクックしてコピーします
・プラグインに含まれる外部のコンテンツや、非アセットファイルを.pakファイルに含めることができます
・各プラットフォームのGame.iniで設定を追加/削除することが可能です
```

以下は設定の追加例ですが、追加/削除することも可能です。

```:
[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToAlwaysStageAsUFS=(Path="AlwaysCooked")
```

## DirectoriesToAlwaysStageAsNonUFS (Additional Non-Asset Directories To Copy)
.pakファイルから意図的に抜き出したいファイルを個別に抽出する際に利用します。

```:
[詳細]
・指定したディレクトリ配下にあるファイルは.pakに格納されずにコピーします
・ここで指定されない非アセットファイルはコピーされずにパッケージ構成に含まれません
・各プラットフォームのGame.iniで設定を追加/削除することが可能です
```

以下は設定の追加例ですが、追加/削除することも可能です。

```:
[/Script/UnrealEd.ProjectPackagingSettings]
+DirectoriesToAlwaysStageAsNonUFS=(Path="Packaged")
```

# PakBlacklist
.pakファイルから指定のアセット、ディレクトリを除外する時に利用します。

```
[詳細]
・これはCook->Staging->Packagingのフローの最後で.pakファイルを生成するフェーズで機能します
・プラットフォーム, ビルド構成を指定して除外する対象を設定ことができます
・UE4.25では非推奨(Duplicated)ですが使用することはできます
```

以下のドキュメントやブログでも説明しています。
[公式ドキュメント：ブラックリストをパッケージ化する](https://docs.unrealengine.com/ja/TestingAndOptimization/PerformanceAndProfiling/ReducingPackageSize/index.html)
[[UE4] PakBlacklistの使い方メモ](https://qiita.com/donbutsu17/items/ab2022b218cec6b30767)

#PakFileRule
.pakファイルから指定のアセット、ディレクトリを除外する時に利用します。

```:
[詳細]
・Config/DefaultPakFileRule.iniに.pakファイルに含めるファイルを制限できます
・.pakファイルに対して格納しないファイルをチャンク毎に指定することができます
・これはCook->Staging->Packagingのフローの最後で.pakファイルを生成するフェーズで機能します
・プラットフォーム毎の指定を追加することが可能です
```

以下のドキュメントやブログでも説明しています。
[[UE4] uasset以外のアセットにChunkIDを設定する(ue4.22以降)](https://qiita.com/EGJ-Takashi_Suzuki/items/d7e980d7a71e9adb6857)
discordjs/rpcを使ってPCのDiscordをスマホからミュートにしたい

## 動機
コロナ渦の大学生なので，ずっと家にいて暇です．常に仲間とDiscordのVCをつないで作業をしています．

ふとトイレに行くときに，PCのマイクをミュートにし忘れることがあります．
イヤホンはBluetoothなので音は聞こえているのですが，トイレとかキッチンに行った時とかにミュートにし忘れることがあって不便です．
そこで，離れたところからスマホでPCのDiscordをミュートにできたらと思っていたのですが，DiscordのRPCなAPIで実現できそうだったので試してみました．

## 環境
- ThinkPad X270
- Arch Linux
- Node.js v14.16.0
- [discord-rpc](https://github.com/discordjs/RPC) v3.2.0
- express
- Discord

Discordのアプリを，Node.jsで動くエージェントサーバーで操作する感じです．
Node.jsのサーバー上で，ExpressによるWebサーバーとDiscordRPCクライアントが走っています．
Webサーバーがリクエストを受け取ったらDiscordのミュートをトグルする，といった形です．

## 準備

Discordの
- ClientID
- ClientSecret
- AccessToken

が必要になります．
AccessTokenのScopesについては，私の場合は，
- `rpc`
- `rpc.api`
- `identify`
のトークンを発行しました．

## ソース
```Javascript
const DiscordRPC = require('discord-rpc')
const express = require('express')

const clientId = 'CLIENT_ID'
const clientSecret = 'CLIENT_SECRET'

if(process.argv.length <= 2) {
  console.error('You must specify access_token')
  return
}
const accessToken = process.argv[2]

const rpc = new DiscordRPC.Client({ transport: 'ipc' });

function startWebServer() {
  const app = express()
  const port = 3000

  app.get('/', (req, res) => {
    rpc.getVoiceSettings().then((e) => {
      return rpc.setVoiceSettings({mute: !e.mute})
    }).then(() => {
      res.sendStatus(200)
    })
  })

  app.listen(port, '0.0.0.0', () => {
    console.log(`Discord Muter listening at http://localhost:${port}`)
  })

}

rpc.on('ready', () => {
  startWebServer()
});

rpc.login({
  clientId,
  scopes: ['rpc', 'rpc.api', 'identify'],
  accessToken
}).catch(console.error);
```

## やってみよう
Discordのアプリを開いて，ボイスチャットに入っておきます．
`$ node index.js {アクセストークン}`でサーバーを起動します．
その状態で，普通に，PCのブラウザから，`http://localhost:3000/`にアクセスすると，ミュートになります．
もう一度アクセスでミュート解除です．

私はTaskerでウィジェット化しています．

他にもRPCにはいろいろなAPIが用意されていそうなので，時間があるときに探してみたいと思います！！
【Flutter】画像のダウンロード表示・http通信でエラー（Connection closed before full header was received）

画像のダウンロード表示で発生したエラーについて調査しました。

```error
Connection closed before full header was received
```

## 遭遇した具体的場面
[cached_network_image](https://pub.dev/packages/cached_network_image)を使用してURLを元に画像を表示する時に、正常に画像が表示されないことがありました。
これは`cached_network_image`のサンプルコードです。URLを元に画像をダウンロードしてキャッシュしています。
同じURLでも発生する時と発生しない時があり、発生頻度は30回に1回くらいでした。

```sampleCode.dart
CachedNetworkImage(
        imageUrl: "http://via.placeholder.com/350x150",
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
     ),
```

どうやら画像だけでなくhttp通信でも同様の問題が発生しているという話もちらほらと見受けられました。（原因が同じかは不明ですが）

## 原因・解決
結論からいうと、Flutterの問題のようで解決策はありませんでした。
[FlutterのGitHubスレッド](https://github.com/flutter/flutter/issues/41573)
[cached_network_imageのGitHubスレッド](https://github.com/Baseflow/flutter_cached_network_image/issues/443)

中の人解決頑張れ〜〜〜〜
UISceneDelegateをつかってUniversal Linkからの起動を制御する

# 概要
iOS13以降から`UISceneDelegate`が導入されたのでUniversal Linkからアプリが起動された挙動も`UISceneDelegate`のライフサイクルで制御します。
遷移したURLによってiOS側で制御を行いたいと思いますので、そちらを試した結果を記します。

# 実装
Universal Linkを利用するための`apple-app-site-association`の設置や`Associated Domains`の設定は完了しているものとします。
XCode11以降で作成されたプロジェクトには`SceneDelegate.swift`が含まれていてこちらにてライフサイクルの制御が管理されています。

Universal LinkなどをつかってSafariなどから遷移してきたときは`scene(_:continue:)`というDelegateメソッドで補足されます。
https://developer.apple.com/documentation/uikit/uiscenedelegate/3238056-scene

`NSUserActivity`に`webpageURL`というプロパティが存在するのでこちらでURLを取得しURLによって表示する画面などを決めて遷移できます。

以下は`hogehoge`というパスのURLでアプリに遷移した際に`HogeHogeViewController`をモーダル表示するときの実装です。

```swift:SceneDelegate.swift
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb else { return }
        guard let url = userActivity.webpageURL else { return }
        if url.scheme == "https" && url.host == "example.jp" && url.path == "hogehoge" {
            window?.rootViewController?.present(HogeHogeViewController(), animated: true, completion: nil)
        }
    }
```

## 問題
上記の実装でUniversal Linkから遷移を制御できるのですが、こちらはアプリがバックグラウンドで起動中しか保続できません。
新規にアプリが起動するときを制御するには`scene(_:willConnectTo:options:)`にて同様の処理を用意する必要があります。

```swift:SceneDelegate.swift
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // URLが存在するものを補足
        guard let userActivity = connectionOptions.userActivities.first(where: { $0.webpageURL != nil }) else { return }

        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb else { return }
        if url.scheme == "https" && url.host == "example.jp" && url.path == "hogehoge" {
            window?.rootViewController?.present(HogeHogeViewController(), animated: true, completion: nil)
        }
    }
```

こちらでアプリが起動していない場合でも画面の制御を行うことができるようになります。

# あとがき
`scene(_:continue:)`だけだと未起動時の制御を補足できないですが、起動していないので実装時に見落としてしまいがちなのとデバッグがしにくく、ハマってしまうこともあるかとも思います。
こちらが役に立てば。
【Rails】pluckメソッドのソースコードを読んでみた

アプリケーション作成時に使用した「pluck」メソッドについて、便利な機能だと思っていましたが、どのように処理をしているのか気になったのでRailsのソースコードを読んでみました。



## **pluck**とは
公式ドキュメントでは以下のように記載されています。

	pluckは、1つのモデルで使用されているテーブルからカラム (1つでも複数でも可) を取得するクエリを送信するのに使用できます。引数としてカラム名のリストを与えると、指定したカラムの値の配列を、対応するデータ型で返します。

例えば、Productモデルがあったとして、nameカラムの中身を確認したい場合は、

```ruby
Product.pluck(:name)
=> ["Ruby on Rails Tote",
 "Ruby on Rails Bag",
 "Ruby on Rails Baseball Jersey",
 "Ruby on Rails Jr. Spaghetti",
 "Ruby on Rails Ringer T-Shirt",
 "Ruby Baseball Jersey",
 "Apache Baseball Jersey",
 "Ruby on Rails Mug",
 "Ruby on Rails Stein"]
```
といったようにProductモデル内のnameカラムを一覧表示することができます。

```ruby
Product.pluck(:id,:name)
=> [[1, "Ruby on Rails Tote"],
 [2, "Ruby on Rails Bag"],
 [3, "Ruby on Rails Baseball Jersey"],
 [4, "Ruby on Rails Jr. Spaghetti"],
 [5, "Ruby on Rails Ringer T-Shirt"],
 [6, "Ruby Baseball Jersey"],
 [7, "Apache Baseball Jersey"],
 [8, "Ruby on Rails Mug"],
 [9, "Ruby on Rails Stein"]]
```
このように第二引数を設定することも可能です。

**pluck**の処理内容と、第二引数を設定した場合の処理がどのように行われているか気になったので、ソースコードを見てみることにしました。

## Railsのソースコード

```ruby:rails/activesupport/lib/active_support/core_ext/enumerable.rb
def pluck(*keys)  
  if keys.many?  
    map { |element| keys.map { |key| element[key] } }  
  else  
  key = keys.first  
    map { |element| element[key] }  
  end  
end
```

### 実際に読んでみる①

まず、引数が一つのケースで考えてみます。

```ruby
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)  
# => ["David", "Rafael", "Aaron"]
```
```ruby
def pluck(*keys)  
  if keys.many?  
    map { |element| keys.map { |key| element[key] } }  
  else  
  key = keys.first  
    map { |element| element[key] }  
  end  
end
```
一行目
(*keys)に、:nameが配列として格納されます。

	pluck(*keys)：　*をつければ引数を複数個設定できる。(可変長引数)  引数は配列として受け取られる。

二行目
**keys.many?** → 今回、keysは一つだけなのでスルー

	many?　→　条件を満たす要素が 2 つ以上ある場合に true。

五行目
keysの配列で、最初の要素（:name）を、**key**に格納

六行目

	[{ name:  "David"  },  { name:  "Rafael"  },  { name:  "Aaron"  }]
	
上記の配列をmapメソッドでそれぞれ、key に関連づけられた値を抽出。その後、配列として出力した結果、

	=> ["David", "Rafael", "Aaron"]
nameカラムの要素のみ表示できました。

### 実際に読んでみる②

次に、引数が２つ以上のケースで考えてみます。

```ruby
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)  
# => [[1, "David"], [2, "Rafael"]]
```
```ruby
def pluck(*keys)  
  if keys.many?  
    map { |element| keys.map { |key| element[key] } }  
  else  
  key = keys.first  
    map { |element| element[key] }  
  end  
end
```
一行目
(*keys)に、:id, :name が配列として格納されます。

二行目
keysが、２つ以上なのでそのまま進みます。

三行目

	[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }]

まず、上記の配列からmapメソッドで、keys → **(:id, :name)** に関連づけられた要素を抽出。
そこからさらに、mapメソッドを使って配列の要素それぞれに、上記の処理を行う。

その後、配列として出力した結果、

	=> [[1, "David"], [2, "Rafael"]]

idカラムとnameカラムの要素を表示することができました。

##終わりに
ソースコードのどこを読めばいいのかとか、おろそかにしてきた基礎文法の知識の足りなさが露呈したりと、たったこれを読むだけでもかなり苦戦しました。
ただ、今まで公式ドキュメント読んでも、「...？」だった内容が事細かに書いてある（当たり前）ので、ソースコードで確認することで、どういう仕組みか納得して進められるなと思った次第です。
これからも、誰かのソースコードを読むことは必須になってくるので、気になったメソッドとかがあったらソースコードを見てみる癖を付けておきたいなと思うのでした。
Self-HostedなGitLabへSSH接続可能にする

# 目的
自前のVPSなどへSelf-HostedされたGitLabにSSHで接続したい。
手順の殆どはGitHub.comと同じです。


# 現在のSSH鍵の状況を確認

```shell-session
$ ls -la ~/.ssh
... config
... id_ed25519_github
... id_ed25519_github.pub
... known_hosts
```

これらと被らないように鍵名をつける

# 鍵作成
今回はRSAで作成

```shell-session
$ cd ~/.ssh
$ ssh-keygen -t rsa -b 4096 -C "your_email_address@example.com" -f "ssh-key_filename"
```

例えば

```shell-session
$ ssh-keygen -t rsa -b 4096 -C hoge@fuga.com -f id_rsa_gitlab_yourname

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_gitlab_username
Your public key has been saved in id_rsa_gitlab_username.pub
The key fingerprint is:
SHA256:7RhE9TbDGMfyTWQ1H4IpmiMQp/mr7GM+YjfGIAr9qDk hoge@fuga.com
The key's randomart image is:
+---[RSA 4096]----+
|   ...  ..o.+oo+.|
|   .+  . ..O..o +|
|   o.   + oo*o  .|
|    .. = . ..o.  |
| .   .. S .      |
|o..   .  +       |
|+ oo .  . .      |
|E+oOo            |
|+oB=+            |
+----[SHA256]-----+

```

# ssh configの編集

```shell-session
$ vim ~/.ssh/config
```

SSH鍵のファイル名をデフォルトから変更しているので、
`~/.ssh/config` にて下記のように記載

```config
Host your-host-name.com
  HostName your-host-name.com
  IdentityFile ~/.ssh/id_rsa_gitlab_yourname
  User git
```

# GitLab側で公開鍵を登録

GitLabでSSH接続したいユーザーでログイン

画面右上(?) の
Profile Settings => SSH Keys
と入っていく

サーバー内のSSH公開鍵をコピーしてGitLabのSSH鍵登録欄にペースト。
名前は適当につけておく。

```shell-session
$ xclip -selection c id_rsa_gitlab_yourname.pub
```

#接続テスト

GitLabに接続できているかチェック

```shell-session
$ ssh -T git@your-host-name.com
```
と入力して

```shell-session
Welcome to GitLab!
```

などと表示されれば接続が成功しているのでOK


#参考
- https://qiita.com/suthio/items/2760e4cff0e185fe2db9
CGI版PHPで動くディレクトリの下層でモジュール版PHPを動かす(.htacecss)

# 目的
CGI版のPHPが動いているディレクトリの下層でモジュール版PHPを動かしたい

# 環境
- CentOS7
- Apache/2.4.6 (CentOS)

# 対象

今、`/var/www/html`に以下のように書いているとして、
下記のように、例えば `/var/www/html`にはPHP5.4で動いてほしいアプリケーションがあるとする。

```shell-session:/var/www/html/.htaccess
Options +ExecCGI
FCGIWrapper /usr/bin/php54-cgi .php
<FilesMatch \.php$>
     SetHandler fcgid-script
</FilesMatch>
```

`/var/www/html/app01`に新しいプロジェクトを置いて、
モジュール版(ここではPHP7系)を動かしたい場合,

# .htaccess

```shell-session:/var/www/html/app01/.htaccess
<FilesMatch \.php$>
     SetHandler application/x-httpd-php
</FilesMatch>
```

# 結果
モジュール版PHPを動かすことができる。

# 参考

- https://qiita.com/SuguruOoki/items/269389f242bd1cd3facb
- https://qiita.com/ut_1029/items/51f9f95d00d960b91fe4
PHPアウトプット

<h1>エンジニアの卵です</h1>
昨年新卒で入社した会社を辞めてテックキャンプを卒業。（８４期生）
今年からIT会社でエンジニアとして働きはじめましたが、まだまだわからないことだらけなので、
Qiitaを通して自分の理解度の定着と他の方の記事をみて学んで行きたいなと思います。

今はプロゲートレベルですが、これから胸を貼ってエンジニアですと言えるようにがんばっていきます。


<h2>オブジェクト思考</h2>

オブジェクト思考とはオブジェクト（もの）を中心とした考え方で
ECサイトでいうところの商品やユーザー、商品を入れるカートなどの１つ１つを指します。

コードを書く際に商品についてのコード、ユーザーについてのコード、カートについてのコードなど分けて記し、またそれらを組み立てながら作成していくため、この考え方が必要になります。



<h3>クラスとインスタンス</h3>

クラスとはオブジェクトを生成するための設計図のようなものです。
クラスの中に商品についてやメニューについてなど書いていきます。

インスタンスとはクラスを元に生成された実体を言います。
インスタンスとオブジェクトは同じような意味です。

分かりやすくたこ焼きで例えると、
クラス　たこ焼き機
インスタンス　たこ焼きの生成
インスタンスに情報を追加する場合　ソースなど

を指します。
当時全く理解出来ませんでしたがこの例えを聞いて少し理解出来るようになりました。
分かりやすいものもあったので自分用に貼ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/783181/b95a014d-2e59-1f6c-631c-50e62488f1a1.png)


PHPのクラスとインスタンスの書き方は以下のようになります。

・クラス

<?php
class Menu{
}

「class クラス名」と定義して{}の中にクラスの内容を書いていきます。
クラス名の最初は大文字になります。



・インスタンス
$menu1 = new Menu():
$menu2 = new Menu();

インスタンスはnewを使って生成します。
$menuという変数に生成したインスタンスMenuを代入しています。
インスタンスはクラスの中では生成出来ません。



<h4>プロパティ</h4>

プロパティとメソッドはクラスの中に記述するものです。
プロパティはインスタンスが持つデータを言います。

例えばプロパティはインスタンスが持つデータなので名前や値段などです。


PHPのプロパティの書き方は以下のようになります。

<?php
 class Menu{
  public $name;

}

Munuクラス（設計図）の中に「public $プロパティ名」でプロパティを定義します。
今回は$nameプロパティを定義しました。
また「インスタンス->プロパティ名」斗することでインスタンスのプロパティにアクセスすることが出来ます。

例

 class Menu{
 public $name
}

$sushi =new Menu();
$sushi ->name ='SUSHI';
echo $sushi->name;

上記で起こっていることは以下になります。

Menuクラスを生成。
$name プロパティを定義。

Menuインスタンスを生成して$sushiに代入。
$sushiの値をSUSHIに設定。
（echo は出力する時に使うもの）
echo $sushi->name;の結果はSUSHIになる。




<h5>メソッド</h5>
メソッドもクラスの中に記述するもので、
それぞれのインスタンが持つ関数（処理するもの）です。

例えば、インスタンスの名前を取得したり注文数を取得したりします。
インスタンス固有の関数をメソッドと言います。


PHPのメソッドの書き方は以下のようになります。

class Menu{
 public function hello(){
 　echo '私はMenuインスタンスです'；
 }
}

$sushi =new Menu();
$sushi　-> hello();

上記で起こっていることは以下になります。

Menuクラスに「public function メソッド名()」でメソッドを定義。

Munuインスタンスを生成して$sushiに代入。
「インスタンス->メソッド名()」でメソッドにアクセスするため（呼び出している）
結果は私はMunuインスタンスですと表示される

$sushi　-> hello();が実行されたら
public function hello(){
 　echo '私はMenuインスタンスです'；を呼び出しているというイメージ。


自分のまとめとして投稿していますが、今後文章も上手くなれるように頑張ります。
また次回PHPの続きを投稿します。
無料で作るWebサービス Codelet 作成日記 #1 環境構築①

# Herokuを使う
英語っぽくない名前で覚えにくい！ Hero + Haiku(俳句)なんだって。知らんけど。
http://computerbecamecloud.blogspot.com/2013/12/engine-yardheroku-paas.html

この記事通りにやったらできるよ！
思った以上に簡単にできるよ！
https://qiita.com/sho7650/items/ebd87c5dc2c4c7abb8f0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/6295d0b6-b83d-5265-f5a8-39f878754105.png)

# 開発環境(Windows10)にPythonをインストールする
とりあえず開発環境で実行できないことには始まらない。
このサイトを参考に、雑にVSCodeにPython開発環境をインストールしてみる。
https://docs.microsoft.com/ja-jp/learn/modules/python-install-vscode/
何となくできた。

試しにHellow Worldする。
https://dianxnao.com/visual-studio-code%E3%81%A7python%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%92%E5%A7%8B%E3%82%81%E3%82%8B%EF%BC%88windows%E7%B7%A8%EF%BC%89/#toc7
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/9b2aafbe-0a76-b3ad-cc9b-a69d9c858af6.png)
できた。

# WebフレームワークFlaskをインストールする
これを試してみるが、PowerShellの設定で阻まれたっぽい。
https://www.atmarkit.co.jp/ait/articles/1807/24/news024.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/b4edadbd-7caf-ffe9-eb6e-437d00a392fc.png)

PowerShellの設定で、ローカルのスクリプトはすべて実行可能にした。
https://qiita.com/Targityen/items/3d2e0b5b0b7b04963750

とりあえずflaskのインストールはできたっぽい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/5c843d2f-2854-0f94-51a4-bc39e30702fc.png)

動いた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/b798873e-2cc4-1491-16c8-ce8433c519cc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170904/d2614b2b-9593-10c5-8000-b0a1bc7d9fa7.png)
ところで、Terminalの上の方で失敗したログが出てるけど、環境変数FLASK_APPに設定できるプログラムの入り口が「wsgi.py」または「app.py」のどちらかに限定されるって、仕様としてどうなの？
それならそもそも環境変数FLASK_APPって不要なんじゃないの？
まぁいいや。
小説家になろうのPython用非公式APIクライアントを作ってみた

# 成果物

https://github.com/wyokobe/narou_api

# 使い方

```bash
pip install -U narou-api
```

```python
import narou_api

r = narou_api.get(out="json", st=1, lim=500, order="quarterpoint")
print(r.text)
```

# 説明

[小説家になろう](https://syosetu.com/)は、日本最大級の小説投稿サイトです。
いわゆる「なろう系」と呼ばれている作品を数多く生み出してきた所でもあります。
それらの作品群を検索するのに便利なAPIが公式から提供されています。

https://dev.syosetu.com/man/api/

今回、そのAPIをpythonで簡単に実行できることを目的とした非公式APIクライアントを作成することにしました。
パッケージ化することで今後の開発の利便性の向上と、広く利用してもらうための周知、[PyPI](https://pypi.org/)を用いたプロジェクトのパッケージ化と配布方法の理解が本記事の目的です。

# 手順

こちらの方の記事が大変参考になりましたのでご紹介させていただきます。

https://qiita.com/propella/items/803923b2ff02482242cd

基本的にはこちらの記事をほぼほぼ参考にしてパッケージ化を実施しました。
それ以外の差異としては、コアとなるAPI実行機能でAPIの仕様変更があっても柔軟に対応できるように、GETパラメータを可変長引数で渡せるように実装したことと、それをCLIでも利用できるようにしたことです。

```python:__init__.py
import sys

import requests


def get(**kwargs):
    """
    なろう小説API
    @see https://dev.syosetu.com/man/api/
    """
    url = "https://api.syosetu.com/novelapi/api/"
    return exec_get(url, **kwargs)

"""
中略
"""

def exec_get(url, **kwargs):
    """共通GETエクゼキューター"""
    is_first_loop = True
    for k, v in kwargs.items():
        if is_first_loop:
            url += "?"
            is_first_loop = False
        else:
            url += "&"
        url += f"{k}={v}"

    return requests.get(url)


commands = locals()


def main() -> None:
    if sys.argv[1] in commands:
        func = commands[sys.argv[1]]
        r = func(**dict(arg.split('=') for arg in sys.argv[2:]))
        print(r.text)
```

（3/22追記:なろう小説API以外のAPIにも対応しました。詳細は[README](https://github.com/wyokobe/narou_api/blob/v1.0.0/README.md)を参照ください）

`locals()`を使うと、以下のような関数の名前と関数の辞書を取得できます。

```
{
  (略)
  'get': <function get at 0x10fa533a0>, 
  （略）
}
```

CLIの第一引数を取得する`sys.argv[1]`はString型として扱われるので、locals辞書と合わせることで、CLIで複数の関数をコマンドとして実行するように対応しました。

setup.pyの作成し、以下のコマンドでローカルインストールしたら、CLIで実行可能になります。

```
pip install -U -e .
```

実行コマンド例

```
python -m narou_api get lim=1 out=json
narou_api get lim=1 out=json
```

配布物を作成します。

```bash
pip install --upgrade pip setuptools wheel
python setup.py bdist_wheel
```

whlファイルが作成できたら、適当なプロジェクトにインストールしてテストします。

```
pip install ../narou_api/dist/narou_api-0.0.1-py3-none-any.whl
```

## プロジェクトをPyPIにアップロードする

配布物が作成できたことを確認できたら、以下を参考にPyPIにアップロードする準備をします。

https://packaging.python.org/guides/distributing-packages-using-setuptools/#uploading-your-project-to-pypi

まずは、[PyPI](https://pypi.org/) で[アカウントを作成](https://pypi.org/account/register/)します。
次に[APIトークン](https://pypi.org/help/#apitoken)を作成します。
トークン名はパッケージ名と同じ、スコープは「All Project」で問題ないです。
作成されたトークンはセキュリティの関係で一度しか表示されないので、必ず控えるようにしてください。
控えたトークンを元に以下のように.pypircを作成します。

```$HOME/.pypirc
[pypi]
username = __token__
password = <the token value, including the `pypi-` prefix>
```

これでアップロードできる準備が整いましたので、後は以下のコマンドを実行してアップロードします。

```
twine check dist/*
twine upload dist/*
```

成功するとこのような感じでパッケージが作成されます。

https://pypi.org/project/narou-api/

以上でパッケージの作成と配布は完了です。

意外とあっさりとパッケージが作成できたなという印象です。
これを使用することでpythonでも簡単になろう小説を検索できるようになります。
簡単なスクレイピングの実施などに是非ご活用ください。
AWS OpsWorks とは

## 勉強前イメージ

自動でインフラ作るイメージ

## 調査

### AWS OpsWorks とは

ChefやPuppet を使って運用を自動化する構成管理のサービスで
システムの構成などを一元的に設定することができます
EC2インスタンスやオンプレの環境でのサーバ設定やデプロイ、管理を自動化できます。
OpsWorks には3つの種類が存在します。

### ChefやPuppet とは？

- Chef

シェフ と読み、
ファイルに記述した設定内容に応じて自動的にユーザーの作成やパッケージのインストールを行う
サーバ管理ツールになります。

- Puppet

パペット と読み、
Chefと同様にサーバーの環境設定やインストールなどを自動化する設定管理ツールになります。

どちらも古くからあるansibleみたいなやつだなぁと思いました。

### AWS OpsWorks の3つの種類

- AWS OpsWorks スタック

サーバの管理サービスとAWSのアプリケーションの管理サービスで、
データベースやアプリケーションなどスタックとして作成し、EC2などを設定したり
他のAWSサービスに接続させることが出来ます。

- AWS OpsWorks for Chef Automate

Chef Automateのフルマネージド設定管理サービスで
すでにChefを利用している場合はこちらを選択します。
サーバー設定やバックアップが自動化され、サーバなど管理する必要がなくなります。

- AWS OpsWorks for Puppet Enterprise

Puppet Enterpriseのフルマネージド設定管理サービスで
すでにPuppetを利用している場合はこちらを選択します。
こちらもフルマネージドなのでサーバなど管理する必要がなくなります。

### AWS OpsWorks スタックの用語

- スタック
	- 全インスタンスの構成を管理します
	- リージョン間でもコピーが可能なので、リージョンごとにスタックを立てて同様のサーバを構築できます

![1Stack - My Sample Stack (Linux) – AWS OpsWorks - G.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/fbbc0ccf-a6fd-762b-92e7-3f938c006f09.png)


- レイヤー
	- どんなパッケージをインストールするかのレシピを指定します

![2Node.js App Server - Layers - My Sample Stack (Lin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/6838e27c-c218-b791-65cc-f350bcd1373e.png)


- App
	- アプリケーションサーバにデプロイされるもの

![3Apps - My Sample Stack (Linux) – AWS OpsWorks - Go.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/b0f659a2-83a9-3873-63ea-f83c1431a129.png)

## 勉強後イメージ

イメージとしては、
RDSのAuroraとmysqlとpostgresqlって感じ？
AWS独自と元々あるやつに適応したやつみたいな

## 参考

- [サーバー設定ツール「Chef」の概要と基礎的な使い方](https://knowledge.sakura.ad.jp/867/)
- [Puppetを使ったLinuxシステムの設定自動管理](https://knowledge.sakura.ad.jp/755/)
- [[AWSマイスターシリーズ] AWS OpsWorks](https://www.slideshare.net/AmazonWebServicesJapan/20131016-aws-meisterregenerateopsworks)
- [AWS OpsWorks](https://aws.amazon.com/jp/opsworks/)
Amazon FSx とは

## 勉強前イメージ

ファイルシステムな感じがする・・・ただのイメージだけど

## 調査

### Amazon FSx とは

フルマネージド型のファイルストレージサービスになります。
FSxにはファイルシステムのタイプが以下の2種類あります。

- Amazon FSx for Windows File Server
	- ビジネス向けで、Active Directoryとの連携が可能
	- SMBプロトコルを介してアクセス
- Amazon FSx for Lustre
	- 機械学習や動画処理など速度が必要な高性能ファイルシステム
	- ミリ秒未満のレイテンシーや最大1秒あたり数百ギガバイトのスループットで処理が可能

![1Amazon FSx - Google Chrome 2021-03-17 20.57.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/c96261f4-e29f-a07d-4570-591c46b7b962.png)

### Amazon FSx の特徴

- フルマネージド型

マネジメントコンソールからすぐに作成でき、
ストレージサービスの構築については考える必要がありません。

- 可用性・耐久性

FSx for Windows File Server ではシングルAZ,マルチAZの選択が可能で、
FSx for Lustre では、データに合わせてデプロイタイプとストレージタイプを選択することが出来ます。

### Amazon FSx for Windows File Server について

- Windows Serverに構築

Windows Server上に構築されたストレージで、下記の機能が提供されています。
- エンドユーザーファイルの復元
- ユーザークォータ
- アクセスコントロールリスト(ACL)

- 高速なパフォーマンス

ストレージオプションが存在し、SSD,HDDのどちらも選べるようになっています。
また、ファイルシステムのスループットレベルを選択していつでも変更できます。

- ユースケース
	- ホームディレクトリ
	- ウェブ配信、コンテンツ管理

### Amazon FSx for Lustre について

- 高性能

データ処理用に最適化されており、高いパフォーマンスが提供されている

- s3のデータとのリンク

バケットをリンクさせることで
Lustreからs3のデータにアクセスすることが出来ます。

- ユースケース
	- 機械学習
	- ビデオレンダリングなどメディア処理

## 勉強後イメージ

またストレージサービスきた・・・
いっぱいあるけど、イメージとしてはファイルサーバ的な立ち位置なのかな
またまとめないと。
windows file serverの記事いっぱいあるのに、Lustreは少ない気がする

## 参考

- [Amazon FSx](https://aws.amazon.com/jp/fsx/#:~:text=Amazon%20FSx%20%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0&text=Windows%20Server%20%E4%B8%8A%E3%81%AB%E6%A7%8B%E7%AF%89,%E6%A9%9F%E8%83%BD%E3%82%92%E6%8F%90%E4%BE%9B%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)
- [完全マネージド型のファイルストレージを提供するAmazon FSx for Windows File Server](https://www.acrovision.jp/service/aws/?p=1129)
WebAssembly (Wasm)の可能性 

# 書いたこと

Wasm側からブラウザに対してどういった処理が行えるのかサンプルをいくつか作ったのでご紹介します。

Wasmは、C++やGoなど各種の言語から生成することが出来ます。
今回は、Rustを使ってWasmを生成しています。

図: Rustを使ったWasmの生成と実行の流れ
![wasm-compile-baseline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/1f2a38fc-3ab8-8c46-bb8d-6275645428d5.png)

# WebAssembly(Wasm)について

ブラウザのためのアセンブリ言語

WebAssembly(Wasm)の場合は、構文を解析する必要がなく、そのまま機械語にコンパイルすることが可能になっています。
それによってデスクトップ上のアプリケーションと同様にCPU上でコードを実行させることができます。

||JavaScript|Wasm|
|:---|:---|:---|
|構文解析| あり | なし |
|コードの実行|インタプリタ上 (※1)|CPU上|

(※1 Chrome(V8)は、最適化可能なJSコードであれば、2回目以降、機械語にコンパイルされ、Wasmと同様にCPU上で実行されます。）

# 作成したWebアプリケーション

* Wasm側からのアラートの表示
* フィボナッチ計算
* 画像のエッジ抽出

## アラートの表示

https://angry-northcutt-a21c97.netlify.app/

![Screen Shot 2021-03-21 at 23.47.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/7b5ccfe3-81c1-8878-73e6-cffa5844050e.png)

## フィボナッチ計算

https://thirsty-lewin-0a44c0.netlify.app/

![Screen Shot 2021-03-21 at 23.48.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/83ff8461-1159-5358-2949-e66af73479f4.png)

## 画像のエッジ抽出

https://serene-williams-b63516.netlify.app/

![Screen Shot 2021-03-21 at 23.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/1ae6d33a-5209-cafc-87c4-6719301f4605.png)

# まとめ

Wasmを使うことで、画像処理やファイルのパースといった重たい処理を高速化するだけでなく、Wasm側からDOMを流し込むことも可能です。

# ソースコード

アラートの表示

https://github.com/yossan/calling-alert-from-wasm

フィボナッチ計算

https://github.com/yossan/Fibonacci-In-Wasm

エッジ抽出

https://github.com/yossan/derivative-filter-in-wasm
DigitalMicrographのColor Table作成

###Custom Color Table の作成
　DigitalMicrographにてモノクロ画像に色を付ける場合、右クリックメニューのImage infoからColorタブを選択し、Color Tableから好みのグラデーションを選択する。プリセットのColor Tableは正直今一つであると思う。モノクロ印刷したときに輝度値で強度が判定できるようなColor Tableを採用しているのであろうか。カラー印刷やディスプレイ上での見栄えは今一つ。そこで自分好みのColor Tableを作る。
参考：
https://stackoverflow.com/questions/25922047/how-to-save-customized-color-tables-in-digitalmicrograph-gms-2-x
　.dm3と.dm4の両方を「C:\Users\_USERNAME_\AppData\Local\Gatan\ColorTables\」に保存しないと Color Table に反映されないとあるが、GMS 3.xではバグは修正された？ただし.dm4では認識せず、.dm3を保存すると認識した。GMS3でも.dm3で保存する必要があるようだ。ColorTablesフォルダはインストール直後には存在せず、自分で作成する必要がある。Prefsフォルダがある階層。
　また、公開されているscriptではColorTableを編集するものがあるが、Color LookUp Table (CLUT)という名前がつけられているので検索時の参考まで。
http://www.dmscripting.com/applyclut.html

###RGBカラーのColor Tableを生成するscript
　デフォルトのColorTableにはRainbowがあり、光の波長の変化を表現するには最適。ただし、強度を表現する場合、色と強度の対応が直観的でない。強度が弱いときは青、強いときは赤としたいのだが、Rainbowでは逆である。そこで青から赤に連続的に変化するColor Tableを作成する。モノクロ印刷には向かない。
![rainbow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1216299/c38832fb-0ff5-805e-687c-252c8b141f65.png)
これはデフォルトで入っているRainbow Color Table

　上図のようにColor Tableは16x16のサイズを持つ256ピクセルの画像データである。最初に256階調の1次元データ(clut)を作成し、それを16x16(clutimg)に整形する。
######青-緑-赤のグラデーションのColor Table
```DigitalMicrograph:blue2red.s
//custom color lookup table
//blue-green-red
number x=256
image clut:=RGBImage("",4,x,1)
clut[0,0,1,x/4]=rgb(0,icol*4,x)
clut[0,x/4,1,x/2]=rgb(0,x,x-icol*4)
clut[0,x/2,1,x*(3/4)]=rgb(icol*4,x,0)
clut[0,x*(3/4),1,x]=rgb(x,x-icol*4,0)

number xx=16
image clutimg:=RGBImage("blue to red",4,xx,xx)
for(number i=0;i<xx;i++)
{
	clutimg[i,0,i+1,xx]=clut[0,16*i,1,xx*i+xx]
}
clutimg.showimage()
```
　生成した画像を.dm3形式でC:\Users\_USERNAME_\AppData\Local\Gatan\ColorTables\に保存すると、新しいColor Tableとして様々な画像のカラー化に適用することができる。
![blue to red.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1216299/5a82292f-c2f0-aae7-40fd-98ecd0824727.png)
######黒-青-緑-赤-白のグラデーションのColor Table
　青の前に黒を入れると、強度が弱いところを表現しやすい（気がする）。最大強度は赤でも良いが、紫-白とすることで、強度が飽和する感が出る（気がする）。これもモノクロ印刷には不向き。電子線マイクロアナライザーEPMAの元素マッピングデータにおいてよく使われるカラースケールと類似のものを目指した。

```DigitalMicrograph:blue2purple.s
//custom color lookup table
//blue-green-red-purple
//lowest: black, highest, white

number x=256
number n=7
image clut:=RGBImage("",4,x,1)
clut[0,0,1,x/n]=rgb(0,0,icol*n)
clut[0,x/n,1,2*x/n]=rgb(0,icol*n,x)
clut[0,2*x/n,1,3*x/n]=rgb(0,x,x-icol*n)
clut[0,3*x/n,1,4*x/n]=rgb(icol*n,x,0)
clut[0,4*x/n,1,5*x/n]=rgb(x,x-icol*n,0)
clut[0,5*x/n,1,6*x/n]=rgb(x,0,icol*n)
clut[0,6*x/n,1,7*x/n]=rgb(x,icol*n,x)

number xx=16
image clutimg:=RGBImage("blue to purple",4,xx,xx)
for(number i=0;i<xx;i++)
{
	clutimg[i,0,i+1,xx]=clut[0,16*i,1,xx*i+xx]
}
clutimg.showimage()
```

![blue to purple.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1216299/e15dd06e-a3c1-ef8e-320d-7796378a128d.png)

　TEMの電子回折図形はこれらのColor Tableで疑似的に着色すると、弱い回折斑点が見やすくなることがある。
pip uninstall時のUnicodeDecodeError対処法【venv環境】

# 環境
Windows 10
Python 3.8.5
pip 20.1.1
venv環境

# エラー内容
すでにインストールされていたnumpy 1.19.2を1.18.5にバージョンダウンしようとした時に，以下のUnicodeDecodeErrorが発生しました．
パスに日本語が含まれていたからでしょうか…？

```
(.venv) PS G:\研究\product> pip install numpy==1.18.5
Collecting numpy==1.18.5
  Using cached numpy-1.18.5-cp38-cp38-win_amd64.whl (12.8 MB)
Installing collected packages: numpy
  Attempting uninstall: numpy
    Found existing installation: numpy 1.19.2
ERROR: Exception:
Traceback (most recent call last):
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\cli\base_command.py", line 188, in _main
    status = self.run(options, args)
    return func(self, options, args)
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\commands\install.py", line 398, in run
    installed = install_given_reqs(
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\req\__init__.py", line 63, in install_given_reqs
    uninstalled_pathset = requirement.uninstall(
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\req\req_install.py", line 675, in uninstall
    uninstalled_pathset = UninstallPathSet.from_dist(dist)
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\req\req_uninstall.py", line 456, in from_dist
    if not dist_is_local(dist):
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\misc.py", line 383, in dist_is_local
    return is_local(dist_location(dist))
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\misc.py", line 529, in dist_location
    egg_link = egg_link_path(dist)
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\misc.py", line 505, in egg_link_path
    if not virtualenv_no_global() and user_site:
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\virtualenv.py", line 111, in virtualenv_no_global
    return _no_global_under_venv()
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\virtualenv.py", line 72, in _no_global_under_venv
    cfg_lines = _get_pyvenv_cfg_lines()
  File "g:\研究\product\.venv\lib\site-packages\pip\_internal\utils\virtualenv.py", line 55, in _get_pyvenv_cfg_lines
    return f.read().splitlines()  # avoids trailing newlines
UnicodeDecodeError: 'cp932' codec can't decode byte 0x88 in position 24: illegal multibyte sequence
```

# 解決方法
エラーの一番下にある，`.venv\lib\site-packages\pip\_internal\utils\virtualenv.py`の55行目を探して，以下のように変更しました．
ちなみに`.venv`はvenvで設定した環境名です．

#### 変更前

```
with open(pyvenv_cfg_file) as f:
    return f.read().splitlines()
```

#### 変更後

```
with open(pyvenv_cfg_file, encoding="utf-8") as f:   # encodingを追加
    return f.read().splitlines()
```

# 結果
無事バージョンダウンできました．

```
(.venv) PS G:\研究\product> pip install numpy==1.18.5
Collecting numpy==1.18.5
  Using cached numpy-1.18.5-cp38-cp38-win_amd64.whl (12.8 MB)
Installing collected packages: numpy
  Attempting uninstall: numpy
    Found existing installation: numpy 1.19.2
    Uninstalling numpy-1.19.2:
      Successfully uninstalled numpy-1.19.2
Successfully installed numpy-1.18.5
```
t4g.nanoのスポットインスタンスは$0.0016 /1 時間（月$1.152！！）※2021/03/21現在

タイトルのとおりです。
月1ドル強って安すぎません？
タイトルは東京リージョン価格です。バージニア北部だと$0.0013 /1 時間（月$0.936）と、1ドルを切ります。。

ちなみにスペックは、、、

| vCPU | ECU | メモリ（GiB） | インスタンスストレージ (GB) | オンデマンド料金（東京リージョン） | 
| --: | --- | --: | --- | --: |
| 2 | 該当なし | 0.5GiB | EBS のみ | 0.0054USD/時間 |

オンデマンドだとしても月$3.888です。
【初心者備忘録】役立ったちょいテク編(1)：テーブルのデータの退避(コピー)

※この記事は、初心者プログラマの私が、業務で扱った内容に関連した箇所を
自身の学習用にまとめたものです。
##使用したちょいテク
・テーブルのデータの退避(コピー)
##使用した場面
・指定したテーブルの指定したデータを削除するためのパッケージをPLSQLで作成時
※動作確認やテスト時、再度削除前のデータを使用したかったため
##手順
【手順1】
退避したいデータがあるテーブルのソース(CREATE TABLEのソース)をコピーしてSOL実行画面に貼り付け
※退避したいデータのあるテーブルと全く同じ型のテーブルを作成するため、既存テーブル作成ソースをコピーする
【手順2】
手順1で貼り付けた既存テーブルのソースの中で、既存テーブル名が入っている箇所を、新テーブル名に全て置換
【手順3】
手順2を実行し、新テーブルを作成
【手順4】
既存テーブル(下記のOLD_TABLE)のデータを新テーブル(下記のNEW_TABLE)に複製

~~~oracle
INSERT INTO NEW_TABLE
SELECT * FROM OLD_TABLE
~~~


---
##データコピーの時間を効率的に使う為に
コピーしたいデータ量が膨大な場合は、【手順4】実行処理にかなり時間がかかってしまうので、
一旦コピーしたいテーブルを全て【手順1,2,3】で作成し、
【手順4】のデータコピーを、【手順1,2,3】で作成した全テーブル分一遍に行い、
コピー中に他の事をやったり、終業時間間際であれば、PCを起動させたまま帰れば、データコピーの
待ち時間を削減できる。

~~~oracle
INSERT INTO NEW_TABLE
SELECT * FROM OLD_TABLE
/
INSERT INTO NEW_TABLE2
SELECT * FROM OLD_TABLE2
/
INSERT INTO NEW_TABLE3
SELECT * FROM OLD_TABLE3
/
~~~
[JS] 変数宣言とデータ型


##const
constは、後から書き換えることができない変数を定義する書き方です。constの特徴として、再代入、再定義ともに不可という制約があります。

 constで指定する「後から書き換えることのできないデータ」は、正確には「定数」と呼びます。ただし、説明が煩雑になるため、カリキュラムにおいては定数と変数の区別はせずに、双方「変数」で統一します。

【例】コンソール
const sample = "おはよう"

sample = "おはよう"
// 再代入NG →エラーが起こる

const sample = "おはよう"
// 再定義NG →エラーが起こる
constで変数定義をしましょう

コンソール
const fruits = "apple"

console.log(fruits)
// => apple と出力される

コンソール
const language = "Ruby"

language = "PHP"
// => エラーが起こる

##let
letは、後で書き換えることができる変数を定義する書き方です。letの特徴として、再代入は可能ですが、再定義は不可という制約があります。

【例】コンソール
let sample = "おはよう"

sample = "おはよう"
// 再代入OK

let sample = "おはよう"
// 再定義NG →エラーが起こる
letで変数定義をしましょう

コンソール
let color = "white"
console.log(color)
// => white と出力される

##データ型
// Number, String, Boolean, Undefined, Null, Symbol

// 動的型付け言語
let variable = 12;
variable = "str";
variable = false+
variable = undefined;
variable = nil;
variable = symbol("abc");

※symbolとは、不変なデータ型で、オブジェクトのプロパティ識別子として使われたりします。
　プリミティブデータ型です。Symbol(　) 関数は、symbol 型の値を返します。

//静的型付け言語
int num = 10;

ー型の確認ー
typeof演算子を使って、変数の型を確認できます。
console.log(typeof variable);

例えば
let variable = 12;
console.log(typeof variable);　　　←に差し込めばコンソールには12と出力されます。
variable = "str";

let variable = 12;
variable = "str";
console.log(typeof variable);　　　←に差し込めばコンソールにはstrと出力されます。
AWSアカウントの解約方法

基本はAWSのドキュメントに従って解約すればよい。

[AWS アカウントを解約するにはどうすればよいですか? | AWS ナレッジセンター](https://aws.amazon.com/jp/premiumsupport/knowledge-center/close-aws-account/)
[アカウントの解約 | AWS Billing and Cost Management ユーザーガイド](https://docs.aws.amazon.com/ja_jp/awsaccountbilling/latest/aboutv2/close-account.html)

ルートユーザーのメールアドレスを使ったログインは、メンバーのログインとはページが別なので注意。
注意事項があるのでよく確認すること。以下抜粋。

- ルートユーザーでしか解約できない
- ルートユーザーのメールアドレスは、別アカウントの新規作成に利用できなくなる（事前に変更可能）
- 解約後の90日間を解約後期間という
- 解約後期間はサインインでき、サポート機能を使える
- 解約後期間内ならアカウントを再開できる
- 再開すると解約後期間内の利用料が課金される
- 解約後期間が過ぎると全リソースは削除される
- サブスクリプションは解約後も課金される

# リソースの削除

バックアップを取った上で、リソースを削除する。
解約時にアクティブなリソースが全て削除されるとは限らないので、解約前に自分で削除しておくことがベストプラクティスになっている。

# 未払いの請求書を支払う

決済アカウント経由で支払っているなら何もすることはないはず。
そうでなければ、マネジメントコンソールから未払いの支払いを行う。

# 解約

マネジメントコンソールのマイアカウントから解約する。
解約ボタンはページ最下部にある。

# リソース終了の確認

必要なら解約から3日後以降にリソースが終了されていることを確認できる。
マネジメントコンソールで引き続き請求がされていないことを確認する。

# AWSアカウントの再開

AWSアカウントの再開は、マネジメントコンソールのサポート機能から行う。
前述の解約後期間を過ぎると再開できないので注意。
docker-compose

#【docker-compose】

自分用の簡単な備忘録です。
かなり苦戦しました。
まだまだです。継続して学んでいきます。

##Dockerを立ち上げて、コンテナを使う理由

ホストにインストールしてから、アプリケーションをインストールするのは大変であり、コンフリクトを起こす可能性もある。
コンテナを入れて、もしエラーが出ても使い捨てが可能。
Dockerfileを使えば、同じものを簡単に作ることが可能になる。

<strong>1つのコンテナに1つのアプリケーションが鉄則である。</strong>

##RailsのDockerfileの作成の流れ

$ cd ~/Documents
$ mkdir rails-product && cd rails-product
$ code Dockerfile
$ touch Gemfile Gemfile.lock
$ code Gemfile

```Dockerfile:Dockerfile
FROM ruby:2.7
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update -qq && apt-get install -y build-essential nodejs yarn
RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN  bundle install
COPY . /app

```
 
##docker-compose

$docker runコマンドが長くなるとき
複数コンテナをまとめて起動する必要があるとき
　→　docker-compose.ymlで楽をしよう。

docker-compose：コンテナを起動するときにどのように起動するか。
（dockerfile：コンテナの中身そのものを定義している。）

```docker-compose.yml
version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/app
    ports:
      - 3000:3000
    depends_on:
      - db
    tty: true
    stdin_open: true
  db:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
volumes:
  db-volume:
```
※volumesは、相対パスで記入する。
チーム開発などで他の人が使うとき絶対パスだと条件が同じにならない。

docker-compose.ymlに記入されているので、簡単に実行ができる。

##$docker-composeコマンド

<strong>docker-compose.ymlに記入されているので簡単に実行可能。
</strong>

$docker-compose build
　→　ビルドをする。

$docker-compose up
　→　イメージが作られていなければ、ビルドをしてrunをする。
　→　既にイメージがある場合は、ビルドをせずにrunをする。
　（再度ビルドしたい時は、--buildをつける。）

$docker-compose up -d
　→　バックグラウンドで実行してくれる。

$docker-compose ps
　→　docker-compose.ymlのコンテナの状態を確認する。
　（コンテナの一覧を表示する。）

$docker-compose exec < service > < command >
　→　serviceを指定して、commandを実行する。

$docker-compose down 
　→　コンテナをストップして、関係するイメージも全て消してくれる。（やり直しに使用）

$docker-compose logs
　→　コンテナのログを表示する。 

##おわりに

かめさん ありがとうございました。
[米国AI開発者がゼロから教えるDocker講座](https://www.udemy.com/course/aidocker/)

感謝の気持ちを大事にしてこれからも頑張っていこう〜！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/472549/0cc65171-1158-f26b-38a5-4646a1260f9f.png)
【Rails】現在使用しているgemの中身を見る方法

#gemの中身を見たい意図

* Rubyの勉強のため、gemの中身を見てみたい。
* でも中の見方がわからない。GitHub上で見ても、個別のファイルごとになっているので見辛い。エディタで表示されるように見たい。 

#バージョンの状況
- ruby 3.0.0
- rails 6.1.3
- homebrewをインストール済

#今回勉強するために見たかったgem
- [gimei](https://github.com/willnet/gimei)

#結論

```terminal
 % bundle open gem名
```

#見るまでにエラーが出た💦解消までの経緯（参考）

##まずは準備（Railsアプリ上で、見たいgemをインストール）
```rails:Gemfile
gem 'gimei'
```
```terminal:terminal
% bundle install
```

##カレントディレクトリにて、実行したところエラー発生

⚠️開きたいなら、エディターをセットしてね！

```terminal:terminal
% bundle open gimei
To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR
```

##解決策
- `Homebrew-cask`と呼ばれるmacのGUIをコマンドでインストール出来る拡張機能を使った。
- `Homebrew-cask`内にある`sublime-text`というエディターをインストールし、環境変数に設定したところopenコマンドで見られるようになった！

```terminal:terminal
# （ホームディレクトリに移動して）tapコマンドで、caskを追加
% brew tap homebrew/cask

# brew内に、sublime-textがあるか探す・・
% brew search subl 
=> Casks
subler  sublercli  sublime-merge  sublime-text

# sublime-textをインストール
% brew install sublime-text

# 環境変数に設定
% export EDITOR='subl -w'

# （gimeiをインストールしていたアプリに移動して）
% bundle open gimei
```

すごくコードが綺麗😖
また複数のクラスがあるRubyコードを見たのが初めてだった。
（やっと見れるようになったので、勉強のため詳細はこれから見ようと思います！！）
![gimei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696641/4db31965-3c35-a02b-225e-afe949db38fc.png)


見るまでに戸惑ったので、誰かの役に立てば幸いです。
誤っている部分などありましたら教えてください🙇‍♀️